summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuis Hector Chavez <lhchavez@google.com>2017-07-26 17:33:47 +0000
committerLuis Hector Chavez <lhchavez@google.com>2017-07-26 17:33:47 +0000
commite5b2c6fa6f923f3a2f66346c2f169d9f0fceb3dc (patch)
tree26593cf846dcf61fbaa2d04558e4984333832351
parent0601274935e7f632eb0d6ce0fd223b744349d20b (diff)
downloadlibchrome-o-iot-preview-5.tar.gz
Revert "libchrome: Uprev the library to r456626 from Chromium"android-o-iot-preview-5o-iot-preview-5
This reverts commit 0601274935e7f632eb0d6ce0fd223b744349d20b. Reason for revert: Broke the mac_sdk Exempt-From-Owner-Approval: Fixing mac_sdk Change-Id: I2cab1818261f3b75dcf7dfc3edf6d6b7bab541a8
-rw-r--r--Android.bp42
-rw-r--r--SConstruct9
-rw-r--r--base/BUILD.gn554
-rw-r--r--base/DEPS1
-rw-r--r--base/allocator/BUILD.gn78
-rw-r--r--base/allocator/README.md4
-rw-r--r--base/allocator/allocator.gyp450
-rw-r--r--base/allocator/allocator_shim.cc206
-rw-r--r--base/allocator/allocator_shim.h53
-rw-r--r--base/allocator/allocator_shim_default_dispatch_to_glibc.cc53
-rw-r--r--base/allocator/allocator_shim_default_dispatch_to_linker_wrapped_symbols.cc83
-rw-r--r--base/allocator/allocator_shim_internals.h2
-rw-r--r--base/allocator/allocator_shim_override_cpp_symbols.h45
-rw-r--r--base/allocator/allocator_shim_override_libc_symbols.h47
-rw-r--r--base/allocator/allocator_shim_override_linker_wrapped_symbols.h56
-rw-r--r--base/at_exit.cc10
-rw-r--r--base/at_exit.h4
-rw-r--r--base/atomic_ref_count.h2
-rw-r--r--base/base.gyp1801
-rw-r--r--base/base.gypi1106
-rw-r--r--base/base_nacl.gyp158
-rw-r--r--base/base_switches.cc6
-rw-r--r--base/base_switches.h1
-rw-r--r--base/base_unittests.isolate56
-rw-r--r--base/bind.h56
-rw-r--r--base/bind_helpers.h123
-rw-r--r--base/bind_internal.h114
-rw-r--r--base/bind_unittest.cc826
-rw-r--r--base/bit_cast.h33
-rw-r--r--base/bits.h59
-rw-r--r--base/bits_unittest.cc20
-rw-r--r--base/callback.h464
-rw-r--r--base/callback_forward.h22
-rw-r--r--base/callback_helpers.h11
-rw-r--r--base/callback_helpers_unittest.cc18
-rw-r--r--base/callback_internal.cc71
-rw-r--r--base/callback_internal.h56
-rw-r--r--base/callback_unittest.cc37
-rw-r--r--base/cancelable_callback.h10
-rw-r--r--base/command_line.cc9
-rw-r--r--base/compiler_specific.h38
-rw-r--r--base/containers/mru_cache.h8
-rw-r--r--base/containers/scoped_ptr_hash_map.h176
-rw-r--r--base/containers/small_map.h23
-rw-r--r--base/cpu.cc55
-rw-r--r--base/cpu.h2
-rw-r--r--base/cpu_unittest.cc10
-rw-r--r--base/critical_closure.h27
-rw-r--r--base/debug/activity_tracker.cc1389
-rw-r--r--base/debug/activity_tracker.h1102
-rw-r--r--base/debug/activity_tracker_unittest.cc340
-rw-r--r--base/debug/alias.cc3
-rw-r--r--base/debug/debugger_posix.cc2
-rw-r--r--base/debug/debugging_flags.h5
-rw-r--r--base/debug/dump_without_crashing.cc35
-rw-r--r--base/debug/dump_without_crashing.h31
-rw-r--r--base/debug/leak_tracker_unittest.cc2
-rw-r--r--base/debug/profiler.cc225
-rw-r--r--base/debug/profiler.h94
-rw-r--r--base/debug/stack_trace.cc252
-rw-r--r--base/debug/stack_trace.h65
-rw-r--r--base/debug/stack_trace_posix.cc49
-rw-r--r--base/debug/task_annotator.cc44
-rw-r--r--base/debug/task_annotator.h2
-rw-r--r--base/debug/task_annotator_unittest.cc2
-rw-r--r--base/debug/thread_heap_usage_tracker.h117
-rw-r--r--base/feature_list.cc95
-rw-r--r--base/feature_list.h29
-rw-r--r--base/feature_list_unittest.cc69
-rw-r--r--base/file_version_info_unittest.cc144
-rw-r--r--base/files/dir_reader_posix_unittest.cc2
-rw-r--r--base/files/file.cc8
-rw-r--r--base/files/file.h71
-rw-r--r--base/files/file_descriptor_watcher_posix.cc210
-rw-r--r--base/files/file_descriptor_watcher_posix.h99
-rw-r--r--base/files/file_descriptor_watcher_posix_unittest.cc318
-rw-r--r--base/files/file_path.cc3
-rw-r--r--base/files/file_path.h7
-rw-r--r--base/files/file_path_unittest.cc1
-rw-r--r--base/files/file_path_watcher.cc9
-rw-r--r--base/files/file_path_watcher.h47
-rw-r--r--base/files/file_path_watcher_fsevents.cc94
-rw-r--r--base/files/file_path_watcher_fsevents.h9
-rw-r--r--base/files/file_path_watcher_kqueue.cc163
-rw-r--r--base/files/file_path_watcher_kqueue.h30
-rw-r--r--base/files/file_path_watcher_linux.cc160
-rw-r--r--base/files/file_path_watcher_mac.cc61
-rw-r--r--base/files/file_path_watcher_unittest.cc140
-rw-r--r--base/files/file_posix.cc9
-rw-r--r--base/files/file_tracing.cc37
-rw-r--r--base/files/file_tracing.h12
-rw-r--r--base/files/file_unittest.cc186
-rw-r--r--base/files/file_util.h19
-rw-r--r--base/files/file_util_mac.mm12
-rw-r--r--base/files/file_util_posix.cc76
-rw-r--r--base/files/important_file_writer.cc61
-rw-r--r--base/files/important_file_writer.h59
-rw-r--r--base/files/important_file_writer_unittest.cc149
-rw-r--r--base/files/memory_mapped_file_posix.cc9
-rw-r--r--base/files/scoped_file.cc8
-rw-r--r--base/files/scoped_temp_dir.cc5
-rw-r--r--base/files/scoped_temp_dir.h4
-rw-r--r--base/files/scoped_temp_dir_unittest.cc8
-rw-r--r--base/id_map.h139
-rw-r--r--base/id_map_unittest.cc70
-rw-r--r--base/json/json_file_value_serializer.cc11
-rw-r--r--base/json/json_file_value_serializer.h11
-rw-r--r--base/json/json_parser.cc321
-rw-r--r--base/json/json_parser.h55
-rw-r--r--base/json/json_parser_unittest.cc91
-rw-r--r--base/json/json_reader.h5
-rw-r--r--base/json/json_reader_unittest.cc58
-rw-r--r--base/json/json_string_value_serializer.cc13
-rw-r--r--base/json/json_string_value_serializer.h13
-rw-r--r--base/json/json_value_converter.h153
-rw-r--r--base/json/json_value_converter_unittest.cc11
-rw-r--r--base/json/json_value_serializer_unittest.cc154
-rw-r--r--base/json/json_writer.cc20
-rw-r--r--base/json/json_writer.h2
-rw-r--r--base/json/json_writer_unittest.cc20
-rw-r--r--base/lazy_instance.h60
-rw-r--r--base/lazy_instance_unittest.cc23
-rw-r--r--base/location.h2
-rw-r--r--base/logging.cc44
-rw-r--r--base/logging.h402
-rw-r--r--base/logging_unittest.cc206
-rw-r--r--base/mac/bind_objc_block.h22
-rw-r--r--base/mac/bundle_locations.h1
-rw-r--r--base/mac/foundation_util.h9
-rw-r--r--base/mac/foundation_util.mm5
-rw-r--r--base/mac/mac_util.h130
-rw-r--r--base/mac/mach_port_broker.mm7
-rw-r--r--base/mac/scoped_authorizationref.h2
-rw-r--r--base/mac/scoped_block.h28
-rw-r--r--base/mac/scoped_nsobject.h7
-rw-r--r--base/mac/sdk_forward_declarations.h538
-rw-r--r--base/mac/sdk_forward_declarations.mm40
-rw-r--r--base/memory/aligned_memory_unittest.cc8
-rw-r--r--base/memory/linked_ptr.h2
-rw-r--r--base/memory/ref_counted.cc17
-rw-r--r--base/memory/ref_counted.h66
-rw-r--r--base/memory/ref_counted_delete_on_message_loop.h75
-rw-r--r--base/memory/ref_counted_unittest.cc35
-rw-r--r--base/memory/scoped_vector.h16
-rw-r--r--base/memory/scoped_vector_unittest.cc2
-rw-r--r--base/memory/shared_memory.h59
-rw-r--r--base/memory/shared_memory_handle.h57
-rw-r--r--base/memory/shared_memory_handle_mac.cc145
-rw-r--r--base/memory/shared_memory_helper.cc98
-rw-r--r--base/memory/shared_memory_helper.h33
-rw-r--r--base/memory/shared_memory_mac.cc176
-rw-r--r--base/memory/shared_memory_posix.cc167
-rw-r--r--base/memory/shared_memory_tracker.cc93
-rw-r--r--base/memory/shared_memory_tracker.h56
-rw-r--r--base/memory/shared_memory_unittest.cc23
-rw-r--r--base/memory/singleton.h23
-rw-r--r--base/memory/weak_ptr.cc4
-rw-r--r--base/memory/weak_ptr_unittest.cc15
-rw-r--r--base/message_loop/incoming_task_queue.cc6
-rw-r--r--base/message_loop/incoming_task_queue.h1
-rw-r--r--base/message_loop/message_loop.cc219
-rw-r--r--base/message_loop/message_loop.h172
-rw-r--r--base/message_loop/message_loop_task_runner_unittest.cc55
-rw-r--r--base/message_loop/message_loop_test.cc268
-rw-r--r--base/message_loop/message_loop_unittest.cc191
-rw-r--r--base/message_loop/message_pump.cc7
-rw-r--r--base/message_loop/message_pump.h9
-rw-r--r--base/message_loop/message_pump_default.cc39
-rw-r--r--base/message_loop/message_pump_glib.h2
-rw-r--r--base/message_loop/message_pump_glib_unittest.cc117
-rw-r--r--base/message_loop/message_pump_libevent.cc38
-rw-r--r--base/message_loop/message_pump_libevent.h19
-rw-r--r--base/message_loop/message_pump_mac.h19
-rw-r--r--base/message_loop/message_pump_mac.mm151
-rw-r--r--base/metrics/field_trial.cc745
-rw-r--r--base/metrics/field_trial.h235
-rw-r--r--base/metrics/field_trial_param_associator.cc80
-rw-r--r--base/metrics/field_trial_param_associator.h71
-rw-r--r--base/metrics/field_trial_unittest.cc260
-rw-r--r--base/metrics/histogram.cc26
-rw-r--r--base/metrics/histogram.h15
-rw-r--r--base/metrics/histogram_base.cc16
-rw-r--r--base/metrics/histogram_base.h6
-rw-r--r--base/metrics/histogram_macros.h507
-rw-r--r--base/metrics/histogram_macros_internal.h157
-rw-r--r--base/metrics/histogram_macros_local.h88
-rw-r--r--base/metrics/histogram_samples.h21
-rw-r--r--base/metrics/histogram_snapshot_manager.cc4
-rw-r--r--base/metrics/histogram_snapshot_manager.h2
-rw-r--r--base/metrics/histogram_unittest.cc13
-rw-r--r--base/metrics/persistent_histogram_allocator.cc233
-rw-r--r--base/metrics/persistent_histogram_allocator.h46
-rw-r--r--base/metrics/persistent_histogram_allocator_unittest.cc147
-rw-r--r--base/metrics/persistent_memory_allocator.cc288
-rw-r--r--base/metrics/persistent_memory_allocator.h352
-rw-r--r--base/metrics/persistent_memory_allocator_unittest.cc153
-rw-r--r--base/metrics/persistent_sample_map.cc62
-rw-r--r--base/metrics/persistent_sample_map.h1
-rw-r--r--base/metrics/persistent_sample_map_unittest.cc37
-rw-r--r--base/metrics/sample_map_unittest.cc1
-rw-r--r--base/metrics/sample_vector_unittest.cc1
-rw-r--r--base/metrics/sparse_histogram.cc6
-rw-r--r--base/metrics/sparse_histogram.h34
-rw-r--r--base/metrics/sparse_histogram_unittest.cc1
-rw-r--r--base/metrics/statistics_recorder.cc187
-rw-r--r--base/metrics/statistics_recorder.h35
-rw-r--r--base/metrics/statistics_recorder_unittest.cc71
-rw-r--r--base/metrics/user_metrics.cc6
-rw-r--r--base/metrics/user_metrics.h3
-rw-r--r--base/native_library.h20
-rw-r--r--base/native_library_posix.cc21
-rw-r--r--base/numerics/safe_conversions.h268
-rw-r--r--base/numerics/safe_conversions_impl.h683
-rw-r--r--base/numerics/safe_math.h585
-rw-r--r--base/numerics/safe_math_impl.h861
-rw-r--r--base/numerics/safe_numerics_unittest.cc692
-rw-r--r--base/observer_list.h170
-rw-r--r--base/observer_list_threadsafe.h143
-rw-r--r--base/observer_list_unittest.cc450
-rw-r--r--base/optional.h105
-rw-r--r--base/optional_unittest.cc103
-rw-r--r--base/pending_task.cc24
-rw-r--r--base/pending_task.h11
-rw-r--r--base/pickle.cc1
-rw-r--r--base/posix/global_descriptors.cc16
-rw-r--r--base/posix/global_descriptors.h21
-rw-r--r--base/post_task_and_reply_with_result_internal.h35
-rw-r--r--base/power_monitor/power_monitor_device_source.h19
-rw-r--r--base/power_monitor/power_monitor_source.h9
-rw-r--r--base/process/internal_linux.cc41
-rw-r--r--base/process/internal_linux.h8
-rw-r--r--base/process/kill.h11
-rw-r--r--base/process/kill_posix.cc2
-rw-r--r--base/process/launch.cc36
-rw-r--r--base/process/launch.h53
-rw-r--r--base/process/launch_mac.cc153
-rw-r--r--base/process/launch_posix.cc21
-rw-r--r--base/process/memory.cc54
-rw-r--r--base/process/memory.h83
-rw-r--r--base/process/memory_linux.cc212
-rw-r--r--base/process/memory_stubs.cc49
-rw-r--r--base/process/port_provider_mac.cc3
-rw-r--r--base/process/process.h42
-rw-r--r--base/process/process_metrics.cc5
-rw-r--r--base/process/process_metrics.h32
-rw-r--r--base/process/process_metrics_linux.cc33
-rw-r--r--base/process/process_metrics_mac.cc46
-rw-r--r--base/process/process_metrics_posix.cc2
-rw-r--r--base/process/process_metrics_unittest.cc19
-rw-r--r--base/process/process_posix.cc33
-rw-r--r--base/profiler/scoped_profile.h36
-rw-r--r--base/rand_util_posix.cc3
-rw-r--r--base/run_loop.cc4
-rw-r--r--base/run_loop.h3
-rw-r--r--base/scoped_generic.h8
-rw-r--r--base/scoped_observer.h2
-rw-r--r--base/security_unittest.cc21
-rw-r--r--base/sequence_checker.h30
-rw-r--r--base/sequence_checker_impl.cc75
-rw-r--r--base/sequence_checker_impl.h36
-rw-r--r--base/sequence_checker_unittest.cc419
-rw-r--r--base/sequence_token.cc92
-rw-r--r--base/sequence_token.h115
-rw-r--r--base/sequence_token_unittest.cc133
-rw-r--r--base/sequenced_task_runner.cc20
-rw-r--r--base/sequenced_task_runner.h37
-rw-r--r--base/sequenced_task_runner_helpers.h81
-rw-r--r--base/sha1_portable.cc (renamed from base/sha1.cc)9
-rw-r--r--base/stl_util.h296
-rw-r--r--base/stl_util_unittest.cc156
-rw-r--r--base/strings/string_number_conversions.cc5
-rw-r--r--base/strings/string_number_conversions_unittest.cc87
-rw-r--r--base/strings/string_split.cc24
-rw-r--r--base/strings/string_split.h20
-rw-r--r--base/strings/string_split_unittest.cc27
-rw-r--r--base/strings/string_util.cc68
-rw-r--r--base/strings/string_util.h24
-rw-r--r--base/strings/string_util_unittest.cc110
-rw-r--r--base/strings/utf_string_conversion_utils.h3
-rw-r--r--base/strings/utf_string_conversions.cc4
-rw-r--r--base/synchronization/atomic_flag.cc32
-rw-r--r--base/synchronization/atomic_flag.h44
-rw-r--r--base/synchronization/atomic_flag_unittest.cc131
-rw-r--r--base/synchronization/cancellation_flag.cc26
-rw-r--r--base/synchronization/cancellation_flag.h41
-rw-r--r--base/synchronization/cancellation_flag_unittest.cc65
-rw-r--r--base/synchronization/condition_variable.h6
-rw-r--r--base/synchronization/condition_variable_posix.cc2
-rw-r--r--base/synchronization/lock.h17
-rw-r--r--base/synchronization/lock_impl.h5
-rw-r--r--base/synchronization/lock_impl_posix.cc55
-rw-r--r--base/synchronization/waitable_event.h14
-rw-r--r--base/synchronization/waitable_event_posix.cc20
-rw-r--r--base/synchronization/waitable_event_unittest.cc57
-rw-r--r--base/synchronization/waitable_event_watcher.h57
-rw-r--r--base/synchronization/waitable_event_watcher_posix.cc135
-rw-r--r--base/sys_byteorder.h16
-rw-r--r--base/sys_info.h12
-rw-r--r--base/sys_info_chromeos.cc12
-rw-r--r--base/sys_info_mac.mm8
-rw-r--r--base/sys_info_posix.cc45
-rw-r--r--base/sys_info_unittest.cc10
-rw-r--r--base/task/cancelable_task_tracker.cc58
-rw-r--r--base/task/cancelable_task_tracker.h52
-rw-r--r--base/task/cancelable_task_tracker_unittest.cc17
-rw-r--r--base/task_runner.cc11
-rw-r--r--base/task_runner.h6
-rw-r--r--base/task_runner_util.h48
-rw-r--r--base/task_scheduler/scheduler_lock_impl.cc23
-rw-r--r--base/task_scheduler/scheduler_lock_unittest.cc14
-rw-r--r--base/task_scheduler/scoped_set_task_priority_for_current_thread.cc41
-rw-r--r--base/task_scheduler/scoped_set_task_priority_for_current_thread.h36
-rw-r--r--base/task_scheduler/scoped_set_task_priority_for_current_thread_unittest.cc26
-rw-r--r--base/task_scheduler/sequence.cc26
-rw-r--r--base/task_scheduler/sequence.h38
-rw-r--r--base/task_scheduler/sequence_unittest.cc93
-rw-r--r--base/task_scheduler/task.cc12
-rw-r--r--base/task_scheduler/task.h11
-rw-r--r--base/task_scheduler/task_traits.cc56
-rw-r--r--base/task_scheduler/task_traits.h84
-rw-r--r--base/task_scheduler/test_utils.h19
-rw-r--r--base/template_util.h67
-rw-r--r--base/test/BUILD.gn104
-rw-r--r--base/test/gtest_util.cc112
-rw-r--r--base/test/gtest_util.h102
-rw-r--r--base/test/mock_entropy_provider.cc20
-rw-r--r--base/test/mock_entropy_provider.h32
-rw-r--r--base/test/multiprocess_test.cc16
-rw-r--r--base/test/multiprocess_test.h35
-rw-r--r--base/test/multiprocess_test_android.cc476
-rw-r--r--base/test/opaque_ref_counted.cc30
-rw-r--r--base/test/opaque_ref_counted.h5
-rw-r--r--base/test/scoped_feature_list.cc74
-rw-r--r--base/test/scoped_feature_list.h59
-rw-r--r--base/test/sequenced_worker_pool_owner.cc8
-rw-r--r--base/test/sequenced_worker_pool_owner.h2
-rw-r--r--base/test/test_file_util.h21
-rw-r--r--base/test/test_io_thread.cc23
-rw-r--r--base/test/test_io_thread.h16
-rw-r--r--base/test/test_mock_time_task_runner.cc321
-rw-r--r--base/test/test_mock_time_task_runner.h223
-rw-r--r--base/test/test_pending_task.cc4
-rw-r--r--base/test/test_pending_task.h9
-rw-r--r--base/test/test_simple_task_runner.cc62
-rw-r--r--base/test/test_simple_task_runner.h32
-rw-r--r--base/test/test_timeouts.cc8
-rw-r--r--base/test/test_timeouts.h3
-rw-r--r--base/test/trace_event_analyzer.cc4
-rw-r--r--base/test/trace_event_analyzer_unittest.cc2
-rw-r--r--base/third_party/dynamic_annotations/dynamic_annotations.c269
-rw-r--r--base/threading/non_thread_safe.h15
-rw-r--r--base/threading/non_thread_safe_unittest.cc25
-rw-r--r--base/threading/platform_thread.h30
-rw-r--r--base/threading/platform_thread_linux.cc79
-rw-r--r--base/threading/platform_thread_mac.mm5
-rw-r--r--base/threading/platform_thread_posix.cc34
-rw-r--r--base/threading/platform_thread_unittest.cc20
-rw-r--r--base/threading/post_task_and_reply_impl.cc70
-rw-r--r--base/threading/post_task_and_reply_impl.h27
-rw-r--r--base/threading/sequenced_task_runner_handle.cc47
-rw-r--r--base/threading/sequenced_task_runner_handle.h5
-rw-r--r--base/threading/sequenced_worker_pool.cc571
-rw-r--r--base/threading/sequenced_worker_pool.h100
-rw-r--r--base/threading/simple_thread.cc59
-rw-r--r--base/threading/simple_thread.h60
-rw-r--r--base/threading/simple_thread_unittest.cc107
-rw-r--r--base/threading/thread.cc206
-rw-r--r--base/threading/thread.h141
-rw-r--r--base/threading/thread_checker.h22
-rw-r--r--base/threading/thread_checker_impl.cc47
-rw-r--r--base/threading/thread_checker_impl.h36
-rw-r--r--base/threading/thread_checker_unittest.cc268
-rw-r--r--base/threading/thread_local.h93
-rw-r--r--base/threading/thread_local_posix.cc43
-rw-r--r--base/threading/thread_local_storage.cc271
-rw-r--r--base/threading/thread_local_storage.h28
-rw-r--r--base/threading/thread_local_storage_unittest.cc10
-rw-r--r--base/threading/thread_restrictions.cc22
-rw-r--r--base/threading/thread_restrictions.h39
-rw-r--r--base/threading/thread_task_runner_handle.cc46
-rw-r--r--base/threading/thread_task_runner_handle.h12
-rw-r--r--base/threading/thread_unittest.cc348
-rw-r--r--base/threading/worker_pool.cc20
-rw-r--r--base/threading/worker_pool.h8
-rw-r--r--base/threading/worker_pool_posix.cc40
-rw-r--r--base/threading/worker_pool_posix.h7
-rw-r--r--base/threading/worker_pool_posix_unittest.cc6
-rw-r--r--base/time/time.cc47
-rw-r--r--base/time/time.h93
-rw-r--r--base/time/time_mac.cc76
-rw-r--r--base/time/time_posix.cc99
-rw-r--r--base/time/time_unittest.cc33
-rw-r--r--base/timer/timer.cc44
-rw-r--r--base/timer/timer.h49
-rw-r--r--base/timer/timer_unittest.cc521
-rw-r--r--base/trace_event/category_registry.cc156
-rw-r--r--base/trace_event/category_registry.h93
-rw-r--r--base/trace_event/common/trace_event_common.h163
-rw-r--r--base/trace_event/etw_manifest/etw_manifest.gyp41
-rw-r--r--base/trace_event/event_name_filter.cc26
-rw-r--r--base/trace_event/event_name_filter.h46
-rw-r--r--base/trace_event/event_name_filter_unittest.cc41
-rw-r--r--base/trace_event/heap_profiler_allocation_context_tracker.cc58
-rw-r--r--base/trace_event/heap_profiler_allocation_context_tracker.h27
-rw-r--r--base/trace_event/heap_profiler_allocation_context_tracker_unittest.cc97
-rw-r--r--base/trace_event/heap_profiler_allocation_register.cc8
-rw-r--r--base/trace_event/heap_profiler_allocation_register.h32
-rw-r--r--base/trace_event/heap_profiler_event_filter.cc67
-rw-r--r--base/trace_event/heap_profiler_event_filter.h40
-rw-r--r--base/trace_event/heap_profiler_heap_dump_writer.cc3
-rw-r--r--base/trace_event/heap_profiler_stack_frame_deduplicator.cc20
-rw-r--r--base/trace_event/heap_profiler_stack_frame_deduplicator.h2
-rw-r--r--base/trace_event/heap_profiler_type_name_deduplicator.cc28
-rw-r--r--base/trace_event/malloc_dump_provider.cc178
-rw-r--r--base/trace_event/malloc_dump_provider.h2
-rw-r--r--base/trace_event/memory_allocator_dump.h6
-rw-r--r--base/trace_event/memory_dump_manager.cc314
-rw-r--r--base/trace_event/memory_dump_manager.h65
-rw-r--r--base/trace_event/memory_dump_manager_unittest.cc200
-rw-r--r--base/trace_event/memory_dump_provider.h20
-rw-r--r--base/trace_event/memory_dump_request_args.cc17
-rw-r--r--base/trace_event/memory_dump_request_args.h18
-rw-r--r--base/trace_event/memory_dump_scheduler.cc304
-rw-r--r--base/trace_event/memory_dump_scheduler.h141
-rw-r--r--base/trace_event/memory_dump_session_state.cc15
-rw-r--r--base/trace_event/memory_dump_session_state.h22
-rw-r--r--base/trace_event/memory_infra_background_whitelist.cc71
-rw-r--r--base/trace_event/memory_usage_estimator.cc14
-rw-r--r--base/trace_event/memory_usage_estimator.h549
-rw-r--r--base/trace_event/memory_usage_estimator_unittest.cc244
-rw-r--r--base/trace_event/process_memory_dump.cc24
-rw-r--r--base/trace_event/process_memory_dump.h1
-rw-r--r--base/trace_event/trace_buffer.cc13
-rw-r--r--base/trace_event/trace_category.h109
-rw-r--r--base/trace_event/trace_config.cc249
-rw-r--r--base/trace_event/trace_config.h63
-rw-r--r--base/trace_event/trace_config_memory_test_util.h181
-rw-r--r--base/trace_event/trace_config_unittest.cc178
-rw-r--r--base/trace_event/trace_event.gypi107
-rw-r--r--base/trace_event/trace_event.h407
-rw-r--r--base/trace_event/trace_event_argument.cc36
-rw-r--r--base/trace_event/trace_event_argument_unittest.cc10
-rw-r--r--base/trace_event/trace_event_filter.cc21
-rw-r--r--base/trace_event/trace_event_filter.h51
-rw-r--r--base/trace_event/trace_event_filter_test_utils.cc61
-rw-r--r--base/trace_event/trace_event_filter_test_utils.h53
-rw-r--r--base/trace_event/trace_event_impl.cc68
-rw-r--r--base/trace_event/trace_event_impl.h5
-rw-r--r--base/trace_event/trace_event_memory_overhead.cc20
-rw-r--r--base/trace_event/trace_event_synthetic_delay.h3
-rw-r--r--base/trace_event/trace_event_unittest.cc770
-rw-r--r--base/trace_event/trace_log.cc750
-rw-r--r--base/trace_event/trace_log.h140
-rw-r--r--base/trace_event/trace_log_constants.cc3
-rw-r--r--base/trace_event/trace_sampling_thread.cc107
-rw-r--r--base/trace_event/trace_sampling_thread.h54
-rw-r--r--base/tracked_objects.cc326
-rw-r--r--base/tracked_objects.h286
-rw-r--r--base/tracked_objects_unittest.cc262
-rw-r--r--base/tuple.h106
-rw-r--r--base/values.cc987
-rw-r--r--base/values.h400
-rw-r--r--base/values_unittest.cc576
-rw-r--r--base/version.cc2
-rw-r--r--base/version.h12
-rw-r--r--base/version_unittest.cc27
-rw-r--r--base/win/scoped_comptr.h2
-rw-r--r--base/win/scoped_handle_test_dll.cc4
-rw-r--r--base/win/scoped_hdc.h4
-rw-r--r--components/timers/alarm_timer_chromeos.cc493
-rw-r--r--components/timers/alarm_timer_chromeos.h96
-rw-r--r--crypto/BUILD.gn11
-rw-r--r--crypto/apple_keychain.h6
-rw-r--r--crypto/auto_cbb.h35
-rw-r--r--crypto/crypto.gyp236
-rw-r--r--crypto/crypto.gypi88
-rw-r--r--crypto/crypto_nacl.gyp44
-rw-r--r--crypto/crypto_unittests.isolate42
-rw-r--r--crypto/ec_private_key.h56
-rw-r--r--crypto/ec_signature_creator_impl.h2
-rw-r--r--crypto/hmac.cc49
-rw-r--r--crypto/hmac.h5
-rw-r--r--crypto/hmac_nss.cc118
-rw-r--r--crypto/nss_crypto_module_delegate.h1
-rw-r--r--crypto/nss_util.cc226
-rw-r--r--crypto/nss_util.h1
-rw-r--r--crypto/nss_util_internal.h2
-rw-r--r--crypto/openssl_bio_string.cc77
-rw-r--r--crypto/openssl_bio_string.h29
-rw-r--r--crypto/openssl_bio_string_unittest.cc63
-rw-r--r--crypto/openssl_util.cc4
-rw-r--r--crypto/openssl_util.h2
-rw-r--r--crypto/p224_spake.cc15
-rw-r--r--crypto/p224_spake.h6
-rw-r--r--crypto/p224_spake_unittest.cc2
-rw-r--r--crypto/p224_unittest.cc14
-rw-r--r--crypto/random.h2
-rw-r--r--crypto/random_unittest.cc2
-rw-r--r--crypto/rsa_private_key.cc423
-rw-r--r--crypto/rsa_private_key.h179
-rw-r--r--crypto/rsa_private_key_nss.cc151
-rw-r--r--crypto/rsa_private_key_unittest.cc16
-rw-r--r--crypto/scoped_openssl_types.h62
-rw-r--r--crypto/scoped_test_nss_chromeos_user.cc2
-rw-r--r--crypto/scoped_test_nss_db.cc2
-rw-r--r--crypto/scoped_test_system_nss_key_slot.h2
-rw-r--r--crypto/secure_hash.cc2
-rw-r--r--crypto/sha2.cc2
-rw-r--r--crypto/signature_creator.h9
-rw-r--r--crypto/signature_creator_nss.cc119
-rw-r--r--crypto/signature_creator_unittest.cc1
-rw-r--r--crypto/signature_verifier.h26
-rw-r--r--crypto/signature_verifier_nss.cc213
-rw-r--r--crypto/signature_verifier_unittest.cc1
-rw-r--r--crypto/symmetric_key.cc4
-rw-r--r--crypto/symmetric_key.h38
-rw-r--r--crypto/symmetric_key_nss.cc151
-rw-r--r--crypto/third_party/nss/chromium-blapi.h101
-rw-r--r--crypto/third_party/nss/chromium-blapit.h91
-rw-r--r--crypto/third_party/nss/chromium-nss.h79
-rw-r--r--crypto/third_party/nss/chromium-prtypes.h77
-rw-r--r--crypto/third_party/nss/chromium-sha256.h51
-rw-r--r--crypto/third_party/nss/rsawrapr.c160
-rw-r--r--crypto/third_party/nss/sha512.cc1390
-rw-r--r--crypto/wincrypt_shim.h6
-rw-r--r--dbus/BUILD.gn2
-rw-r--r--dbus/bus.cc36
-rw-r--r--dbus/bus.h10
-rw-r--r--dbus/dbus.gyp141
-rw-r--r--dbus/dbus_statistics.cc118
-rw-r--r--dbus/exported_object.cc10
-rw-r--r--dbus/file_descriptor.cc68
-rw-r--r--dbus/file_descriptor.h92
-rw-r--r--dbus/message.cc19
-rw-r--r--dbus/message.h9
-rw-r--r--dbus/mock_bus.h9
-rw-r--r--dbus/mock_object_manager.h1
-rw-r--r--dbus/mock_object_proxy.h4
-rw-r--r--dbus/object_manager.cc75
-rw-r--r--dbus/object_manager.h20
-rw-r--r--dbus/object_proxy.cc7
-rw-r--r--dbus/object_proxy.h14
-rw-r--r--dbus/property.cc132
-rw-r--r--dbus/property.h23
-rw-r--r--dbus/values_util.cc59
-rw-r--r--sandbox/BUILD.gn8
-rw-r--r--sandbox/linux/BUILD.gn40
-rw-r--r--sandbox/linux/bpf_dsl/bpf_dsl.h5
-rw-r--r--sandbox/linux/bpf_dsl/bpf_dsl_forward.h8
-rw-r--r--sandbox/linux/bpf_dsl/bpf_dsl_impl.h1
-rw-r--r--sandbox/linux/sandbox_linux.gypi434
-rw-r--r--sandbox/linux/sandbox_linux_nacl_nonsfi.gyp87
-rw-r--r--sandbox/linux/sandbox_linux_test_sources.gypi93
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc14
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc26
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc94
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc18
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h9
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc3
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc6
-rw-r--r--sandbox/linux/services/credentials.cc61
-rw-r--r--sandbox/linux/services/credentials.h3
-rw-r--r--sandbox/linux/services/credentials_unittest.cc14
-rw-r--r--sandbox/linux/services/syscall_wrappers.cc4
-rw-r--r--sandbox/linux/services/syscall_wrappers.h4
-rw-r--r--sandbox/linux/suid/process_util_linux.c1
-rw-r--r--sandbox/linux/syscall_broker/broker_file_permission.h2
-rw-r--r--sandbox/linux/syscall_broker/broker_file_permission_unittest.cc17
-rw-r--r--sandbox/linux/system_headers/arm64_linux_syscalls.h4
-rw-r--r--sandbox/linux/system_headers/mips64_linux_syscalls.h8
-rw-r--r--sandbox/linux/system_headers/mips_linux_syscalls.h8
-rw-r--r--sandbox/mac/BUILD.gn5
-rw-r--r--sandbox/mac/sandbox_mac.gypi104
-rw-r--r--sandbox/sandbox.gyp35
-rw-r--r--sandbox/sandbox_linux_unittests.isolate23
-rw-r--r--sandbox/win/BUILD.gn45
-rw-r--r--sandbox/win/sandbox_win.gypi432
-rw-r--r--sandbox/win/src/internal_types.h2
-rw-r--r--sandbox/win/src/sandbox.vcproj10
-rw-r--r--sandbox/win/src/sandbox_types.h7
-rw-r--r--sandbox/win/src/security_level.h11
-rw-r--r--sandbox/win/wow_helper.sln19
-rwxr-xr-xsandbox/win/wow_helper/wow_helper.exebin0 -> 67072 bytes
-rw-r--r--sandbox/win/wow_helper/wow_helper.pdbbin0 -> 699392 bytes
-rw-r--r--sandbox/win/wow_helper/wow_helper.vcproj215
585 files changed, 23180 insertions, 31255 deletions
diff --git a/Android.bp b/Android.bp
index dbd99c6b9c..d7526b8993 100644
--- a/Android.bp
+++ b/Android.bp
@@ -77,19 +77,14 @@ libchromeCommonSrc = [
"base/callback_internal.cc",
"base/command_line.cc",
"base/cpu.cc",
- "base/debug/activity_tracker.cc",
"base/debug/alias.cc",
"base/debug/debugger.cc",
"base/debug/debugger_posix.cc",
- "base/debug/dump_without_crashing.cc",
- "base/debug/profiler.cc",
"base/debug/stack_trace.cc",
"base/debug/stack_trace_posix.cc",
"base/debug/task_annotator.cc",
"base/environment.cc",
- "base/feature_list.cc",
"base/files/file.cc",
- "base/files/file_descriptor_watcher_posix.cc",
"base/files/file_enumerator.cc",
"base/files/file_enumerator_posix.cc",
"base/files/file_path.cc",
@@ -120,8 +115,6 @@ libchromeCommonSrc = [
"base/memory/aligned_memory.cc",
"base/memory/ref_counted.cc",
"base/memory/ref_counted_memory.cc",
- "base/memory/shared_memory_helper.cc",
- "base/memory/shared_memory_tracker.cc",
"base/memory/singleton.cc",
"base/memory/weak_ptr.cc",
"base/message_loop/incoming_task_queue.cc",
@@ -132,7 +125,6 @@ libchromeCommonSrc = [
"base/message_loop/message_pump_libevent.cc",
"base/metrics/bucket_ranges.cc",
"base/metrics/field_trial.cc",
- "base/metrics/field_trial_param_associator.cc",
"base/metrics/metrics_hashes.cc",
"base/metrics/histogram_base.cc",
"base/metrics/histogram.cc",
@@ -147,15 +139,12 @@ libchromeCommonSrc = [
"base/metrics/statistics_recorder.cc",
"base/pending_task.cc",
"base/pickle.cc",
- "base/posix/global_descriptors.cc",
"base/posix/file_descriptor_shuffle.cc",
"base/posix/safe_strerror.cc",
"base/process/kill.cc",
"base/process/kill_posix.cc",
"base/process/launch.cc",
"base/process/launch_posix.cc",
- "base/process/memory.cc",
- "base/process/memory_linux.cc",
"base/process/process_handle.cc",
"base/process/process_handle_posix.cc",
"base/process/process_iterator.cc",
@@ -169,9 +158,8 @@ libchromeCommonSrc = [
"base/rand_util_posix.cc",
"base/run_loop.cc",
"base/sequence_checker_impl.cc",
- "base/sequence_token.cc",
"base/sequenced_task_runner.cc",
- "base/sha1.cc",
+ "base/sha1_portable.cc",
"base/strings/pattern.cc",
"base/strings/safe_sprintf.cc",
"base/strings/string16.cc",
@@ -183,7 +171,7 @@ libchromeCommonSrc = [
"base/strings/string_util_constants.cc",
"base/strings/utf_string_conversions.cc",
"base/strings/utf_string_conversion_utils.cc",
- "base/synchronization/atomic_flag.cc",
+ "base/synchronization/cancellation_flag.cc",
"base/synchronization/condition_variable_posix.cc",
"base/synchronization/lock.cc",
"base/synchronization/lock_impl_posix.cc",
@@ -195,12 +183,11 @@ libchromeCommonSrc = [
"base/task/cancelable_task_tracker.cc",
"base/task_runner.cc",
"base/task_scheduler/scheduler_lock_impl.cc",
- "base/task_scheduler/scoped_set_task_priority_for_current_thread.cc",
"base/task_scheduler/sequence.cc",
"base/task_scheduler/sequence_sort_key.cc",
"base/task_scheduler/task.cc",
"base/task_scheduler/task_traits.cc",
- "base/third_party/dynamic_annotations/dynamic_annotations.c",
+ "base/test/trace_event_analyzer.cc",
"base/third_party/icu/icu_utf.cc",
"base/third_party/nspr/prtime.cc",
"base/threading/non_thread_safe_impl.cc",
@@ -213,6 +200,7 @@ libchromeCommonSrc = [
"base/threading/thread_checker_impl.cc",
"base/threading/thread_collision_warner.cc",
"base/threading/thread_id_name_manager.cc",
+ "base/threading/thread_local_posix.cc",
"base/threading/thread_local_storage.cc",
"base/threading/thread_local_storage_posix.cc",
"base/threading/thread_restrictions.cc",
@@ -227,13 +215,10 @@ libchromeCommonSrc = [
"base/time/time_posix.cc",
"base/timer/elapsed_timer.cc",
"base/timer/timer.cc",
- "base/trace_event/category_registry.cc",
- "base/trace_event/event_name_filter.cc",
"base/trace_event/heap_profiler_allocation_context.cc",
"base/trace_event/heap_profiler_allocation_context_tracker.cc",
"base/trace_event/heap_profiler_allocation_register.cc",
"base/trace_event/heap_profiler_allocation_register_posix.cc",
- "base/trace_event/heap_profiler_event_filter.cc",
"base/trace_event/heap_profiler_heap_dump_writer.cc",
"base/trace_event/heap_profiler_stack_frame_deduplicator.cc",
"base/trace_event/heap_profiler_type_name_deduplicator.cc",
@@ -242,22 +227,20 @@ libchromeCommonSrc = [
"base/trace_event/memory_allocator_dump_guid.cc",
"base/trace_event/memory_dump_manager.cc",
"base/trace_event/memory_dump_request_args.cc",
- "base/trace_event/memory_dump_scheduler.cc",
"base/trace_event/memory_dump_session_state.cc",
"base/trace_event/memory_infra_background_whitelist.cc",
- "base/trace_event/memory_usage_estimator.cc",
"base/trace_event/process_memory_dump.cc",
"base/trace_event/process_memory_maps.cc",
"base/trace_event/process_memory_totals.cc",
"base/trace_event/trace_buffer.cc",
"base/trace_event/trace_config.cc",
"base/trace_event/trace_event_argument.cc",
- "base/trace_event/trace_event_filter.cc",
"base/trace_event/trace_event_impl.cc",
"base/trace_event/trace_event_memory_overhead.cc",
"base/trace_event/trace_event_synthetic_delay.cc",
"base/trace_event/trace_log.cc",
"base/trace_event/trace_log_constants.cc",
+ "base/trace_event/trace_sampling_thread.cc",
"base/tracked_objects.cc",
"base/tracking_info.cc",
"base/values.cc",
@@ -389,7 +372,6 @@ cc_library_static {
host_supported: true,
srcs: [
- "base/test/gtest_util.cc",
"base/test/simple_test_clock.cc",
"base/test/simple_test_tick_clock.cc",
"base/test/test_file_util.cc",
@@ -442,13 +424,12 @@ cc_test {
"base/cancelable_callback_unittest.cc",
"base/command_line_unittest.cc",
"base/cpu_unittest.cc",
- "base/debug/activity_tracker_unittest.cc",
"base/debug/debugger_unittest.cc",
"base/debug/leak_tracker_unittest.cc",
"base/debug/task_annotator_unittest.cc",
"base/environment_unittest.cc",
+ "base/file_version_info_unittest.cc",
"base/files/dir_reader_posix_unittest.cc",
- "base/files/file_descriptor_watcher_posix_unittest.cc",
"base/files/file_path_watcher_unittest.cc",
"base/files/file_path_unittest.cc",
"base/files/file_unittest.cc",
@@ -503,7 +484,6 @@ cc_test {
"base/scoped_generic_unittest.cc",
"base/security_unittest.cc",
"base/sequence_checker_unittest.cc",
- "base/sequence_token_unittest.cc",
"base/sha1_unittest.cc",
"base/stl_util_unittest.cc",
"base/strings/pattern_unittest.cc",
@@ -515,7 +495,7 @@ cc_test {
"base/strings/string_util_unittest.cc",
"base/strings/sys_string_conversions_unittest.cc",
"base/strings/utf_string_conversions_unittest.cc",
- "base/synchronization/atomic_flag_unittest.cc",
+ "base/synchronization/cancellation_flag_unittest.cc",
"base/synchronization/condition_variable_unittest.cc",
"base/synchronization/lock_unittest.cc",
"base/synchronization/waitable_event_unittest.cc",
@@ -524,22 +504,19 @@ cc_test {
"base/task/cancelable_task_tracker_unittest.cc",
"base/task_runner_util_unittest.cc",
"base/task_scheduler/scheduler_lock_unittest.cc",
- "base/task_scheduler/scoped_set_task_priority_for_current_thread_unittest.cc",
"base/task_scheduler/sequence_sort_key_unittest.cc",
"base/task_scheduler/sequence_unittest.cc",
"base/task_scheduler/task_traits.cc",
"base/template_util_unittest.cc",
- "base/test/mock_entropy_provider.cc",
"base/test/multiprocess_test.cc",
+ "base/test/multiprocess_test_android.cc",
"base/test/opaque_ref_counted.cc",
- "base/test/scoped_feature_list.cc",
"base/test/scoped_locale.cc",
"base/test/sequenced_worker_pool_owner.cc",
"base/test/test_file_util.cc",
"base/test/test_file_util_linux.cc",
"base/test/test_file_util_posix.cc",
"base/test/test_io_thread.cc",
- "base/test/test_mock_time_task_runner.cc",
"base/test/test_pending_task.cc",
"base/test/test_simple_task_runner.cc",
"base/test/test_switches.cc",
@@ -560,17 +537,14 @@ cc_test {
"base/time/time_unittest.cc",
"base/timer/hi_res_timer_manager_unittest.cc",
"base/timer/timer_unittest.cc",
- "base/trace_event/event_name_filter_unittest.cc",
"base/trace_event/heap_profiler_allocation_context_tracker_unittest.cc",
"base/trace_event/heap_profiler_stack_frame_deduplicator_unittest.cc",
"base/trace_event/heap_profiler_type_name_deduplicator_unittest.cc",
"base/trace_event/memory_allocator_dump_unittest.cc",
"base/trace_event/memory_dump_manager_unittest.cc",
- "base/trace_event/memory_usage_estimator_unittest.cc",
"base/trace_event/process_memory_dump_unittest.cc",
"base/trace_event/trace_config_unittest.cc",
"base/trace_event/trace_event_argument_unittest.cc",
- "base/trace_event/trace_event_filter_test_utils.cc",
"base/trace_event/trace_event_synthetic_delay_unittest.cc",
"base/trace_event/trace_event_unittest.cc",
"base/tracked_objects_unittest.cc",
diff --git a/SConstruct b/SConstruct
index 49cef6f434..72e022e6cd 100644
--- a/SConstruct
+++ b/SConstruct
@@ -270,6 +270,7 @@ base_libs = [
'name' : 'crypto',
'sources' : """
hmac.cc
+ hmac_nss.cc
nss_key_util.cc
nss_util.cc
openssl_util.cc
@@ -282,9 +283,9 @@ base_libs = [
secure_hash.cc
secure_util.cc
sha2.cc
- signature_creator.cc
- signature_verifier.cc
- symmetric_key.cc
+ signature_creator_nss.cc
+ signature_verifier_nss.cc
+ symmetric_key_nss.cc
third_party/nss/rsawrapr.c
third_party/nss/sha512.cc
""",
@@ -308,7 +309,7 @@ base_libs = [
linux/seccomp-bpf/trap.cc
linux/seccomp-bpf-helpers/baseline_policy.cc
- linux/seccomp-bpf-helpers/sigsys_handlers.cc
+ linux/seccomp-bpf-helpers/sigsys_handlers.cc
linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
linux/seccomp-bpf-helpers/syscall_sets.cc
diff --git a/base/BUILD.gn b/base/BUILD.gn
index f84856de5c..c14798959b 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -13,22 +13,18 @@
# unpredictably for the various build types, we prefer a slightly different
# style. Instead, there are big per-platform blocks of inclusions and
# exclusions. If a given file has an inclusion or exclusion rule that applies
-# for multiple conditions, prefer to duplicate it in both lists. This makes it
+# for multiple conditions, perfer to duplicate it in both lists. This makes it
# a bit easier to see which files apply in which cases rather than having a
# huge sequence of random-looking conditionals.
import("//build/buildflag_header.gni")
import("//build/config/allocator.gni")
-import("//build/config/arm.gni")
import("//build/config/chromecast_build.gni")
-import("//build/config/clang/clang.gni")
import("//build/config/compiler/compiler.gni")
-import("//build/config/dcheck_always_on.gni")
import("//build/config/nacl/config.gni")
import("//build/config/sysroot.gni")
import("//build/config/ui.gni")
import("//build/nocompile.gni")
-import("//testing/libfuzzer/fuzzer_test.gni")
import("//testing/test.gni")
declare_args() {
@@ -36,20 +32,16 @@ declare_args() {
# See //base/build_time.cc and //build/write_build_date_header.py for more
# details and the expected format.
override_build_date = "N/A"
-
- # Turn on memory profiling in the task profiler when the heap shim is
- # available. Profiling can then be enabled at runtime by passing the command
- # line flag --enable-heap-profiling=task-profiler.
- enable_memory_task_profiler = use_experimental_allocator_shim
-
- # Partition alloc is included by default except iOS.
- use_partition_alloc = !is_ios
}
if (is_android) {
import("//build/config/android/rules.gni")
}
+if (is_win) {
+ import("//build/config/win/visual_studio_version.gni")
+}
+
config("base_flags") {
if (is_clang) {
cflags = [
@@ -149,11 +141,6 @@ component("base") {
"allocator/allocator_check.h",
"allocator/allocator_extension.cc",
"allocator/allocator_extension.h",
- "allocator/allocator_interception_mac.h",
- "allocator/allocator_interception_mac.mm",
- "allocator/allocator_shim.h",
- "allocator/malloc_zone_functions_mac.cc",
- "allocator/malloc_zone_functions_mac.h",
"android/animation_frame_time_histogram.cc",
"android/animation_frame_time_histogram.h",
"android/apk_assets.cc",
@@ -175,29 +162,23 @@ component("base") {
"android/context_utils.cc",
"android/context_utils.h",
"android/cpu_features.cc",
- "android/cpu_features.h",
"android/cxa_demangle_stub.cc",
- "android/early_trace_event_binding.cc",
- "android/early_trace_event_binding.h",
"android/event_log.cc",
"android/event_log.h",
"android/field_trial_list.cc",
"android/field_trial_list.h",
+ "android/fifo_utils.cc",
+ "android/fifo_utils.h",
"android/important_file_writer_android.cc",
"android/important_file_writer_android.h",
- "android/java_exception_reporter.cc",
- "android/java_exception_reporter.h",
"android/java_handler_thread.cc",
"android/java_handler_thread.h",
- "android/java_message_handler_factory.h",
"android/java_runtime.cc",
"android/java_runtime.h",
"android/jni_android.cc",
"android/jni_android.h",
"android/jni_array.cc",
"android/jni_array.h",
- "android/jni_generator/jni_generator_helper.h",
- "android/jni_int_wrapper.h",
"android/jni_registrar.cc",
"android/jni_registrar.h",
"android/jni_string.cc",
@@ -225,24 +206,16 @@ component("base") {
"android/record_user_action.h",
"android/scoped_java_ref.cc",
"android/scoped_java_ref.h",
- "android/statistics_recorder_android.cc",
- "android/statistics_recorder_android.h",
"android/sys_utils.cc",
"android/sys_utils.h",
- "android/throw_uncaught_exception.cc",
- "android/throw_uncaught_exception.h",
- "android/time_utils.cc",
- "android/time_utils.h",
+ "android/thread_utils.h",
"android/trace_event_binding.cc",
"android/trace_event_binding.h",
- "android/unguessable_token_android.cc",
- "android/unguessable_token_android.h",
"at_exit.cc",
"at_exit.h",
"atomic_ref_count.h",
"atomic_sequence_num.h",
"atomicops.h",
- "atomicops_internals_atomicword_compat.h",
"atomicops_internals_portable.h",
"atomicops_internals_x86_msvc.h",
"auto_reset.h",
@@ -265,34 +238,25 @@ component("base") {
"build_time.cc",
"build_time.h",
"callback.h",
- "callback_forward.h",
"callback_helpers.cc",
"callback_helpers.h",
"callback_internal.cc",
"callback_internal.h",
- "callback_list.h",
"cancelable_callback.h",
"command_line.cc",
"command_line.h",
"compiler_specific.h",
"containers/adapters.h",
- "containers/flat_set.h",
"containers/hash_tables.h",
"containers/linked_list.h",
"containers/mru_cache.h",
+ "containers/scoped_ptr_hash_map.h",
"containers/small_map.h",
"containers/stack_container.h",
"cpu.cc",
"cpu.h",
"critical_closure.h",
"critical_closure_internal_ios.mm",
-
- # This file depends on files from the "debug/allocator" target,
- # but this target does not depend on "debug/allocator".
- "debug/activity_analyzer.cc",
- "debug/activity_analyzer.h",
- "debug/activity_tracker.cc",
- "debug/activity_tracker.h",
"debug/alias.cc",
"debug/alias.h",
"debug/asan_invalid_access.cc",
@@ -309,6 +273,10 @@ component("base") {
"debug/dump_without_crashing.h",
"debug/gdi_debug_util_win.cc",
"debug/gdi_debug_util_win.h",
+
+ # This file depends on files from the "debug/allocator" target,
+ # but this target does not depend on "debug/allocator" (see
+ # allocator.gyp for details).
"debug/leak_annotations.h",
"debug/leak_tracker.h",
"debug/proc_maps_linux.cc",
@@ -322,18 +290,13 @@ component("base") {
"debug/stack_trace_win.cc",
"debug/task_annotator.cc",
"debug/task_annotator.h",
- "debug/thread_heap_usage_tracker.cc",
- "debug/thread_heap_usage_tracker.h",
"deferred_sequenced_task_runner.cc",
"deferred_sequenced_task_runner.h",
"environment.cc",
"environment.h",
- "event_types.h",
"feature_list.cc",
"feature_list.h",
"file_descriptor_posix.h",
- "file_descriptor_store.cc",
- "file_descriptor_store.h",
"file_version_info.h",
"file_version_info_mac.h",
"file_version_info_mac.mm",
@@ -343,9 +306,6 @@ component("base") {
"files/dir_reader_linux.h",
"files/dir_reader_posix.h",
"files/file.cc",
- "files/file.h",
- "files/file_descriptor_watcher_posix.cc",
- "files/file_descriptor_watcher_posix.h",
"files/file_enumerator.cc",
"files/file_enumerator.h",
"files/file_enumerator_posix.cc",
@@ -385,10 +345,6 @@ component("base") {
"files/memory_mapped_file_win.cc",
"files/scoped_file.cc",
"files/scoped_file.h",
- "files/scoped_platform_handle.cc",
- "files/scoped_platform_handle.h",
- "files/scoped_platform_handle_posix.cc",
- "files/scoped_platform_handle_win.cc",
"files/scoped_temp_dir.cc",
"files/scoped_temp_dir.h",
"format_macros.h",
@@ -398,7 +354,6 @@ component("base") {
"hash.cc",
"hash.h",
"id_map.h",
- "ios/block_types.h",
"ios/crb_protocol_observers.h",
"ios/crb_protocol_observers.mm",
"ios/device_util.h",
@@ -449,8 +404,8 @@ component("base") {
"mac/dispatch_source_mach.h",
"mac/foundation_util.h",
"mac/foundation_util.mm",
+ "mac/launch_services_util.cc",
"mac/launch_services_util.h",
- "mac/launch_services_util.mm",
"mac/launchd.cc",
"mac/launchd.h",
"mac/mac_logging.h",
@@ -465,8 +420,6 @@ component("base") {
"mac/mach_port_util.h",
"mac/objc_property_releaser.h",
"mac/objc_property_releaser.mm",
- "mac/objc_release_properties.h",
- "mac/objc_release_properties.mm",
"mac/os_crash_dumps.cc",
"mac/os_crash_dumps.h",
"mac/scoped_aedesc.h",
@@ -474,7 +427,6 @@ component("base") {
"mac/scoped_block.h",
"mac/scoped_cftyperef.h",
"mac/scoped_dispatch_object.h",
- "mac/scoped_ionotificationportref.h",
"mac/scoped_ioobject.h",
"mac/scoped_ioplugininterface.h",
"mac/scoped_launch_data.h",
@@ -506,12 +458,6 @@ component("base") {
"memory/free_deleter.h",
"memory/linked_ptr.h",
"memory/manual_constructor.h",
- "memory/memory_coordinator_client.cc",
- "memory/memory_coordinator_client.h",
- "memory/memory_coordinator_client_registry.cc",
- "memory/memory_coordinator_client_registry.h",
- "memory/memory_coordinator_proxy.cc",
- "memory/memory_coordinator_proxy.h",
"memory/memory_pressure_listener.cc",
"memory/memory_pressure_listener.h",
"memory/memory_pressure_monitor.cc",
@@ -526,7 +472,7 @@ component("base") {
"memory/raw_scoped_refptr_mismatch_checker.h",
"memory/ref_counted.cc",
"memory/ref_counted.h",
- "memory/ref_counted_delete_on_sequence.h",
+ "memory/ref_counted_delete_on_message_loop.h",
"memory/ref_counted_memory.cc",
"memory/ref_counted_memory.h",
"memory/scoped_policy.h",
@@ -536,8 +482,6 @@ component("base") {
"memory/shared_memory_handle.h",
"memory/shared_memory_handle_mac.cc",
"memory/shared_memory_handle_win.cc",
- "memory/shared_memory_helper.cc",
- "memory/shared_memory_helper.h",
"memory/shared_memory_mac.cc",
"memory/shared_memory_nacl.cc",
"memory/shared_memory_posix.cc",
@@ -568,15 +512,10 @@ component("base") {
"message_loop/message_pump_mac.mm",
"message_loop/message_pump_win.cc",
"message_loop/message_pump_win.h",
- "message_loop/timer_slack.h",
"metrics/bucket_ranges.cc",
"metrics/bucket_ranges.h",
"metrics/field_trial.cc",
"metrics/field_trial.h",
- "metrics/field_trial_param_associator.cc",
- "metrics/field_trial_param_associator.h",
- "metrics/field_trial_params.cc",
- "metrics/field_trial_params.h",
"metrics/histogram.cc",
"metrics/histogram.h",
"metrics/histogram_base.cc",
@@ -584,11 +523,7 @@ component("base") {
"metrics/histogram_delta_serialization.cc",
"metrics/histogram_delta_serialization.h",
"metrics/histogram_flattener.h",
- "metrics/histogram_functions.cc",
- "metrics/histogram_functions.h",
"metrics/histogram_macros.h",
- "metrics/histogram_macros_internal.h",
- "metrics/histogram_macros_local.h",
"metrics/histogram_samples.cc",
"metrics/histogram_samples.h",
"metrics/histogram_snapshot_manager.cc",
@@ -612,7 +547,6 @@ component("base") {
"metrics/user_metrics.cc",
"metrics/user_metrics.h",
"metrics/user_metrics_action.h",
- "native_library.cc",
"native_library.h",
"native_library_ios.mm",
"native_library_mac.mm",
@@ -626,8 +560,6 @@ component("base") {
"numerics/safe_conversions_impl.h",
"numerics/safe_math.h",
"numerics/safe_math_impl.h",
- "numerics/saturated_arithmetic.h",
- "numerics/saturated_arithmetic_arm.h",
"observer_list.h",
"observer_list_threadsafe.h",
"optional.h",
@@ -643,18 +575,23 @@ component("base") {
"pickle.h",
"posix/eintr_wrapper.h",
"posix/file_descriptor_shuffle.cc",
- "posix/file_descriptor_shuffle.h",
"posix/global_descriptors.cc",
"posix/global_descriptors.h",
"posix/safe_strerror.cc",
"posix/safe_strerror.h",
"posix/unix_domain_socket_linux.cc",
"posix/unix_domain_socket_linux.h",
- "post_task_and_reply_with_result_internal.h",
"power_monitor/power_monitor.cc",
"power_monitor/power_monitor.h",
"power_monitor/power_monitor_device_source.cc",
"power_monitor/power_monitor_device_source.h",
+ "power_monitor/power_monitor_device_source_android.cc",
+ "power_monitor/power_monitor_device_source_android.h",
+ "power_monitor/power_monitor_device_source_chromeos.cc",
+ "power_monitor/power_monitor_device_source_ios.mm",
+ "power_monitor/power_monitor_device_source_mac.mm",
+ "power_monitor/power_monitor_device_source_posix.cc",
+ "power_monitor/power_monitor_device_source_win.cc",
"power_monitor/power_monitor_source.cc",
"power_monitor/power_monitor_source.h",
"power_monitor/power_observer.h",
@@ -680,7 +617,6 @@ component("base") {
"process/port_provider_mac.h",
"process/process.h",
"process/process_handle.cc",
- "process/process_handle.h",
#"process/process_handle_freebsd.cc", # Unused in Chromium build.
"process/process_handle_linux.cc",
@@ -703,7 +639,6 @@ component("base") {
#"process/process_iterator_openbsd.cc", # Unused in Chromium build.
"process/process_iterator_win.cc",
"process/process_linux.cc",
- "process/process_mac.cc",
"process/process_metrics.cc",
"process/process_metrics.h",
@@ -736,7 +671,6 @@ component("base") {
"rand_util_win.cc",
"run_loop.cc",
"run_loop.h",
- "scoped_clear_errno.h",
"scoped_generic.h",
"scoped_native_library.cc",
"scoped_native_library.h",
@@ -744,8 +678,6 @@ component("base") {
"sequence_checker.h",
"sequence_checker_impl.cc",
"sequence_checker_impl.h",
- "sequence_token.cc",
- "sequence_token.h",
"sequenced_task_runner.cc",
"sequenced_task_runner.h",
"sequenced_task_runner_helpers.h",
@@ -793,8 +725,7 @@ component("base") {
"sync_socket.h",
"sync_socket_posix.cc",
"sync_socket_win.cc",
- "synchronization/atomic_flag.cc",
- "synchronization/atomic_flag.h",
+ "synchronization/cancellation_flag.cc",
"synchronization/cancellation_flag.h",
"synchronization/condition_variable.h",
"synchronization/condition_variable_posix.cc",
@@ -820,9 +751,6 @@ component("base") {
"sys_info.h",
"sys_info_android.cc",
"sys_info_chromeos.cc",
- "sys_info_internal.h",
- "syslog_logging.cc",
- "syslog_logging.h",
#"sys_info_freebsd.cc", # Unused in Chromium build.
"sys_info_ios.mm",
@@ -841,28 +769,20 @@ component("base") {
"task_runner_util.h",
"task_scheduler/delayed_task_manager.cc",
"task_scheduler/delayed_task_manager.h",
- "task_scheduler/initialization_util.cc",
- "task_scheduler/initialization_util.h",
- "task_scheduler/post_task.cc",
- "task_scheduler/post_task.h",
"task_scheduler/priority_queue.cc",
"task_scheduler/priority_queue.h",
"task_scheduler/scheduler_lock.h",
"task_scheduler/scheduler_lock_impl.cc",
"task_scheduler/scheduler_lock_impl.h",
- "task_scheduler/scheduler_single_thread_task_runner_manager.cc",
- "task_scheduler/scheduler_single_thread_task_runner_manager.h",
+ "task_scheduler/scheduler_service_thread.cc",
+ "task_scheduler/scheduler_service_thread.h",
"task_scheduler/scheduler_worker.cc",
"task_scheduler/scheduler_worker.h",
"task_scheduler/scheduler_worker_pool.h",
"task_scheduler/scheduler_worker_pool_impl.cc",
"task_scheduler/scheduler_worker_pool_impl.h",
- "task_scheduler/scheduler_worker_pool_params.cc",
- "task_scheduler/scheduler_worker_pool_params.h",
"task_scheduler/scheduler_worker_stack.cc",
"task_scheduler/scheduler_worker_stack.h",
- "task_scheduler/scoped_set_task_priority_for_current_thread.cc",
- "task_scheduler/scoped_set_task_priority_for_current_thread.h",
"task_scheduler/sequence.cc",
"task_scheduler/sequence.h",
"task_scheduler/sequence_sort_key.cc",
@@ -875,12 +795,9 @@ component("base") {
"task_scheduler/task_scheduler_impl.h",
"task_scheduler/task_tracker.cc",
"task_scheduler/task_tracker.h",
- "task_scheduler/task_tracker_posix.cc",
- "task_scheduler/task_tracker_posix.h",
"task_scheduler/task_traits.cc",
"task_scheduler/task_traits.h",
"template_util.h",
- "test/malloc_wrapper.h",
"third_party/dmg_fp/dmg_fp.h",
"third_party/dmg_fp/dtoa_wrapper.cc",
"third_party/dmg_fp/g_fmt.cc",
@@ -889,7 +806,6 @@ component("base") {
"third_party/nspr/prtime.cc",
"third_party/nspr/prtime.h",
"third_party/superfasthash/superfasthash.c",
- "third_party/valgrind/memcheck.h",
"threading/non_thread_safe.h",
"threading/non_thread_safe_impl.cc",
"threading/non_thread_safe_impl.h",
@@ -919,10 +835,13 @@ component("base") {
"threading/thread_id_name_manager.cc",
"threading/thread_id_name_manager.h",
"threading/thread_local.h",
+ "threading/thread_local_android.cc",
+ "threading/thread_local_posix.cc",
"threading/thread_local_storage.cc",
"threading/thread_local_storage.h",
"threading/thread_local_storage_posix.cc",
"threading/thread_local_storage_win.cc",
+ "threading/thread_local_win.cc",
"threading/thread_restrictions.cc",
"threading/thread_restrictions.h",
"threading/thread_task_runner_handle.cc",
@@ -956,15 +875,9 @@ component("base") {
"timer/mock_timer.h",
"timer/timer.cc",
"timer/timer.h",
- "trace_event/auto_open_close_event.cc",
- "trace_event/auto_open_close_event.h",
"trace_event/blame_context.cc",
"trace_event/blame_context.h",
- "trace_event/category_registry.cc",
- "trace_event/category_registry.h",
"trace_event/common/trace_event_common.h",
- "trace_event/event_name_filter.cc",
- "trace_event/event_name_filter.h",
"trace_event/heap_profiler.h",
"trace_event/heap_profiler_allocation_context.cc",
"trace_event/heap_profiler_allocation_context.h",
@@ -974,8 +887,6 @@ component("base") {
"trace_event/heap_profiler_allocation_register.h",
"trace_event/heap_profiler_allocation_register_posix.cc",
"trace_event/heap_profiler_allocation_register_win.cc",
- "trace_event/heap_profiler_event_filter.cc",
- "trace_event/heap_profiler_event_filter.h",
"trace_event/heap_profiler_heap_dump_writer.cc",
"trace_event/heap_profiler_heap_dump_writer.h",
"trace_event/heap_profiler_stack_frame_deduplicator.cc",
@@ -984,8 +895,6 @@ component("base") {
"trace_event/heap_profiler_type_name_deduplicator.h",
"trace_event/java_heap_dump_provider_android.cc",
"trace_event/java_heap_dump_provider_android.h",
- "trace_event/malloc_dump_provider.cc",
- "trace_event/malloc_dump_provider.h",
"trace_event/memory_allocator_dump.cc",
"trace_event/memory_allocator_dump.h",
"trace_event/memory_allocator_dump_guid.cc",
@@ -995,14 +904,10 @@ component("base") {
"trace_event/memory_dump_provider.h",
"trace_event/memory_dump_request_args.cc",
"trace_event/memory_dump_request_args.h",
- "trace_event/memory_dump_scheduler.cc",
- "trace_event/memory_dump_scheduler.h",
"trace_event/memory_dump_session_state.cc",
"trace_event/memory_dump_session_state.h",
"trace_event/memory_infra_background_whitelist.cc",
"trace_event/memory_infra_background_whitelist.h",
- "trace_event/memory_usage_estimator.cc",
- "trace_event/memory_usage_estimator.h",
"trace_event/process_memory_dump.cc",
"trace_event/process_memory_dump.h",
"trace_event/process_memory_maps.cc",
@@ -1011,7 +916,6 @@ component("base") {
"trace_event/process_memory_totals.h",
"trace_event/trace_buffer.cc",
"trace_event/trace_buffer.h",
- "trace_event/trace_category.h",
"trace_event/trace_config.cc",
"trace_event/trace_config.h",
"trace_event/trace_event.h",
@@ -1020,8 +924,6 @@ component("base") {
"trace_event/trace_event_argument.h",
"trace_event/trace_event_etw_export_win.cc",
"trace_event/trace_event_etw_export_win.h",
- "trace_event/trace_event_filter.cc",
- "trace_event/trace_event_filter.h",
"trace_event/trace_event_impl.cc",
"trace_event/trace_event_impl.h",
"trace_event/trace_event_memory_overhead.cc",
@@ -1033,15 +935,17 @@ component("base") {
"trace_event/trace_log.cc",
"trace_event/trace_log.h",
"trace_event/trace_log_constants.cc",
+ "trace_event/trace_sampling_thread.cc",
+ "trace_event/trace_sampling_thread.h",
"trace_event/tracing_agent.cc",
"trace_event/tracing_agent.h",
+ "trace_event/winheap_dump_provider_win.cc",
+ "trace_event/winheap_dump_provider_win.h",
"tracked_objects.cc",
"tracked_objects.h",
"tracking_info.cc",
"tracking_info.h",
"tuple.h",
- "unguessable_token.cc",
- "unguessable_token.h",
"value_conversions.cc",
"value_conversions.h",
"values.cc",
@@ -1103,7 +1007,6 @@ component("base") {
"win/wrapped_window_proc.h",
]
- all_dependent_configs = []
defines = []
data = []
@@ -1129,48 +1032,14 @@ component("base") {
]
# Needed for <atomic> if using newer C++ library than sysroot
- if (!use_sysroot && (is_android || (is_linux && !is_chromecast))) {
+ if (!use_sysroot && (is_android || is_linux)) {
libs = [ "atomic" ]
}
if (use_experimental_allocator_shim) {
- # TODO(primiano): support other platforms, currently this works only on
- # Linux/CrOS/Android. http://crbug.com/550886 .
- sources += [
- "allocator/allocator_shim.cc",
- "allocator/allocator_shim.h",
- "allocator/allocator_shim_internals.h",
- "allocator/allocator_shim_override_cpp_symbols.h",
- "allocator/allocator_shim_override_libc_symbols.h",
- ]
- if (is_win) {
- sources += [
- "allocator/allocator_shim_default_dispatch_to_winheap.cc",
- "allocator/allocator_shim_override_ucrt_symbols_win.h",
- "allocator/winheap_stubs_win.cc",
- "allocator/winheap_stubs_win.h",
- ]
- } else if (is_linux && use_allocator == "tcmalloc") {
- sources += [
- "allocator/allocator_shim_default_dispatch_to_tcmalloc.cc",
- "allocator/allocator_shim_override_glibc_weak_symbols.h",
- ]
- deps += [ "//base/allocator:tcmalloc" ]
- } else if (is_linux && use_allocator == "none") {
- sources += [ "allocator/allocator_shim_default_dispatch_to_glibc.cc" ]
- } else if (is_android && use_allocator == "none") {
- sources += [
- "allocator/allocator_shim_default_dispatch_to_linker_wrapped_symbols.cc",
- "allocator/allocator_shim_override_linker_wrapped_symbols.h",
- ]
- all_dependent_configs += [ "//base/allocator:wrap_malloc_symbols" ]
- } else if (is_mac) {
- sources += [
- "allocator/allocator_shim_default_dispatch_to_mac_zoned_malloc.cc",
- "allocator/allocator_shim_default_dispatch_to_mac_zoned_malloc.h",
- "allocator/allocator_shim_override_mac_symbols.h",
- ]
- }
+ # The allocator shim is part of the base API. This is to allow clients of
+ # base should to install hooks into the allocator path.
+ public_deps += [ "//base/allocator:unified_allocator_shim" ]
}
# Allow more direct string conversions on platforms with native utf8
@@ -1181,7 +1050,10 @@ component("base") {
# Android.
if (is_android) {
- sources -= [ "debug/stack_trace_posix.cc" ]
+ sources -= [
+ "debug/stack_trace_posix.cc",
+ "power_monitor/power_monitor_device_source_posix.cc",
+ ]
# Android uses some Linux sources, put those back.
set_sources_assignment_filter([])
@@ -1189,14 +1061,14 @@ component("base") {
"debug/proc_maps_linux.cc",
"files/file_path_watcher_linux.cc",
"posix/unix_domain_socket_linux.cc",
- "power_monitor/power_monitor_device_source_android.cc",
- "power_monitor/power_monitor_device_source_android.h",
"process/internal_linux.cc",
"process/memory_linux.cc",
"process/process_handle_linux.cc",
"process/process_iterator_linux.cc",
"process/process_metrics_linux.cc",
"sys_info_linux.cc",
+ "trace_event/malloc_dump_provider.cc",
+ "trace_event/malloc_dump_provider.h",
]
set_sources_assignment_filter(sources_assignment_filter)
@@ -1213,7 +1085,7 @@ component("base") {
# Chromeos.
if (is_chromeos) {
- sources += [ "power_monitor/power_monitor_device_source_chromeos.cc" ]
+ sources -= [ "power_monitor/power_monitor_device_source_posix.cc" ]
}
# NaCl.
@@ -1247,10 +1119,7 @@ component("base") {
"memory/discardable_memory_allocator.h",
"memory/discardable_shared_memory.cc",
"memory/discardable_shared_memory.h",
- "memory/shared_memory_helper.cc",
- "memory/shared_memory_helper.h",
"memory/shared_memory_posix.cc",
- "native_library.cc",
"native_library_posix.cc",
"path_service.cc",
"process/kill.cc",
@@ -1267,8 +1136,6 @@ component("base") {
"synchronization/read_write_lock_posix.cc",
"sys_info.cc",
"sys_info_posix.cc",
- "task_scheduler/initialization_util.cc",
- "task_scheduler/initialization_util.h",
"trace_event/trace_event_system_stats_monitor.cc",
]
@@ -1280,8 +1147,6 @@ component("base") {
configs += [ ":nacl_nonsfi_warnings" ]
} else {
sources -= [
- "files/file_descriptor_watcher_posix.cc",
- "files/file_descriptor_watcher_posix.h",
"files/file_util.cc",
"files/file_util.h",
"files/file_util_posix.cc",
@@ -1294,8 +1159,6 @@ component("base") {
"process/launch.h",
"process/launch_posix.cc",
"rand_util_posix.cc",
- "task_scheduler/task_tracker_posix.cc",
- "task_scheduler/task_tracker_posix.h",
]
}
} else {
@@ -1307,37 +1170,16 @@ component("base") {
"rand_util_nacl.cc",
"synchronization/read_write_lock_nacl.cc",
]
-
- if (use_partition_alloc) {
- # Add stuff that doesn't work in NaCl.
- sources += [
- # PartitionAlloc uses SpinLock, which doesn't work in NaCl (see below).
- "allocator/partition_allocator/address_space_randomization.cc",
- "allocator/partition_allocator/address_space_randomization.h",
- "allocator/partition_allocator/oom.h",
- "allocator/partition_allocator/page_allocator.cc",
- "allocator/partition_allocator/page_allocator.h",
- "allocator/partition_allocator/partition_alloc.cc",
- "allocator/partition_allocator/partition_alloc.h",
- "allocator/partition_allocator/spin_lock.cc",
- "allocator/partition_allocator/spin_lock.h",
- ]
- }
}
# Windows.
if (is_win) {
sources += [
- "power_monitor/power_monitor_device_source_win.cc",
"profiler/win32_stack_frame_unwinder.cc",
"profiler/win32_stack_frame_unwinder.h",
]
sources -= [
- "file_descriptor_store.cc",
- "file_descriptor_store.h",
- "memory/shared_memory_helper.cc",
- "memory/shared_memory_helper.h",
"message_loop/message_pump_libevent.cc",
"strings/string16.cc",
]
@@ -1345,7 +1187,6 @@ component("base") {
deps += [
"//base/trace_event/etw_manifest:chrome_events_win",
"//base/win:base_win_features",
- "//base/win:eventlog_messages",
]
if (is_component_build) {
@@ -1360,60 +1201,63 @@ component("base") {
# These runtime files are copied to the output directory by the
# vs_toolchain script that runs as part of toolchain configuration.
- data += [
- "$root_out_dir/msvcp140${vcrt_suffix}.dll",
- "$root_out_dir/vccorlib140${vcrt_suffix}.dll",
- "$root_out_dir/vcruntime140${vcrt_suffix}.dll",
-
- # Universal Windows 10 CRT files
- "$root_out_dir/api-ms-win-core-console-l1-1-0.dll",
- "$root_out_dir/api-ms-win-core-datetime-l1-1-0.dll",
- "$root_out_dir/api-ms-win-core-debug-l1-1-0.dll",
- "$root_out_dir/api-ms-win-core-errorhandling-l1-1-0.dll",
- "$root_out_dir/api-ms-win-core-file-l1-1-0.dll",
- "$root_out_dir/api-ms-win-core-file-l1-2-0.dll",
- "$root_out_dir/api-ms-win-core-file-l2-1-0.dll",
- "$root_out_dir/api-ms-win-core-handle-l1-1-0.dll",
- "$root_out_dir/api-ms-win-core-heap-l1-1-0.dll",
- "$root_out_dir/api-ms-win-core-interlocked-l1-1-0.dll",
- "$root_out_dir/api-ms-win-core-libraryloader-l1-1-0.dll",
- "$root_out_dir/api-ms-win-core-localization-l1-2-0.dll",
- "$root_out_dir/api-ms-win-core-memory-l1-1-0.dll",
- "$root_out_dir/api-ms-win-core-namedpipe-l1-1-0.dll",
- "$root_out_dir/api-ms-win-core-processenvironment-l1-1-0.dll",
- "$root_out_dir/api-ms-win-core-processthreads-l1-1-0.dll",
- "$root_out_dir/api-ms-win-core-processthreads-l1-1-1.dll",
- "$root_out_dir/api-ms-win-core-profile-l1-1-0.dll",
- "$root_out_dir/api-ms-win-core-rtlsupport-l1-1-0.dll",
- "$root_out_dir/api-ms-win-core-string-l1-1-0.dll",
- "$root_out_dir/api-ms-win-core-synch-l1-1-0.dll",
- "$root_out_dir/api-ms-win-core-synch-l1-2-0.dll",
- "$root_out_dir/api-ms-win-core-sysinfo-l1-1-0.dll",
- "$root_out_dir/api-ms-win-core-timezone-l1-1-0.dll",
- "$root_out_dir/api-ms-win-core-util-l1-1-0.dll",
- "$root_out_dir/api-ms-win-crt-conio-l1-1-0.dll",
- "$root_out_dir/api-ms-win-crt-convert-l1-1-0.dll",
- "$root_out_dir/api-ms-win-crt-environment-l1-1-0.dll",
- "$root_out_dir/api-ms-win-crt-filesystem-l1-1-0.dll",
- "$root_out_dir/api-ms-win-crt-heap-l1-1-0.dll",
- "$root_out_dir/api-ms-win-crt-locale-l1-1-0.dll",
- "$root_out_dir/api-ms-win-crt-math-l1-1-0.dll",
- "$root_out_dir/api-ms-win-crt-multibyte-l1-1-0.dll",
- "$root_out_dir/api-ms-win-crt-private-l1-1-0.dll",
- "$root_out_dir/api-ms-win-crt-process-l1-1-0.dll",
- "$root_out_dir/api-ms-win-crt-runtime-l1-1-0.dll",
- "$root_out_dir/api-ms-win-crt-stdio-l1-1-0.dll",
- "$root_out_dir/api-ms-win-crt-string-l1-1-0.dll",
- "$root_out_dir/api-ms-win-crt-time-l1-1-0.dll",
- "$root_out_dir/api-ms-win-crt-utility-l1-1-0.dll",
- "$root_out_dir/ucrtbase${vcrt_suffix}.dll",
- ]
+ if (visual_studio_version == "2015") {
+ data += [
+ "$root_out_dir/msvcp140${vcrt_suffix}.dll",
+ "$root_out_dir/vccorlib140${vcrt_suffix}.dll",
+ "$root_out_dir/vcruntime140${vcrt_suffix}.dll",
+
+ # Universal Windows 10 CRT files
+ "$root_out_dir/api-ms-win-core-console-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-core-datetime-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-core-debug-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-core-errorhandling-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-core-file-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-core-file-l1-2-0.dll",
+ "$root_out_dir/api-ms-win-core-file-l2-1-0.dll",
+ "$root_out_dir/api-ms-win-core-handle-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-core-heap-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-core-interlocked-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-core-libraryloader-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-core-localization-l1-2-0.dll",
+ "$root_out_dir/api-ms-win-core-memory-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-core-namedpipe-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-core-processenvironment-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-core-processthreads-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-core-processthreads-l1-1-1.dll",
+ "$root_out_dir/api-ms-win-core-profile-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-core-rtlsupport-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-core-string-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-core-synch-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-core-synch-l1-2-0.dll",
+ "$root_out_dir/api-ms-win-core-sysinfo-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-core-timezone-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-core-util-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-crt-conio-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-crt-convert-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-crt-environment-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-crt-filesystem-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-crt-heap-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-crt-locale-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-crt-math-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-crt-multibyte-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-crt-private-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-crt-process-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-crt-runtime-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-crt-stdio-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-crt-string-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-crt-time-l1-1-0.dll",
+ "$root_out_dir/api-ms-win-crt-utility-l1-1-0.dll",
+ "$root_out_dir/ucrtbase${vcrt_suffix}.dll",
+ ]
+ } else {
+ data += [
+ "$root_out_dir/msvcp120${vcrt_suffix}.dll",
+ "$root_out_dir/msvcr120${vcrt_suffix}.dll",
+ ]
+ }
if (is_asan) {
- if (current_cpu == "x64") {
- data += [ "$clang_base_path/lib/clang/$clang_version/lib/windows/clang_rt.asan_dynamic-x86_64.dll" ]
- } else {
- data += [ "$clang_base_path/lib/clang/$clang_version/lib/windows/clang_rt.asan_dynamic-i386.dll" ]
- }
+ data += [ "//third_party/llvm-build/Release+Asserts/lib/clang/$clang_version/lib/windows/clang_rt.asan_dynamic-i386.dll" ]
}
}
@@ -1427,7 +1271,7 @@ component("base") {
"userenv.lib",
"winmm.lib",
]
- all_dependent_configs += [ ":base_win_linker_flags" ]
+ all_dependent_configs = [ ":base_win_linker_flags" ]
} else if (!is_nacl || is_nacl_nonsfi) {
# Non-Windows.
deps += [ "//base/third_party/libevent" ]
@@ -1436,10 +1280,9 @@ component("base") {
# Desktop Mac.
if (is_mac) {
sources += [
- "mac/scoped_typeref.h",
- "power_monitor/power_monitor_device_source_mac.mm",
+ "trace_event/malloc_dump_provider.cc",
+ "trace_event/malloc_dump_provider.h",
]
-
libs = [
"ApplicationServices.framework",
"AppKit.framework",
@@ -1470,6 +1313,11 @@ component("base") {
# Linux.
if (is_linux) {
+ sources += [
+ "trace_event/malloc_dump_provider.cc",
+ "trace_event/malloc_dump_provider.h",
+ ]
+
if (is_asan || is_lsan || is_msan || is_tsan) {
# For llvm-sanitizer.
data += [ "//third_party/llvm-build/Release+Asserts/lib/libstdc++.so.6" ]
@@ -1484,7 +1332,7 @@ component("base") {
defines += [ "USE_SYMBOLIZE" ]
configs += linux_configs
- all_dependent_configs += linux_configs
+ all_dependent_configs = linux_configs
# These dependencies are not required on Android, and in the case
# of xdg_mime must be excluded due to licensing restrictions.
@@ -1558,8 +1406,6 @@ component("base") {
"mac/mach_logging.h",
"mac/objc_property_releaser.h",
"mac/objc_property_releaser.mm",
- "mac/objc_release_properties.h",
- "mac/objc_release_properties.mm",
"mac/scoped_block.h",
"mac/scoped_mach_port.cc",
"mac/scoped_mach_port.h",
@@ -1575,7 +1421,6 @@ component("base") {
"memory/shared_memory_posix.cc",
"message_loop/message_pump_mac.h",
"message_loop/message_pump_mac.mm",
- "power_monitor/power_monitor_device_source_ios.mm",
"process/memory_stubs.cc",
"strings/sys_string_conversions_mac.mm",
"threading/platform_thread_mac.mm",
@@ -1585,17 +1430,6 @@ component("base") {
set_sources_assignment_filter(sources_assignment_filter)
}
- if (is_posix && !is_mac && !is_ios && !is_android && !is_chromeos) {
- sources += [ "power_monitor/power_monitor_device_source_posix.cc" ]
- }
-
- if (is_posix && !is_mac && !is_nacl) {
- sources += [
- "memory/shared_memory_tracker.cc",
- "memory/shared_memory_tracker.h",
- ]
- }
-
if (!use_glib) {
sources -= [
"message_loop/message_pump_glib.cc",
@@ -1603,7 +1437,7 @@ component("base") {
]
}
- if (using_sanitizer) {
+ if (is_asan || is_lsan || is_msan || is_tsan) {
data += [ "//tools/valgrind/asan/" ]
if (is_win) {
data +=
@@ -1625,10 +1459,7 @@ component("base") {
buildflag_header("debugging_flags") {
header = "debugging_flags.h"
header_dir = "base/debug"
- flags = [
- "ENABLE_PROFILING=$enable_profiling",
- "ENABLE_MEMORY_TASK_PROFILER=$enable_memory_task_profiler",
- ]
+ flags = [ "ENABLE_PROFILING=$enable_profiling" ]
}
# This is the subset of files from base that should not be used with a dynamic
@@ -1638,20 +1469,10 @@ static_library("base_static") {
sources = [
"base_switches.cc",
"base_switches.h",
- "task_scheduler/switches.cc",
- "task_scheduler/switches.h",
"win/pe_image.cc",
"win/pe_image.h",
]
- if (is_win) {
- # Disable sanitizer coverage in win/pe_image.cc. It is called by the sandbox
- # before sanitizer coverage can initialize. http://crbug.com/484711
- configs -= [ "//build/config/sanitizers:default_sanitizer_flags" ]
- configs +=
- [ "//build/config/sanitizers:default_sanitizer_flags_but_coverage" ]
- }
-
if (!is_debug) {
configs -= [ "//build/config/compiler:default_optimization" ]
configs += [ "//build/config/compiler:optimize_max" ]
@@ -1672,14 +1493,12 @@ component("i18n") {
"i18n/case_conversion.h",
"i18n/char_iterator.cc",
"i18n/char_iterator.h",
- "i18n/character_encoding.cc",
- "i18n/character_encoding.h",
- "i18n/encoding_detection.cc",
- "i18n/encoding_detection.h",
"i18n/file_util_icu.cc",
"i18n/file_util_icu.h",
"i18n/i18n_constants.cc",
"i18n/i18n_constants.h",
+ "i18n/icu_encoding_detection.cc",
+ "i18n/icu_encoding_detection.h",
"i18n/icu_string_conversions.cc",
"i18n/icu_string_conversions.h",
"i18n/icu_util.cc",
@@ -1706,7 +1525,6 @@ component("i18n") {
defines = [ "BASE_I18N_IMPLEMENTATION" ]
configs += [ "//build/config/compiler:wexit_time_destructors" ]
public_deps = [
- "//third_party/ced",
"//third_party/icu",
]
deps = [
@@ -1732,7 +1550,6 @@ test("base_perftests") {
"message_loop/message_pump_perftest.cc",
# "test/run_all_unittests.cc",
- "json/json_perftest.cc",
"threading/thread_perftest.cc",
]
deps = [
@@ -1893,7 +1710,6 @@ if (is_ios || is_mac) {
test("base_unittests") {
sources = [
- "allocator/malloc_zone_functions_mac_unittest.cc",
"allocator/tcmalloc_unittest.cc",
"android/application_status_listener_unittest.cc",
"android/content_uri_utils_unittest.cc",
@@ -1904,14 +1720,12 @@ test("base_unittests") {
"android/path_utils_unittest.cc",
"android/scoped_java_ref_unittest.cc",
"android/sys_utils_unittest.cc",
- "android/unguessable_token_android_unittest.cc",
"at_exit_unittest.cc",
"atomicops_unittest.cc",
"barrier_closure_unittest.cc",
"base64_unittest.cc",
"base64url_unittest.cc",
"big_endian_unittest.cc",
- "bind_helpers_unittest.cc",
"bind_unittest.cc",
"bit_cast_unittest.cc",
"bits_unittest.cc",
@@ -1922,28 +1736,24 @@ test("base_unittests") {
"cancelable_callback_unittest.cc",
"command_line_unittest.cc",
"containers/adapters_unittest.cc",
- "containers/flat_set_unittest.cc",
"containers/hash_tables_unittest.cc",
"containers/linked_list_unittest.cc",
"containers/mru_cache_unittest.cc",
+ "containers/scoped_ptr_hash_map_unittest.cc",
"containers/small_map_unittest.cc",
"containers/stack_container_unittest.cc",
"cpu_unittest.cc",
- "debug/activity_analyzer_unittest.cc",
- "debug/activity_tracker_unittest.cc",
"debug/crash_logging_unittest.cc",
"debug/debugger_unittest.cc",
"debug/leak_tracker_unittest.cc",
"debug/proc_maps_linux_unittest.cc",
"debug/stack_trace_unittest.cc",
"debug/task_annotator_unittest.cc",
- "debug/thread_heap_usage_tracker_unittest.cc",
"deferred_sequenced_task_runner_unittest.cc",
"environment_unittest.cc",
"feature_list_unittest.cc",
"file_version_info_win_unittest.cc",
"files/dir_reader_posix_unittest.cc",
- "files/file_descriptor_watcher_posix_unittest.cc",
"files/file_locking_unittest.cc",
"files/file_path_unittest.cc",
"files/file_path_watcher_unittest.cc",
@@ -1953,7 +1763,6 @@ test("base_unittests") {
"files/file_util_unittest.cc",
"files/important_file_writer_unittest.cc",
"files/memory_mapped_file_unittest.cc",
- "files/scoped_platform_handle_unittest.cc",
"files/scoped_temp_dir_unittest.cc",
"gmock_unittest.cc",
"guid_unittest.cc",
@@ -1961,7 +1770,6 @@ test("base_unittests") {
"i18n/break_iterator_unittest.cc",
"i18n/case_conversion_unittest.cc",
"i18n/char_iterator_unittest.cc",
- "i18n/character_encoding_unittest.cc",
"i18n/file_util_icu_unittest.cc",
"i18n/icu_string_conversions_unittest.cc",
"i18n/message_formatter_unittest.cc",
@@ -1989,7 +1797,6 @@ test("base_unittests") {
"mac/mac_util_unittest.mm",
"mac/mach_port_broker_unittest.cc",
"mac/objc_property_releaser_unittest.mm",
- "mac/objc_release_properties_unittest.mm",
"mac/scoped_nsobject_unittest.mm",
"mac/scoped_objc_class_swizzler_unittest.mm",
"mac/scoped_sending_event_unittest.mm",
@@ -1997,11 +1804,9 @@ test("base_unittests") {
"memory/aligned_memory_unittest.cc",
"memory/discardable_shared_memory_unittest.cc",
"memory/linked_ptr_unittest.cc",
- "memory/memory_coordinator_client_registry_unittest.cc",
"memory/memory_pressure_listener_unittest.cc",
"memory/memory_pressure_monitor_chromeos_unittest.cc",
"memory/memory_pressure_monitor_mac_unittest.cc",
- "memory/memory_pressure_monitor_unittest.cc",
"memory/memory_pressure_monitor_win_unittest.cc",
"memory/ptr_util_unittest.cc",
"memory/ref_counted_memory_unittest.cc",
@@ -2016,13 +1821,10 @@ test("base_unittests") {
"message_loop/message_loop_unittest.cc",
"message_loop/message_pump_glib_unittest.cc",
"message_loop/message_pump_io_ios_unittest.cc",
- "message_loop/message_pump_mac_unittest.cc",
"metrics/bucket_ranges_unittest.cc",
- "metrics/field_trial_params_unittest.cc",
"metrics/field_trial_unittest.cc",
"metrics/histogram_base_unittest.cc",
"metrics/histogram_delta_serialization_unittest.cc",
- "metrics/histogram_functions_unittest.cc",
"metrics/histogram_macros_unittest.cc",
"metrics/histogram_snapshot_manager_unittest.cc",
"metrics/histogram_unittest.cc",
@@ -2036,12 +1838,10 @@ test("base_unittests") {
"metrics/statistics_recorder_unittest.cc",
"native_library_unittest.cc",
"numerics/safe_numerics_unittest.cc",
- "numerics/saturated_arithmetic_unittest.cc",
"observer_list_unittest.cc",
"optional_unittest.cc",
"os_compat_android_unittest.cc",
"path_service_unittest.cc",
- "pending_task_unittest.cc",
"pickle_unittest.cc",
"posix/file_descriptor_shuffle_unittest.cc",
"posix/unix_domain_socket_linux_unittest.cc",
@@ -2062,8 +1862,6 @@ test("base_unittests") {
"scoped_native_library_unittest.cc",
"security_unittest.cc",
"sequence_checker_unittest.cc",
- "sequence_token_unittest.cc",
- "sequenced_task_runner_unittest.cc",
"sha1_unittest.cc",
"stl_util_unittest.cc",
"strings/nullable_string16_unittest.cc",
@@ -2083,7 +1881,7 @@ test("base_unittests") {
"strings/utf_string_conversions_unittest.cc",
"supports_user_data_unittest.cc",
"sync_socket_unittest.cc",
- "synchronization/atomic_flag_unittest.cc",
+ "synchronization/cancellation_flag_unittest.cc",
"synchronization/condition_variable_unittest.cc",
"synchronization/lock_unittest.cc",
"synchronization/read_write_lock_unittest.cc",
@@ -2097,33 +1895,27 @@ test("base_unittests") {
"task_scheduler/delayed_task_manager_unittest.cc",
"task_scheduler/priority_queue_unittest.cc",
"task_scheduler/scheduler_lock_unittest.cc",
- "task_scheduler/scheduler_single_thread_task_runner_manager_unittest.cc",
+ "task_scheduler/scheduler_service_thread_unittest.cc",
"task_scheduler/scheduler_worker_pool_impl_unittest.cc",
"task_scheduler/scheduler_worker_stack_unittest.cc",
"task_scheduler/scheduler_worker_unittest.cc",
- "task_scheduler/scoped_set_task_priority_for_current_thread_unittest.cc",
"task_scheduler/sequence_sort_key_unittest.cc",
"task_scheduler/sequence_unittest.cc",
"task_scheduler/task_scheduler_impl_unittest.cc",
- "task_scheduler/task_tracker_posix_unittest.cc",
"task_scheduler/task_tracker_unittest.cc",
- "task_scheduler/task_traits_unittest.cc",
- "task_scheduler/task_unittest.cc",
"task_scheduler/test_task_factory.cc",
"task_scheduler/test_task_factory.h",
"task_scheduler/test_utils.h",
"template_util_unittest.cc",
"test/histogram_tester_unittest.cc",
- "test/mock_callback_unittest.cc",
- "test/scoped_mock_time_message_loop_task_runner_unittest.cc",
- "test/scoped_task_scheduler_unittest.cc",
+ "test/icu_test_util.cc",
+ "test/icu_test_util.h",
"test/test_pending_task_unittest.cc",
"test/test_reg_util_win_unittest.cc",
"test/trace_event_analyzer_unittest.cc",
"test/user_action_tester_unittest.cc",
"threading/non_thread_safe_unittest.cc",
"threading/platform_thread_unittest.cc",
- "threading/post_task_and_reply_impl_unittest.cc",
"threading/sequenced_task_runner_handle_unittest.cc",
"threading/sequenced_worker_pool_unittest.cc",
"threading/simple_thread_unittest.cc",
@@ -2132,7 +1924,6 @@ test("base_unittests") {
"threading/thread_id_name_manager_unittest.cc",
"threading/thread_local_storage_unittest.cc",
"threading/thread_local_unittest.cc",
- "threading/thread_task_runner_handle_unittest.cc",
"threading/thread_unittest.cc",
"threading/watchdog_unittest.cc",
"threading/worker_pool_posix_unittest.cc",
@@ -2145,7 +1936,6 @@ test("base_unittests") {
"timer/timer_unittest.cc",
"tools_sanity_unittest.cc",
"trace_event/blame_context_unittest.cc",
- "trace_event/event_name_filter_unittest.cc",
"trace_event/heap_profiler_allocation_context_tracker_unittest.cc",
"trace_event/heap_profiler_allocation_register_unittest.cc",
"trace_event/heap_profiler_heap_dump_writer_unittest.cc",
@@ -2154,19 +1944,15 @@ test("base_unittests") {
"trace_event/java_heap_dump_provider_android_unittest.cc",
"trace_event/memory_allocator_dump_unittest.cc",
"trace_event/memory_dump_manager_unittest.cc",
- "trace_event/memory_usage_estimator_unittest.cc",
"trace_event/process_memory_dump_unittest.cc",
- "trace_event/trace_category_unittest.cc",
"trace_event/trace_config_unittest.cc",
"trace_event/trace_event_argument_unittest.cc",
- "trace_event/trace_event_filter_test_utils.cc",
- "trace_event/trace_event_filter_test_utils.h",
"trace_event/trace_event_synthetic_delay_unittest.cc",
"trace_event/trace_event_system_stats_monitor_unittest.cc",
"trace_event/trace_event_unittest.cc",
+ "trace_event/winheap_dump_provider_win_unittest.cc",
"tracked_objects_unittest.cc",
"tuple_unittest.cc",
- "unguessable_token_unittest.cc",
"values_unittest.cc",
"version_unittest.cc",
"vlog_unittest.cc",
@@ -2200,9 +1986,7 @@ test("base_unittests") {
":base",
":i18n",
":message_loop_tests",
- "//base/allocator:features",
- "//base/test:native_library_test_utils",
- "//base/test:run_all_base_unittests",
+ "//base/test:run_all_unittests",
"//base/test:test_support",
"//base/third_party/dynamic_annotations",
"//testing/gmock",
@@ -2210,10 +1994,6 @@ test("base_unittests") {
"//third_party/icu",
]
- data_deps = [
- "//base/test:test_shared_library",
- ]
-
if (is_ios || is_mac) {
deps += [ ":base_unittests_arc" ]
}
@@ -2236,15 +2016,10 @@ test("base_unittests") {
}
if (is_android) {
- sources -= [
- "process/process_unittest.cc",
- "process/process_util_unittest.cc",
- ]
deps += [
":base_java",
":base_java_unittest_support",
"//base/android/jni_generator:jni_generator_tests",
- "//base/test:test_support_java",
]
}
@@ -2267,7 +2042,6 @@ test("base_unittests") {
"mac/bind_objc_block_unittest.mm",
"mac/foundation_util_unittest.mm",
"mac/objc_property_releaser_unittest.mm",
- "mac/objc_release_properties_unittest.mm",
"mac/scoped_nsobject_unittest.mm",
"strings/sys_string_conversions_mac_unittest.mm",
]
@@ -2276,10 +2050,6 @@ test("base_unittests") {
# TODO(GYP): dep on copy_test_data_ios action.
}
- if (use_partition_alloc) {
- sources += [ "allocator/partition_allocator/partition_alloc_unittest.cc" ]
- }
-
if (is_mac) {
libs = [
"CoreFoundation.framework",
@@ -2294,6 +2064,10 @@ test("base_unittests") {
deps += [ "//base/test:malloc_wrapper" ]
+ if (use_glib) {
+ configs += [ "//build/config/linux:glib" ]
+ }
+
if (!is_component_build) {
# Set rpath to find libmalloc_wrapper.so even in a non-component build.
configs += [ "//build/config/gcc:rpath_for_built_shared_libraries" ]
@@ -2382,7 +2156,6 @@ if (enable_nocompile_tests) {
"callback_list_unittest.nc",
"callback_unittest.nc",
"memory/weak_ptr_unittest.nc",
- "metrics/histogram_unittest.nc",
]
deps = [
@@ -2394,6 +2167,7 @@ if (enable_nocompile_tests) {
}
if (is_android) {
+ # GYP: //base.gyp:base_jni_headers
generate_jni("base_jni_headers") {
sources = [
"android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java",
@@ -2405,12 +2179,10 @@ if (is_android) {
"android/java/src/org/chromium/base/ContentUriUtils.java",
"android/java/src/org/chromium/base/ContextUtils.java",
"android/java/src/org/chromium/base/CpuFeatures.java",
- "android/java/src/org/chromium/base/EarlyTraceEvent.java",
"android/java/src/org/chromium/base/EventLog.java",
"android/java/src/org/chromium/base/FieldTrialList.java",
"android/java/src/org/chromium/base/ImportantFileWriterAndroid.java",
"android/java/src/org/chromium/base/JNIUtils.java",
- "android/java/src/org/chromium/base/JavaExceptionReporter.java",
"android/java/src/org/chromium/base/JavaHandlerThread.java",
"android/java/src/org/chromium/base/LocaleUtils.java",
"android/java/src/org/chromium/base/MemoryPressureListener.java",
@@ -2420,14 +2192,10 @@ if (is_android) {
"android/java/src/org/chromium/base/SysUtils.java",
"android/java/src/org/chromium/base/SystemMessageHandler.java",
"android/java/src/org/chromium/base/ThreadUtils.java",
- "android/java/src/org/chromium/base/ThrowUncaughtException.java",
- "android/java/src/org/chromium/base/TimeUtils.java",
"android/java/src/org/chromium/base/TraceEvent.java",
- "android/java/src/org/chromium/base/UnguessableToken.java",
"android/java/src/org/chromium/base/library_loader/LibraryLoader.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",
]
public_deps = [
@@ -2437,11 +2205,13 @@ if (is_android) {
jni_package = "base"
}
+ # GYP: //base.gyp:android_runtime_jni_headers
generate_jar_jni("android_runtime_jni_headers") {
jni_package = "base"
classes = [ "java/lang/Runtime.class" ]
}
+ # GYP: //base.gyp:base_java
android_library("base_java") {
srcjar_deps = [
":base_android_java_enums_srcjar",
@@ -2450,7 +2220,6 @@ if (is_android) {
]
deps = [
- "//third_party/android_tools:android_support_annotations_java",
"//third_party/android_tools:android_support_multidex_java",
"//third_party/jsr-305:jsr_305_javalib",
]
@@ -2471,24 +2240,22 @@ if (is_android) {
"android/java/src/org/chromium/base/ContentUriUtils.java",
"android/java/src/org/chromium/base/ContextUtils.java",
"android/java/src/org/chromium/base/CpuFeatures.java",
- "android/java/src/org/chromium/base/EarlyTraceEvent.java",
"android/java/src/org/chromium/base/EventLog.java",
"android/java/src/org/chromium/base/FieldTrialList.java",
"android/java/src/org/chromium/base/FileUtils.java",
"android/java/src/org/chromium/base/ImportantFileWriterAndroid.java",
"android/java/src/org/chromium/base/JNIUtils.java",
- "android/java/src/org/chromium/base/JavaExceptionReporter.java",
"android/java/src/org/chromium/base/JavaHandlerThread.java",
"android/java/src/org/chromium/base/LocaleUtils.java",
"android/java/src/org/chromium/base/Log.java",
"android/java/src/org/chromium/base/MemoryPressureListener.java",
- "android/java/src/org/chromium/base/NonThreadSafe.java",
"android/java/src/org/chromium/base/ObserverList.java",
"android/java/src/org/chromium/base/PackageUtils.java",
"android/java/src/org/chromium/base/PathService.java",
"android/java/src/org/chromium/base/PathUtils.java",
"android/java/src/org/chromium/base/PerfTraceEvent.java",
"android/java/src/org/chromium/base/PowerMonitor.java",
+ "android/java/src/org/chromium/base/PowerStatusReceiver.java",
"android/java/src/org/chromium/base/Promise.java",
"android/java/src/org/chromium/base/ResourceExtractor.java",
"android/java/src/org/chromium/base/SecureRandomInitializer.java",
@@ -2496,10 +2263,7 @@ if (is_android) {
"android/java/src/org/chromium/base/SysUtils.java",
"android/java/src/org/chromium/base/SystemMessageHandler.java",
"android/java/src/org/chromium/base/ThreadUtils.java",
- "android/java/src/org/chromium/base/ThrowUncaughtException.java",
- "android/java/src/org/chromium/base/TimeUtils.java",
"android/java/src/org/chromium/base/TraceEvent.java",
- "android/java/src/org/chromium/base/UnguessableToken.java",
"android/java/src/org/chromium/base/VisibleForTesting.java",
"android/java/src/org/chromium/base/annotations/AccessedByNative.java",
"android/java/src/org/chromium/base/annotations/CalledByNative.java",
@@ -2519,10 +2283,8 @@ if (is_android) {
"android/java/src/org/chromium/base/library_loader/ModernLinker.java",
"android/java/src/org/chromium/base/library_loader/NativeLibraryPreloader.java",
"android/java/src/org/chromium/base/library_loader/ProcessInitException.java",
- "android/java/src/org/chromium/base/metrics/CachedMetrics.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",
"android/java/src/org/chromium/base/multidex/ChromiumMultiDexInstaller.java",
]
@@ -2535,46 +2297,34 @@ if (is_android) {
]
}
+ # GYP: //base.gyp:base_javatests
android_library("base_javatests") {
- testonly = true
deps = [
":base_java",
":base_java_test_support",
- "//third_party/android_support_test_runner:runner_java",
- "//third_party/junit:junit",
]
java_files = [
"android/javatests/src/org/chromium/base/AdvancedMockContextTest.java",
"android/javatests/src/org/chromium/base/ApiCompatibilityUtilsTest.java",
"android/javatests/src/org/chromium/base/CommandLineInitUtilTest.java",
"android/javatests/src/org/chromium/base/CommandLineTest.java",
-
- # TODO(nona): move to Junit once that is built for Android N.
- "android/javatests/src/org/chromium/base/LocaleUtilsTest.java",
"android/javatests/src/org/chromium/base/ObserverListTest.java",
"android/javatests/src/org/chromium/base/metrics/RecordHistogramTest.java",
]
}
+ # GYP: //base.gyp:base_java_test_support
android_library("base_java_test_support") {
- testonly = true
deps = [
":base_java",
"//testing/android/reporter:reporter_java",
- "//third_party/android_support_test_runner:exposed_instrumentation_api_publish_java",
- "//third_party/android_support_test_runner:runner_java",
- "//third_party/hamcrest:hamcrest_core_java",
- "//third_party/junit",
]
java_files = [
"test/android/javatests/src/org/chromium/base/test/BaseActivityInstrumentationTestCase.java",
"test/android/javatests/src/org/chromium/base/test/BaseChromiumInstrumentationTestRunner.java",
"test/android/javatests/src/org/chromium/base/test/BaseInstrumentationTestRunner.java",
- "test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java",
- "test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java",
"test/android/javatests/src/org/chromium/base/test/BaseTestResult.java",
"test/android/javatests/src/org/chromium/base/test/util/AdvancedMockContext.java",
- "test/android/javatests/src/org/chromium/base/test/util/CallbackHelper.java",
"test/android/javatests/src/org/chromium/base/test/util/CommandLineFlags.java",
"test/android/javatests/src/org/chromium/base/test/util/DisableIf.java",
"test/android/javatests/src/org/chromium/base/test/util/DisableIfSkipCheck.java",
@@ -2586,7 +2336,6 @@ if (is_android) {
"test/android/javatests/src/org/chromium/base/test/util/InstrumentationUtils.java",
"test/android/javatests/src/org/chromium/base/test/util/IntegrationTest.java",
"test/android/javatests/src/org/chromium/base/test/util/Manual.java",
- "test/android/javatests/src/org/chromium/base/test/util/Matchers.java",
"test/android/javatests/src/org/chromium/base/test/util/MetricsUtils.java",
"test/android/javatests/src/org/chromium/base/test/util/MinAndroidSdkLevel.java",
"test/android/javatests/src/org/chromium/base/test/util/MinAndroidSdkLevelSkipCheck.java",
@@ -2608,11 +2357,25 @@ if (is_android) {
]
}
+ # TODO(jbudorick): Remove this once we roll to robolectric 3.0 and pull
+ # in the multidex shadow library. crbug.com/522043
+ # GYP: //base.gyp:base_junit_test_support
+ java_library("base_junit_test_support") {
+ testonly = true
+ java_files = [ "test/android/junit/src/org/chromium/base/test/shadows/ShadowMultiDex.java" ]
+ deps = [
+ "//third_party/android_tools:android_support_multidex_java",
+ "//third_party/robolectric:android-all-4.3_r2-robolectric-0",
+ "//third_party/robolectric:robolectric_java",
+ ]
+ srcjar_deps = [ ":base_build_config_gen" ]
+ }
+
+ # GYP: //base.gyp:base_junit_tests
junit_binary("base_junit_tests") {
java_files = [
"android/junit/src/org/chromium/base/BaseChromiumApplicationTest.java",
"android/junit/src/org/chromium/base/LogTest.java",
- "android/junit/src/org/chromium/base/NonThreadSafeTest.java",
"android/junit/src/org/chromium/base/PromiseTest.java",
"test/android/junit/src/org/chromium/base/test/util/DisableIfTest.java",
"test/android/junit/src/org/chromium/base/test/util/MinAndroidSdkLevelSkipCheckTest.java",
@@ -2622,10 +2385,14 @@ if (is_android) {
deps = [
":base_java",
":base_java_test_support",
+ ":base_junit_test_support",
]
- srcjar_deps = [ ":base_build_config_gen" ]
}
+ # GYP: //base.gyp:base_java_application_state
+ # GYP: //base.gyp:base_java_library_load_from_apk_status_codes
+ # GYP: //base.gyp:base_java_library_process_type
+ # GYP: //base.gyp:base_java_memory_pressure_level
java_cpp_enum("base_android_java_enums_srcjar") {
sources = [
"android/application_status_listener.h",
@@ -2635,6 +2402,7 @@ if (is_android) {
]
}
+ # GYP: //base/base.gyp:base_build_config_gen
java_cpp_template("base_build_config_gen") {
sources = [
"android/java/templates/BuildConfig.template",
@@ -2642,11 +2410,12 @@ if (is_android) {
package_name = "org/chromium/base"
defines = []
- if (is_java_debug || dcheck_always_on) {
- defines += [ "_DCHECK_IS_ON" ]
+ if (!is_java_debug) {
+ defines += [ "NDEBUG" ]
}
}
+ # GYP: //base/base.gyp:base_native_libraries_gen
java_cpp_template("base_native_libraries_gen") {
sources = [
"android/java/templates/NativeLibraries.template",
@@ -2654,23 +2423,12 @@ if (is_android) {
package_name = "org/chromium/base/library_loader"
}
+ # GYP: //base.gyp:base_java_unittest_support
android_library("base_java_unittest_support") {
- testonly = true
deps = [
":base_java",
]
- java_files = [
- "test/android/java/src/org/chromium/base/ContentUriTestUtils.java",
- "test/android/java/src/org/chromium/base/TestSystemMessageHandler.java",
- ]
+ java_files =
+ [ "test/android/java/src/org/chromium/base/ContentUriTestUtils.java" ]
}
}
-
-fuzzer_test("base_json_correctness_fuzzer") {
- sources = [
- "json/correctness_fuzzer.cc",
- ]
- deps = [
- ":base",
- ]
-}
diff --git a/base/DEPS b/base/DEPS
index 4b25f3f310..c0e95a00f6 100644
--- a/base/DEPS
+++ b/base/DEPS
@@ -2,7 +2,6 @@ include_rules = [
"+jni",
"+third_party/ashmem",
"+third_party/apple_apsl",
- "+third_party/ced",
"+third_party/lss",
"+third_party/modp_b64",
"+third_party/tcmalloc",
diff --git a/base/allocator/BUILD.gn b/base/allocator/BUILD.gn
index 8cdb06161f..490b8e871b 100644
--- a/base/allocator/BUILD.gn
+++ b/base/allocator/BUILD.gn
@@ -13,10 +13,8 @@ declare_args() {
enable_debugallocation = is_debug
}
-# The Windows-only allocator shim is only enabled for Release static builds, and
-# is mutually exclusive with the generalized shim.
-win_use_allocator_shim = is_win && !is_component_build && !is_debug &&
- !use_experimental_allocator_shim && !is_asan
+# Allocator shim is only enabled for Release static builds.
+win_use_allocator_shim = is_win && !is_component_build && !is_debug
# This "allocator" meta-target will forward to the default allocator according
# to the build settings.
@@ -95,8 +93,6 @@ if (win_use_allocator_shim) {
sources = [
"allocator_shim_win.cc",
"allocator_shim_win.h",
- "winheap_stubs_win.cc",
- "winheap_stubs_win.h",
]
configs += [ ":allocator_shim_define" ]
}
@@ -222,14 +218,6 @@ if (use_allocator == "tcmalloc") {
":tcmalloc_flags",
]
- # Thumb mode disabled due to bug in clang integrated assembler
- # TODO(https://llvm.org/bugs/show_bug.cgi?id=31058)
- configs -= [ "//build/config/compiler:compiler_arm_thumb" ]
- configs += [ "//build/config/compiler:compiler_arm" ]
-
- # TODO(crbug.com/633719) Make tcmalloc work with AFDO if possible.
- configs -= [ "//build/config/compiler:afdo" ]
-
deps = []
if (enable_profiling) {
@@ -288,22 +276,52 @@ if (use_allocator == "tcmalloc") {
buildflag_header("features") {
header = "features.h"
- flags = [
- "USE_EXPERIMENTAL_ALLOCATOR_SHIM=$use_experimental_allocator_shim",
- "ENABLE_WIN_ALLOCATOR_SHIM_TESTS=($use_experimental_allocator_shim || $win_use_allocator_shim)",
- ]
+ flags = [ "USE_EXPERIMENTAL_ALLOCATOR_SHIM=$use_experimental_allocator_shim" ]
}
-# Used to shim malloc symbols on Android. see //base/allocator/README.md.
-config("wrap_malloc_symbols") {
- ldflags = [
- "-Wl,-wrap,calloc",
- "-Wl,-wrap,free",
- "-Wl,-wrap,malloc",
- "-Wl,-wrap,memalign",
- "-Wl,-wrap,posix_memalign",
- "-Wl,-wrap,pvalloc",
- "-Wl,-wrap,realloc",
- "-Wl,-wrap,valloc",
- ]
+if (use_experimental_allocator_shim) {
+ # Used to shim malloc symbols on Android. see //base/allocator/README.md.
+ config("wrap_malloc_symbols") {
+ ldflags = [
+ "-Wl,-wrap,calloc",
+ "-Wl,-wrap,free",
+ "-Wl,-wrap,malloc",
+ "-Wl,-wrap,memalign",
+ "-Wl,-wrap,posix_memalign",
+ "-Wl,-wrap,pvalloc",
+ "-Wl,-wrap,realloc",
+ "-Wl,-wrap,valloc",
+ ]
+ }
+
+ source_set("unified_allocator_shim") {
+ # TODO(primiano): support other platforms, currently this works only on
+ # Linux/CrOS/Android. http://crbug.com/550886 .
+ configs += [ "//base:base_implementation" ] # for BASE_EXPORT
+ visibility = [ "//base:base" ]
+ sources = [
+ "allocator_shim.cc",
+ "allocator_shim.h",
+ "allocator_shim_internals.h",
+ "allocator_shim_override_cpp_symbols.h",
+ "allocator_shim_override_libc_symbols.h",
+ ]
+ if (is_linux && use_allocator == "tcmalloc") {
+ sources += [
+ "allocator_shim_default_dispatch_to_tcmalloc.cc",
+ "allocator_shim_override_glibc_weak_symbols.h",
+ ]
+ deps = [
+ ":tcmalloc",
+ ]
+ } else if (is_linux && use_allocator == "none") {
+ sources += [ "allocator_shim_default_dispatch_to_glibc.cc" ]
+ } else if (is_android && use_allocator == "none") {
+ sources += [
+ "allocator_shim_default_dispatch_to_linker_wrapped_symbols.cc",
+ "allocator_shim_override_linker_wrapped_symbols.h",
+ ]
+ all_dependent_configs = [ ":wrap_malloc_symbols" ]
+ }
+ }
}
diff --git a/base/allocator/README.md b/base/allocator/README.md
index a211732c3f..164df51ae6 100644
--- a/base/allocator/README.md
+++ b/base/allocator/README.md
@@ -189,8 +189,8 @@ Related links
- [Unified allocator shim doc - Feb 2016][url-allocator-shim]
- [Allocator cleanup doc - Jan 2016][url-allocator-cleanup]
- [Proposal to use PartitionAlloc as default allocator](https://crbug.com/339604)
-- [Memory-Infra: Tools to profile memory usage in Chrome](/docs/memory-infra/README.md)
+- [Memory-Infra: Tools to profile memory usage in Chrome](components/tracing/docs/memory_infra.md)
[url-allocator-cleanup]: https://docs.google.com/document/d/1V77Kgp_4tfaaWPEZVxNevoD02wXiatnAv7Ssgr0hmjg/edit?usp=sharing
-[url-memory-infra-heap-profiler]: /docs/memory-infra/heap_profiler.md
+[url-memory-infra-heap-profiler]: components/tracing/docs/heap_profiler.md
[url-allocator-shim]: https://docs.google.com/document/d/1yKlO1AO4XjpDad9rjcBOI15EKdAGsuGO_IeZy0g0kxo/edit?usp=sharing
diff --git a/base/allocator/allocator.gyp b/base/allocator/allocator.gyp
new file mode 100644
index 0000000000..674d4d645f
--- /dev/null
+++ b/base/allocator/allocator.gyp
@@ -0,0 +1,450 @@
+# 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.
+
+{
+ 'target_defaults': {
+ 'variables': {
+ # This code gets run a lot and debugged rarely, so it should be fast
+ # by default. See http://crbug.com/388949.
+ 'debug_optimize': '2',
+ 'win_debug_Optimization': '0',
+ # Run time checks are incompatible with any level of optimizations.
+ 'win_debug_RuntimeChecks': '0',
+ },
+ },
+ 'variables': {
+ 'tcmalloc_dir': '../../third_party/tcmalloc/chromium',
+ 'use_vtable_verify%': 0,
+ # Provide a way to force disable debugallocation in Debug builds
+ # e.g. for profiling (it's more rare to profile Debug builds,
+ # but people sometimes need to do that).
+ 'disable_debugallocation%': 0,
+ },
+ 'targets': [
+ # The only targets that should depend on allocator are 'base' and
+ # executables that don't depend, directly or indirectly, on base (a few).
+ # All the other targets get a transitive dependency on this target via base.
+ {
+ 'target_name': 'allocator',
+ 'variables': {
+ 'conditions': [
+ ['use_allocator!="none" or (OS=="win" and win_use_allocator_shim==1)', {
+ 'allocator_target_type%': 'static_library',
+ }, {
+ 'allocator_target_type%': 'none',
+ }],
+ ],
+ },
+ 'type': '<(allocator_target_type)',
+ 'toolsets': ['host', 'target'],
+ 'conditions': [
+ ['OS=="win" and win_use_allocator_shim==1', {
+ 'msvs_settings': {
+ # TODO(sgk): merge this with build/common.gypi settings
+ 'VCLibrarianTool': {
+ 'AdditionalOptions': ['/ignore:4006,4221'],
+ },
+ 'VCLinkerTool': {
+ 'AdditionalOptions': ['/ignore:4006'],
+ },
+ },
+ 'include_dirs': [
+ '../..',
+ ],
+ 'sources': [
+ 'allocator_shim_win.cc',
+ 'allocator_shim_win.h',
+ ],
+ 'configurations': {
+ 'Debug_Base': {
+ 'msvs_settings': {
+ 'VCCLCompilerTool': {
+ 'RuntimeLibrary': '0',
+ },
+ },
+ },
+ },
+ }], # OS=="win"
+ ['use_allocator=="tcmalloc"', {
+ # Disable the heap checker in tcmalloc.
+ 'defines': [
+ 'NO_HEAP_CHECK',
+ ],
+ 'dependencies': [
+ '../third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ ],
+ # The order of this include_dirs matters, as tc-malloc has its own
+ # base/ mini-fork. Do not factor these out of this conditions section.
+ 'include_dirs': [
+ '.',
+ '<(tcmalloc_dir)/src/base',
+ '<(tcmalloc_dir)/src',
+ '../..',
+ ],
+ 'sources': [
+ # Generated for our configuration from tcmalloc's build
+ # and checked in.
+ '<(tcmalloc_dir)/src/config.h',
+ '<(tcmalloc_dir)/src/config_android.h',
+ '<(tcmalloc_dir)/src/config_linux.h',
+ '<(tcmalloc_dir)/src/config_win.h',
+
+ # all tcmalloc native and forked files
+ '<(tcmalloc_dir)/src/addressmap-inl.h',
+ '<(tcmalloc_dir)/src/base/abort.cc',
+ '<(tcmalloc_dir)/src/base/abort.h',
+ '<(tcmalloc_dir)/src/base/arm_instruction_set_select.h',
+ '<(tcmalloc_dir)/src/base/atomicops-internals-arm-generic.h',
+ '<(tcmalloc_dir)/src/base/atomicops-internals-arm-v6plus.h',
+ '<(tcmalloc_dir)/src/base/atomicops-internals-linuxppc.h',
+ '<(tcmalloc_dir)/src/base/atomicops-internals-macosx.h',
+ '<(tcmalloc_dir)/src/base/atomicops-internals-windows.h',
+ '<(tcmalloc_dir)/src/base/atomicops-internals-x86.cc',
+ '<(tcmalloc_dir)/src/base/atomicops-internals-x86.h',
+ '<(tcmalloc_dir)/src/base/atomicops.h',
+ '<(tcmalloc_dir)/src/base/commandlineflags.h',
+ '<(tcmalloc_dir)/src/base/cycleclock.h',
+ # We don't list dynamic_annotations.c since its copy is already
+ # present in the dynamic_annotations target.
+ '<(tcmalloc_dir)/src/base/dynamic_annotations.h',
+ '<(tcmalloc_dir)/src/base/elf_mem_image.cc',
+ '<(tcmalloc_dir)/src/base/elf_mem_image.h',
+ '<(tcmalloc_dir)/src/base/elfcore.h',
+ '<(tcmalloc_dir)/src/base/googleinit.h',
+ '<(tcmalloc_dir)/src/base/linux_syscall_support.h',
+ '<(tcmalloc_dir)/src/base/linuxthreads.cc',
+ '<(tcmalloc_dir)/src/base/linuxthreads.h',
+ '<(tcmalloc_dir)/src/base/logging.cc',
+ '<(tcmalloc_dir)/src/base/logging.h',
+ '<(tcmalloc_dir)/src/base/low_level_alloc.cc',
+ '<(tcmalloc_dir)/src/base/low_level_alloc.h',
+ '<(tcmalloc_dir)/src/base/simple_mutex.h',
+ '<(tcmalloc_dir)/src/base/spinlock.cc',
+ '<(tcmalloc_dir)/src/base/spinlock.h',
+ '<(tcmalloc_dir)/src/base/spinlock_internal.cc',
+ '<(tcmalloc_dir)/src/base/spinlock_internal.h',
+ '<(tcmalloc_dir)/src/base/spinlock_linux-inl.h',
+ '<(tcmalloc_dir)/src/base/spinlock_posix-inl.h',
+ '<(tcmalloc_dir)/src/base/spinlock_win32-inl.h',
+ '<(tcmalloc_dir)/src/base/stl_allocator.h',
+ '<(tcmalloc_dir)/src/base/synchronization_profiling.h',
+ '<(tcmalloc_dir)/src/base/sysinfo.cc',
+ '<(tcmalloc_dir)/src/base/sysinfo.h',
+ '<(tcmalloc_dir)/src/base/thread_annotations.h',
+ '<(tcmalloc_dir)/src/base/thread_lister.c',
+ '<(tcmalloc_dir)/src/base/thread_lister.h',
+ '<(tcmalloc_dir)/src/base/vdso_support.cc',
+ '<(tcmalloc_dir)/src/base/vdso_support.h',
+ '<(tcmalloc_dir)/src/central_freelist.cc',
+ '<(tcmalloc_dir)/src/central_freelist.h',
+ '<(tcmalloc_dir)/src/common.cc',
+ '<(tcmalloc_dir)/src/common.h',
+ '<(tcmalloc_dir)/src/debugallocation.cc',
+ '<(tcmalloc_dir)/src/free_list.cc',
+ '<(tcmalloc_dir)/src/free_list.h',
+ '<(tcmalloc_dir)/src/getpc.h',
+ '<(tcmalloc_dir)/src/gperftools/heap-checker.h',
+ '<(tcmalloc_dir)/src/gperftools/heap-profiler.h',
+ '<(tcmalloc_dir)/src/gperftools/malloc_extension.h',
+ '<(tcmalloc_dir)/src/gperftools/malloc_extension_c.h',
+ '<(tcmalloc_dir)/src/gperftools/malloc_hook.h',
+ '<(tcmalloc_dir)/src/gperftools/malloc_hook_c.h',
+ '<(tcmalloc_dir)/src/gperftools/profiler.h',
+ '<(tcmalloc_dir)/src/gperftools/stacktrace.h',
+ '<(tcmalloc_dir)/src/gperftools/tcmalloc.h',
+ '<(tcmalloc_dir)/src/heap-checker-bcad.cc',
+ '<(tcmalloc_dir)/src/heap-checker.cc',
+ '<(tcmalloc_dir)/src/heap-profile-table.cc',
+ '<(tcmalloc_dir)/src/heap-profile-table.h',
+ '<(tcmalloc_dir)/src/heap-profiler.cc',
+ '<(tcmalloc_dir)/src/internal_logging.cc',
+ '<(tcmalloc_dir)/src/internal_logging.h',
+ '<(tcmalloc_dir)/src/libc_override.h',
+ '<(tcmalloc_dir)/src/libc_override_gcc_and_weak.h',
+ '<(tcmalloc_dir)/src/libc_override_glibc.h',
+ '<(tcmalloc_dir)/src/libc_override_osx.h',
+ '<(tcmalloc_dir)/src/libc_override_redefine.h',
+ '<(tcmalloc_dir)/src/linked_list.h',
+ '<(tcmalloc_dir)/src/malloc_extension.cc',
+ '<(tcmalloc_dir)/src/malloc_hook-inl.h',
+ '<(tcmalloc_dir)/src/malloc_hook.cc',
+ '<(tcmalloc_dir)/src/malloc_hook_mmap_freebsd.h',
+ '<(tcmalloc_dir)/src/malloc_hook_mmap_linux.h',
+ '<(tcmalloc_dir)/src/maybe_threads.cc',
+ '<(tcmalloc_dir)/src/maybe_threads.h',
+ '<(tcmalloc_dir)/src/memfs_malloc.cc',
+ '<(tcmalloc_dir)/src/memory_region_map.cc',
+ '<(tcmalloc_dir)/src/memory_region_map.h',
+ '<(tcmalloc_dir)/src/packed-cache-inl.h',
+ '<(tcmalloc_dir)/src/page_heap.cc',
+ '<(tcmalloc_dir)/src/page_heap.h',
+ '<(tcmalloc_dir)/src/page_heap_allocator.h',
+ '<(tcmalloc_dir)/src/pagemap.h',
+ '<(tcmalloc_dir)/src/profile-handler.cc',
+ '<(tcmalloc_dir)/src/profile-handler.h',
+ '<(tcmalloc_dir)/src/profiledata.cc',
+ '<(tcmalloc_dir)/src/profiledata.h',
+ '<(tcmalloc_dir)/src/profiler.cc',
+ '<(tcmalloc_dir)/src/raw_printer.cc',
+ '<(tcmalloc_dir)/src/raw_printer.h',
+ '<(tcmalloc_dir)/src/sampler.cc',
+ '<(tcmalloc_dir)/src/sampler.h',
+ '<(tcmalloc_dir)/src/span.cc',
+ '<(tcmalloc_dir)/src/span.h',
+ '<(tcmalloc_dir)/src/stack_trace_table.cc',
+ '<(tcmalloc_dir)/src/stack_trace_table.h',
+ '<(tcmalloc_dir)/src/stacktrace.cc',
+ '<(tcmalloc_dir)/src/stacktrace_arm-inl.h',
+ '<(tcmalloc_dir)/src/stacktrace_config.h',
+ '<(tcmalloc_dir)/src/stacktrace_generic-inl.h',
+ '<(tcmalloc_dir)/src/stacktrace_libunwind-inl.h',
+ '<(tcmalloc_dir)/src/stacktrace_powerpc-inl.h',
+ '<(tcmalloc_dir)/src/stacktrace_win32-inl.h',
+ '<(tcmalloc_dir)/src/stacktrace_with_context.cc',
+ '<(tcmalloc_dir)/src/stacktrace_x86-inl.h',
+ '<(tcmalloc_dir)/src/static_vars.cc',
+ '<(tcmalloc_dir)/src/static_vars.h',
+ '<(tcmalloc_dir)/src/symbolize.cc',
+ '<(tcmalloc_dir)/src/symbolize.h',
+ '<(tcmalloc_dir)/src/system-alloc.cc',
+ '<(tcmalloc_dir)/src/system-alloc.h',
+ '<(tcmalloc_dir)/src/tcmalloc.cc',
+ '<(tcmalloc_dir)/src/tcmalloc_guard.h',
+ '<(tcmalloc_dir)/src/thread_cache.cc',
+ '<(tcmalloc_dir)/src/thread_cache.h',
+
+ 'debugallocation_shim.cc',
+ ],
+ # sources! means that these are not compiled directly.
+ 'sources!': [
+ # We simply don't use these, but list them above so that IDE
+ # users can view the full available source for reference, etc.
+ '<(tcmalloc_dir)/src/addressmap-inl.h',
+ '<(tcmalloc_dir)/src/base/atomicops-internals-linuxppc.h',
+ '<(tcmalloc_dir)/src/base/atomicops-internals-macosx.h',
+ '<(tcmalloc_dir)/src/base/atomicops-internals-x86-msvc.h',
+ '<(tcmalloc_dir)/src/base/atomicops-internals-x86.h',
+ '<(tcmalloc_dir)/src/base/atomicops.h',
+ '<(tcmalloc_dir)/src/base/commandlineflags.h',
+ '<(tcmalloc_dir)/src/base/cycleclock.h',
+ '<(tcmalloc_dir)/src/base/elf_mem_image.h',
+ '<(tcmalloc_dir)/src/base/elfcore.h',
+ '<(tcmalloc_dir)/src/base/googleinit.h',
+ '<(tcmalloc_dir)/src/base/linux_syscall_support.h',
+ '<(tcmalloc_dir)/src/base/simple_mutex.h',
+ '<(tcmalloc_dir)/src/base/spinlock_linux-inl.h',
+ '<(tcmalloc_dir)/src/base/spinlock_posix-inl.h',
+ '<(tcmalloc_dir)/src/base/spinlock_win32-inl.h',
+ '<(tcmalloc_dir)/src/base/stl_allocator.h',
+ '<(tcmalloc_dir)/src/base/thread_annotations.h',
+ '<(tcmalloc_dir)/src/getpc.h',
+ '<(tcmalloc_dir)/src/gperftools/heap-checker.h',
+ '<(tcmalloc_dir)/src/gperftools/heap-profiler.h',
+ '<(tcmalloc_dir)/src/gperftools/malloc_extension.h',
+ '<(tcmalloc_dir)/src/gperftools/malloc_extension_c.h',
+ '<(tcmalloc_dir)/src/gperftools/malloc_hook.h',
+ '<(tcmalloc_dir)/src/gperftools/malloc_hook_c.h',
+ '<(tcmalloc_dir)/src/gperftools/profiler.h',
+ '<(tcmalloc_dir)/src/gperftools/stacktrace.h',
+ '<(tcmalloc_dir)/src/gperftools/tcmalloc.h',
+ '<(tcmalloc_dir)/src/heap-checker-bcad.cc',
+ '<(tcmalloc_dir)/src/heap-checker.cc',
+ '<(tcmalloc_dir)/src/libc_override.h',
+ '<(tcmalloc_dir)/src/libc_override_gcc_and_weak.h',
+ '<(tcmalloc_dir)/src/libc_override_glibc.h',
+ '<(tcmalloc_dir)/src/libc_override_osx.h',
+ '<(tcmalloc_dir)/src/libc_override_redefine.h',
+ '<(tcmalloc_dir)/src/malloc_hook_mmap_freebsd.h',
+ '<(tcmalloc_dir)/src/malloc_hook_mmap_linux.h',
+ '<(tcmalloc_dir)/src/memfs_malloc.cc',
+ '<(tcmalloc_dir)/src/packed-cache-inl.h',
+ '<(tcmalloc_dir)/src/page_heap_allocator.h',
+ '<(tcmalloc_dir)/src/pagemap.h',
+ '<(tcmalloc_dir)/src/stacktrace_arm-inl.h',
+ '<(tcmalloc_dir)/src/stacktrace_config.h',
+ '<(tcmalloc_dir)/src/stacktrace_generic-inl.h',
+ '<(tcmalloc_dir)/src/stacktrace_libunwind-inl.h',
+ '<(tcmalloc_dir)/src/stacktrace_powerpc-inl.h',
+ '<(tcmalloc_dir)/src/stacktrace_win32-inl.h',
+ '<(tcmalloc_dir)/src/stacktrace_with_context.cc',
+ '<(tcmalloc_dir)/src/stacktrace_x86-inl.h',
+ '<(tcmalloc_dir)/src/tcmalloc_guard.h',
+
+ # Included by debugallocation_shim.cc.
+ '<(tcmalloc_dir)/src/debugallocation.cc',
+ '<(tcmalloc_dir)/src/tcmalloc.cc',
+ ],
+ 'variables': {
+ 'clang_warning_flags': [
+ # tcmalloc initializes some fields in the wrong order.
+ '-Wno-reorder',
+ # tcmalloc contains some unused local template specializations.
+ '-Wno-unused-function',
+ # tcmalloc uses COMPILE_ASSERT without static_assert but with
+ # typedefs.
+ '-Wno-unused-local-typedefs',
+ # for magic2_ in debugallocation.cc (only built in Debug builds)
+ # typedefs.
+ '-Wno-unused-private-field',
+ ],
+ },
+ 'conditions': [
+ ['OS=="linux" or OS=="freebsd" or OS=="solaris" or OS=="android"', {
+ 'sources!': [
+ '<(tcmalloc_dir)/src/system-alloc.h',
+ ],
+ # We enable all warnings by default, but upstream disables a few.
+ # Keep "-Wno-*" flags in sync with upstream by comparing against:
+ # http://code.google.com/p/google-perftools/source/browse/trunk/Makefile.am
+ 'cflags': [
+ '-Wno-sign-compare',
+ '-Wno-unused-result',
+ ],
+ 'link_settings': {
+ 'ldflags': [
+ # Don't let linker rip this symbol out, otherwise the heap&cpu
+ # profilers will not initialize properly on startup.
+ '-Wl,-uIsHeapProfilerRunning,-uProfilerStart',
+ # Do the same for heap leak checker.
+ '-Wl,-u_Z21InitialMallocHook_NewPKvj,-u_Z22InitialMallocHook_MMapPKvS0_jiiix,-u_Z22InitialMallocHook_SbrkPKvi',
+ '-Wl,-u_Z21InitialMallocHook_NewPKvm,-u_Z22InitialMallocHook_MMapPKvS0_miiil,-u_Z22InitialMallocHook_SbrkPKvl',
+ '-Wl,-u_ZN15HeapLeakChecker12IgnoreObjectEPKv,-u_ZN15HeapLeakChecker14UnIgnoreObjectEPKv',
+ ],
+ },
+ # Compiling tcmalloc with -fvisibility=default is only necessary when
+ # not using the allocator shim, which provides the correct visibility
+ # annotations for those symbols which need to be exported (see
+ # //base/allocator/allocator_shim_override_glibc_weak_symbols.h and
+ # //base/allocator/allocator_shim_internals.h for the definition of
+ # SHIM_ALWAYS_EXPORT).
+ 'conditions': [
+ ['use_experimental_allocator_shim==0', {
+ 'cflags!': [
+ '-fvisibility=hidden',
+ ],
+ }],
+ ],
+ }],
+ ['profiling!=1', {
+ 'sources!': [
+ # cpuprofiler
+ '<(tcmalloc_dir)/src/base/thread_lister.c',
+ '<(tcmalloc_dir)/src/base/thread_lister.h',
+ '<(tcmalloc_dir)/src/profile-handler.cc',
+ '<(tcmalloc_dir)/src/profile-handler.h',
+ '<(tcmalloc_dir)/src/profiledata.cc',
+ '<(tcmalloc_dir)/src/profiledata.h',
+ '<(tcmalloc_dir)/src/profiler.cc',
+ ],
+ }],
+ ['use_experimental_allocator_shim==1', {
+ 'defines': [
+ 'TCMALLOC_DONT_REPLACE_SYSTEM_ALLOC',
+ ],
+ }]
+ ],
+ 'configurations': {
+ 'Debug_Base': {
+ 'conditions': [
+ ['disable_debugallocation==0', {
+ 'defines': [
+ # Use debugallocation for Debug builds to catch problems
+ # early and cleanly, http://crbug.com/30715 .
+ 'TCMALLOC_FOR_DEBUGALLOCATION',
+ ],
+ }],
+ ],
+ },
+ },
+ }], # use_allocator=="tcmalloc
+ # For CrOS builds with vtable verification. According to the author of
+ # crrev.com/10854031 this is used in conjuction with some other CrOS
+ # build flag, to enable verification of any allocator that uses virtual
+ # function calls.
+ ['use_vtable_verify==1', {
+ 'cflags': [
+ '-fvtable-verify=preinit',
+ ],
+ }],
+ ['order_profiling != 0', {
+ 'target_conditions' : [
+ ['_toolset=="target"', {
+ 'cflags!': [ '-finstrument-functions' ],
+ }],
+ ],
+ }],
+ ], # conditions of 'allocator' target.
+ }, # 'allocator' target.
+ {
+ # GN: //base/allocator:features
+ # When referenced from a target that might be compiled in the host
+ # toolchain, always refer to 'allocator_features#target'.
+ 'target_name': 'allocator_features',
+ 'includes': [ '../../build/buildflag_header.gypi' ],
+ 'variables': {
+ 'buildflag_header_path': 'base/allocator/features.h',
+ 'buildflag_flags': [
+ 'USE_EXPERIMENTAL_ALLOCATOR_SHIM=<(use_experimental_allocator_shim)',
+ ],
+ },
+ }, # 'allocator_features' target.
+ ], # targets.
+ 'conditions': [
+ ['use_experimental_allocator_shim==1', {
+ 'targets': [
+ {
+ # GN: //base/allocator:unified_allocator_shim
+ 'target_name': 'unified_allocator_shim',
+ 'toolsets': ['host', 'target'],
+ 'type': 'static_library',
+ 'defines': [ 'BASE_IMPLEMENTATION' ],
+ 'sources': [
+ 'allocator_shim.cc',
+ 'allocator_shim.h',
+ 'allocator_shim_internals.h',
+ 'allocator_shim_override_cpp_symbols.h',
+ 'allocator_shim_override_libc_symbols.h',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ 'conditions': [
+ ['OS=="linux" and use_allocator=="tcmalloc"', {
+ 'sources': [
+ 'allocator_shim_default_dispatch_to_tcmalloc.cc',
+ 'allocator_shim_override_glibc_weak_symbols.h',
+ ],
+ }],
+ ['use_allocator=="none" and (OS=="linux" or (OS=="android" and _toolset == "host" and host_os == "linux"))', {
+ 'sources': [
+ 'allocator_shim_default_dispatch_to_glibc.cc',
+ ],
+ }],
+ ['OS=="android" and _toolset == "target"', {
+ 'sources': [
+ 'allocator_shim_default_dispatch_to_linker_wrapped_symbols.cc',
+ 'allocator_shim_override_linker_wrapped_symbols.h',
+ ],
+ # On Android all references to malloc & friends symbols are
+ # rewritten, at link time, and routed to the shim.
+ # See //base/allocator/README.md.
+ 'all_dependent_settings': {
+ 'ldflags': [
+ '-Wl,-wrap,calloc',
+ '-Wl,-wrap,free',
+ '-Wl,-wrap,malloc',
+ '-Wl,-wrap,memalign',
+ '-Wl,-wrap,posix_memalign',
+ '-Wl,-wrap,pvalloc',
+ '-Wl,-wrap,realloc',
+ '-Wl,-wrap,valloc',
+ ],
+ },
+ }],
+ ]
+ }, # 'unified_allocator_shim' target.
+ ],
+ }]
+ ],
+}
diff --git a/base/allocator/allocator_shim.cc b/base/allocator/allocator_shim.cc
index fbdbdfc8c2..09ed45fde3 100644
--- a/base/allocator/allocator_shim.cc
+++ b/base/allocator/allocator_shim.cc
@@ -5,26 +5,16 @@
#include "base/allocator/allocator_shim.h"
#include <errno.h>
+#include <unistd.h>
#include <new>
#include "base/atomicops.h"
#include "base/logging.h"
#include "base/macros.h"
-#include "base/process/process_metrics.h"
#include "base/threading/platform_thread.h"
#include "build/build_config.h"
-#if !defined(OS_WIN)
-#include <unistd.h>
-#else
-#include "base/allocator/winheap_stubs_win.h"
-#endif
-
-#if defined(OS_MACOSX)
-#include <malloc/malloc.h>
-#endif
-
// No calls to malloc / new in this file. They would would cause re-entrancy of
// the shim, which is hard to deal with. Keep this code as simple as possible
// and don't use any external C++ object here, not even //base ones. Even if
@@ -38,25 +28,30 @@ subtle::AtomicWord g_chain_head = reinterpret_cast<subtle::AtomicWord>(
&allocator::AllocatorDispatch::default_dispatch);
bool g_call_new_handler_on_malloc_failure = false;
-
-#if !defined(OS_WIN)
subtle::Atomic32 g_new_handler_lock = 0;
-#endif
-inline size_t GetCachedPageSize() {
+// In theory this should be just base::ThreadChecker. But we can't afford
+// the luxury of a LazyInstance<ThreadChecker> here as it would cause a new().
+bool CalledOnValidThread() {
+ using subtle::Atomic32;
+ const Atomic32 kInvalidTID = static_cast<Atomic32>(kInvalidThreadId);
+ static Atomic32 g_tid = kInvalidTID;
+ Atomic32 cur_tid = static_cast<Atomic32>(PlatformThread::CurrentId());
+ Atomic32 prev_tid =
+ subtle::NoBarrier_CompareAndSwap(&g_tid, kInvalidTID, cur_tid);
+ return prev_tid == kInvalidTID || prev_tid == cur_tid;
+}
+
+inline size_t GetPageSize() {
static size_t pagesize = 0;
if (!pagesize)
- pagesize = base::GetPageSize();
+ pagesize = sysconf(_SC_PAGESIZE);
return pagesize;
}
// Calls the std::new handler thread-safely. Returns true if a new_handler was
// set and called, false if no new_handler was set.
-bool CallNewHandler(size_t size) {
-#if defined(OS_WIN)
- return base::allocator::WinCallNewHandler(size);
-#else
- ALLOW_UNUSED_PARAM(size);
+bool CallNewHandler() {
// TODO(primiano): C++11 has introduced ::get_new_handler() which is supposed
// to be thread safe and would avoid the spinlock boilerplate here. However
// it doesn't seem to be available yet in the Linux chroot headers yet.
@@ -74,7 +69,6 @@ bool CallNewHandler(size_t size) {
// Assume the new_handler will abort if it fails. Exception are disabled and
// we don't support the case of a new_handler throwing std::bad_balloc.
return true;
-#endif
}
inline const allocator::AllocatorDispatch* GetChainHead() {
@@ -101,34 +95,14 @@ void SetCallNewHandlerOnMallocFailure(bool value) {
void* UncheckedAlloc(size_t size) {
const allocator::AllocatorDispatch* const chain_head = GetChainHead();
- return chain_head->alloc_function(chain_head, size, nullptr);
+ return chain_head->alloc_function(chain_head, size);
}
void InsertAllocatorDispatch(AllocatorDispatch* dispatch) {
- // Loop in case of (an unlikely) race on setting the list head.
- size_t kMaxRetries = 7;
- for (size_t i = 0; i < kMaxRetries; ++i) {
- const AllocatorDispatch* chain_head = GetChainHead();
- dispatch->next = chain_head;
-
- // This function guarantees to be thread-safe w.r.t. concurrent
- // insertions. It also has to guarantee that all the threads always
- // see a consistent chain, hence the MemoryBarrier() below.
- // InsertAllocatorDispatch() is NOT a fastpath, as opposite to malloc(), so
- // we don't really want this to be a release-store with a corresponding
- // acquire-load during malloc().
- subtle::MemoryBarrier();
- subtle::AtomicWord old_value =
- reinterpret_cast<subtle::AtomicWord>(chain_head);
- // Set the chain head to the new dispatch atomically. If we lose the race,
- // the comparison will fail, and the new head of chain will be returned.
- if (subtle::NoBarrier_CompareAndSwap(
- &g_chain_head, old_value,
- reinterpret_cast<subtle::AtomicWord>(dispatch)) == old_value) {
- // Success.
- return;
- }
- }
+ // Ensure this is always called on the same thread.
+ DCHECK(CalledOnValidThread());
+
+ dispatch->next = GetChainHead();
// This function does not guarantee to be thread-safe w.r.t. concurrent
// insertions, but still has to guarantee that all the threads always
@@ -143,6 +117,7 @@ void InsertAllocatorDispatch(AllocatorDispatch* dispatch) {
}
void RemoveAllocatorDispatchForTesting(AllocatorDispatch* dispatch) {
+ DCHECK(CalledOnValidThread());
DCHECK_EQ(GetChainHead(), dispatch);
subtle::NoBarrier_Store(&g_chain_head,
reinterpret_cast<subtle::AtomicWord>(dispatch->next));
@@ -152,10 +127,8 @@ void RemoveAllocatorDispatchForTesting(AllocatorDispatch* dispatch) {
} // namespace base
// The Shim* functions below are the entry-points into the shim-layer and
-// are supposed to be invoked by the allocator_shim_override_*
+// are supposed to be invoked / aliased by the allocator_shim_override_*
// headers to route the malloc / new symbols through the shim layer.
-// They are defined as ALWAYS_INLINE in order to remove a level of indirection
-// between the system-defined entry points and the shim implementations.
extern "C" {
// The general pattern for allocations is:
@@ -170,155 +143,102 @@ extern "C" {
// just suicide priting a message).
// - Assume it did succeed if it returns, in which case reattempt the alloc.
-ALWAYS_INLINE void* ShimCppNew(size_t size) {
+void* ShimCppNew(size_t size) {
const allocator::AllocatorDispatch* const chain_head = GetChainHead();
void* ptr;
do {
- void* context = nullptr;
-#if defined(OS_MACOSX)
- context = malloc_default_zone();
-#endif
- ptr = chain_head->alloc_function(chain_head, size, context);
- } while (!ptr && CallNewHandler(size));
+ ptr = chain_head->alloc_function(chain_head, size);
+ } while (!ptr && CallNewHandler());
return ptr;
}
-ALWAYS_INLINE void ShimCppDelete(void* address) {
- void* context = nullptr;
-#if defined(OS_MACOSX)
- context = malloc_default_zone();
-#endif
+void ShimCppDelete(void* address) {
const allocator::AllocatorDispatch* const chain_head = GetChainHead();
- return chain_head->free_function(chain_head, address, context);
+ return chain_head->free_function(chain_head, address);
}
-ALWAYS_INLINE void* ShimMalloc(size_t size, void* context) {
+void* ShimMalloc(size_t size) {
const allocator::AllocatorDispatch* const chain_head = GetChainHead();
void* ptr;
do {
- ptr = chain_head->alloc_function(chain_head, size, context);
- } while (!ptr && g_call_new_handler_on_malloc_failure &&
- CallNewHandler(size));
+ ptr = chain_head->alloc_function(chain_head, size);
+ } while (!ptr && g_call_new_handler_on_malloc_failure && CallNewHandler());
return ptr;
}
-ALWAYS_INLINE void* ShimCalloc(size_t n, size_t size, void* context) {
+void* ShimCalloc(size_t n, size_t size) {
const allocator::AllocatorDispatch* const chain_head = GetChainHead();
void* ptr;
do {
- ptr = chain_head->alloc_zero_initialized_function(chain_head, n, size,
- context);
- } while (!ptr && g_call_new_handler_on_malloc_failure &&
- CallNewHandler(size));
+ ptr = chain_head->alloc_zero_initialized_function(chain_head, n, size);
+ } while (!ptr && g_call_new_handler_on_malloc_failure && CallNewHandler());
return ptr;
}
-ALWAYS_INLINE void* ShimRealloc(void* address, size_t size, void* context) {
+void* ShimRealloc(void* address, size_t size) {
// realloc(size == 0) means free() and might return a nullptr. We should
// not call the std::new_handler in that case, though.
const allocator::AllocatorDispatch* const chain_head = GetChainHead();
void* ptr;
do {
- ptr = chain_head->realloc_function(chain_head, address, size, context);
+ ptr = chain_head->realloc_function(chain_head, address, size);
} while (!ptr && size && g_call_new_handler_on_malloc_failure &&
- CallNewHandler(size));
+ CallNewHandler());
return ptr;
}
-ALWAYS_INLINE void* ShimMemalign(size_t alignment, size_t size, void* context) {
+void* ShimMemalign(size_t alignment, size_t size) {
const allocator::AllocatorDispatch* const chain_head = GetChainHead();
void* ptr;
do {
- ptr = chain_head->alloc_aligned_function(chain_head, alignment, size,
- context);
- } while (!ptr && g_call_new_handler_on_malloc_failure &&
- CallNewHandler(size));
+ ptr = chain_head->alloc_aligned_function(chain_head, alignment, size);
+ } while (!ptr && g_call_new_handler_on_malloc_failure && CallNewHandler());
return ptr;
}
-ALWAYS_INLINE int ShimPosixMemalign(void** res, size_t alignment, size_t size) {
+int ShimPosixMemalign(void** res, size_t alignment, size_t size) {
// posix_memalign is supposed to check the arguments. See tc_posix_memalign()
// in tc_malloc.cc.
if (((alignment % sizeof(void*)) != 0) ||
((alignment & (alignment - 1)) != 0) || (alignment == 0)) {
return EINVAL;
}
- void* ptr = ShimMemalign(alignment, size, nullptr);
+ void* ptr = ShimMemalign(alignment, size);
*res = ptr;
return ptr ? 0 : ENOMEM;
}
-ALWAYS_INLINE void* ShimValloc(size_t size, void* context) {
- return ShimMemalign(GetCachedPageSize(), size, context);
+void* ShimValloc(size_t size) {
+ return ShimMemalign(GetPageSize(), size);
}
-ALWAYS_INLINE void* ShimPvalloc(size_t size) {
+void* ShimPvalloc(size_t size) {
// pvalloc(0) should allocate one page, according to its man page.
if (size == 0) {
- size = GetCachedPageSize();
+ size = GetPageSize();
} else {
- size = (size + GetCachedPageSize() - 1) & ~(GetCachedPageSize() - 1);
+ size = (size + GetPageSize() - 1) & ~(GetPageSize() - 1);
}
- // The third argument is nullptr because pvalloc is glibc only and does not
- // exist on OSX/BSD systems.
- return ShimMemalign(GetCachedPageSize(), size, nullptr);
+ return ShimMemalign(GetPageSize(), size);
}
-ALWAYS_INLINE void ShimFree(void* address, void* context) {
+void ShimFree(void* address) {
const allocator::AllocatorDispatch* const chain_head = GetChainHead();
- return chain_head->free_function(chain_head, address, context);
-}
-
-ALWAYS_INLINE size_t ShimGetSizeEstimate(const void* address, void* context) {
- const allocator::AllocatorDispatch* const chain_head = GetChainHead();
- return chain_head->get_size_estimate_function(
- chain_head, const_cast<void*>(address), context);
-}
-
-ALWAYS_INLINE unsigned ShimBatchMalloc(size_t size,
- void** results,
- unsigned num_requested,
- void* context) {
- const allocator::AllocatorDispatch* const chain_head = GetChainHead();
- return chain_head->batch_malloc_function(chain_head, size, results,
- num_requested, context);
-}
-
-ALWAYS_INLINE void ShimBatchFree(void** to_be_freed,
- unsigned num_to_be_freed,
- void* context) {
- const allocator::AllocatorDispatch* const chain_head = GetChainHead();
- return chain_head->batch_free_function(chain_head, to_be_freed,
- num_to_be_freed, context);
-}
-
-ALWAYS_INLINE void ShimFreeDefiniteSize(void* ptr, size_t size, void* context) {
- const allocator::AllocatorDispatch* const chain_head = GetChainHead();
- return chain_head->free_definite_size_function(chain_head, ptr, size,
- context);
+ return chain_head->free_function(chain_head, address);
}
} // extern "C"
-#if !defined(OS_WIN) && !defined(OS_MACOSX)
-// Cpp symbols (new / delete) should always be routed through the shim layer
-// except on Windows and macOS where the malloc intercept is deep enough that it
-// also catches the cpp calls.
+// Cpp symbols (new / delete) should always be routed through the shim layer.
#include "base/allocator/allocator_shim_override_cpp_symbols.h"
-#endif
-#if defined(OS_ANDROID) || defined(ANDROID)
// Android does not support symbol interposition. The way malloc symbols are
// intercepted on Android is by using link-time -wrap flags.
-#include "base/allocator/allocator_shim_override_linker_wrapped_symbols.h"
-#elif defined(OS_WIN)
-// On Windows we use plain link-time overriding of the CRT symbols.
-#include "base/allocator/allocator_shim_override_ucrt_symbols_win.h"
-#elif defined(OS_MACOSX)
-#include "base/allocator/allocator_shim_default_dispatch_to_mac_zoned_malloc.h"
-#include "base/allocator/allocator_shim_override_mac_symbols.h"
-#else
+#if !defined(OS_ANDROID) && !defined(ANDROID)
+// Ditto for plain malloc() / calloc() / free() etc. symbols.
#include "base/allocator/allocator_shim_override_libc_symbols.h"
+#else
+#include "base/allocator/allocator_shim_override_linker_wrapped_symbols.h"
#endif
// In the case of tcmalloc we also want to plumb into the glibc hooks
@@ -328,22 +248,6 @@ ALWAYS_INLINE void ShimFreeDefiniteSize(void* ptr, size_t size, void* context) {
#include "base/allocator/allocator_shim_override_glibc_weak_symbols.h"
#endif
-#if defined(OS_MACOSX)
-namespace base {
-namespace allocator {
-void InitializeAllocatorShim() {
- // Prepares the default dispatch. After the intercepted malloc calls have
- // traversed the shim this will route them to the default malloc zone.
- InitializeDefaultDispatchToMacAllocator();
-
- // This replaces the default malloc zone, causing calls to malloc & friends
- // from the codebase to be routed to ShimMalloc() above.
- OverrideMacSymbols();
-}
-} // namespace allocator
-} // namespace base
-#endif
-
// Cross-checks.
#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
diff --git a/base/allocator/allocator_shim.h b/base/allocator/allocator_shim.h
index 65ac1eb7de..f1a1e3d7ce 100644
--- a/base/allocator/allocator_shim.h
+++ b/base/allocator/allocator_shim.h
@@ -8,7 +8,6 @@
#include <stddef.h>
#include "base/base_export.h"
-#include "build/build_config.h"
namespace base {
namespace allocator {
@@ -46,54 +45,23 @@ namespace allocator {
// wihout introducing unnecessary perf hits.
struct AllocatorDispatch {
- using AllocFn = void*(const AllocatorDispatch* self,
- size_t size,
- void* context);
+ using AllocFn = void*(const AllocatorDispatch* self, size_t size);
using AllocZeroInitializedFn = void*(const AllocatorDispatch* self,
size_t n,
- size_t size,
- void* context);
+ size_t size);
using AllocAlignedFn = void*(const AllocatorDispatch* self,
size_t alignment,
- size_t size,
- void* context);
+ size_t size);
using ReallocFn = void*(const AllocatorDispatch* self,
void* address,
- size_t size,
- void* context);
- using FreeFn = void(const AllocatorDispatch* self,
- void* address,
- void* context);
- // Returns the best available estimate for the actual amount of memory
- // consumed by the allocation |address|. If possible, this should include
- // heap overhead or at least a decent estimate of the full cost of the
- // allocation. If no good estimate is possible, returns zero.
- using GetSizeEstimateFn = size_t(const AllocatorDispatch* self,
- void* address,
- void* context);
- using BatchMallocFn = unsigned(const AllocatorDispatch* self,
- size_t size,
- void** results,
- unsigned num_requested,
- void* context);
- using BatchFreeFn = void(const AllocatorDispatch* self,
- void** to_be_freed,
- unsigned num_to_be_freed,
- void* context);
- using FreeDefiniteSizeFn = void(const AllocatorDispatch* self,
- void* ptr,
- size_t size,
- void* context);
+ size_t size);
+ using FreeFn = void(const AllocatorDispatch* self, void* address);
AllocFn* const alloc_function;
AllocZeroInitializedFn* const alloc_zero_initialized_function;
AllocAlignedFn* const alloc_aligned_function;
ReallocFn* const realloc_function;
FreeFn* const free_function;
- GetSizeEstimateFn* const get_size_estimate_function;
- BatchMallocFn* const batch_malloc_function;
- BatchFreeFn* const batch_free_function;
- FreeDefiniteSizeFn* const free_definite_size_function;
const AllocatorDispatch* next;
@@ -111,10 +79,10 @@ BASE_EXPORT void SetCallNewHandlerOnMallocFailure(bool value);
// regardless of SetCallNewHandlerOnMallocFailure().
BASE_EXPORT void* UncheckedAlloc(size_t size);
-// Inserts |dispatch| in front of the allocator chain. This method is
+// Inserts |dispatch| in front of the allocator chain. This method is NOT
// thread-safe w.r.t concurrent invocations of InsertAllocatorDispatch().
-// The callers have responsibility for inserting a single dispatch no more
-// than once.
+// The callers have the responsibility of linearizing the changes to the chain
+// (or more likely call these always on the same thread).
BASE_EXPORT void InsertAllocatorDispatch(AllocatorDispatch* dispatch);
// Test-only. Rationale: (1) lack of use cases; (2) dealing safely with a
@@ -122,11 +90,6 @@ BASE_EXPORT void InsertAllocatorDispatch(AllocatorDispatch* dispatch);
// in malloc(), which we really don't want.
BASE_EXPORT void RemoveAllocatorDispatchForTesting(AllocatorDispatch* dispatch);
-#if defined(OS_MACOSX)
-// On macOS, the allocator shim needs to be turned on during runtime.
-BASE_EXPORT void InitializeAllocatorShim();
-#endif // defined(OS_MACOSX)
-
} // namespace allocator
} // namespace base
diff --git a/base/allocator/allocator_shim_default_dispatch_to_glibc.cc b/base/allocator/allocator_shim_default_dispatch_to_glibc.cc
index 6f386d4cc0..02facbad2e 100644
--- a/base/allocator/allocator_shim_default_dispatch_to_glibc.cc
+++ b/base/allocator/allocator_shim_default_dispatch_to_glibc.cc
@@ -4,10 +4,6 @@
#include "base/allocator/allocator_shim.h"
-#include <malloc.h>
-
-#include "base/compiler_specific.h"
-
// This translation unit defines a default dispatch for the allocator shim which
// routes allocations to libc functions.
// The code here is strongly inspired from tcmalloc's libc_override_glibc.h.
@@ -24,60 +20,33 @@ namespace {
using base::allocator::AllocatorDispatch;
-void* GlibcMalloc(const AllocatorDispatch*, size_t size, void* context) {
- ALLOW_UNUSED_PARAM(context);
+void* GlibcMalloc(const AllocatorDispatch*, size_t size) {
return __libc_malloc(size);
}
-void* GlibcCalloc(const AllocatorDispatch*,
- size_t n,
- size_t size,
- void* context) {
- ALLOW_UNUSED_PARAM(context);
+void* GlibcCalloc(const AllocatorDispatch*, size_t n, size_t size) {
return __libc_calloc(n, size);
}
-void* GlibcRealloc(const AllocatorDispatch*,
- void* address,
- size_t size,
- void* context) {
- ALLOW_UNUSED_PARAM(context);
+void* GlibcRealloc(const AllocatorDispatch*, void* address, size_t size) {
return __libc_realloc(address, size);
}
-void* GlibcMemalign(const AllocatorDispatch*,
- size_t alignment,
- size_t size,
- void* context) {
- ALLOW_UNUSED_PARAM(context);
+void* GlibcMemalign(const AllocatorDispatch*, size_t alignment, size_t size) {
return __libc_memalign(alignment, size);
}
-void GlibcFree(const AllocatorDispatch*, void* address, void* context) {
- ALLOW_UNUSED_PARAM(context);
+void GlibcFree(const AllocatorDispatch*, void* address) {
__libc_free(address);
}
-size_t GlibcGetSizeEstimate(const AllocatorDispatch*,
- void* address,
- void* context) {
- // TODO(siggi, primiano): malloc_usable_size may need redirection in the
- // presence of interposing shims that divert allocations.
- ALLOW_UNUSED_PARAM(context);
- return malloc_usable_size(address);
-}
-
} // namespace
const AllocatorDispatch AllocatorDispatch::default_dispatch = {
- &GlibcMalloc, /* alloc_function */
- &GlibcCalloc, /* alloc_zero_initialized_function */
- &GlibcMemalign, /* alloc_aligned_function */
- &GlibcRealloc, /* realloc_function */
- &GlibcFree, /* free_function */
- &GlibcGetSizeEstimate, /* get_size_estimate_function */
- nullptr, /* batch_malloc_function */
- nullptr, /* batch_free_function */
- nullptr, /* free_definite_size_function */
- nullptr, /* next */
+ &GlibcMalloc, /* alloc_function */
+ &GlibcCalloc, /* alloc_zero_initialized_function */
+ &GlibcMemalign, /* alloc_aligned_function */
+ &GlibcRealloc, /* realloc_function */
+ &GlibcFree, /* free_function */
+ nullptr, /* next */
};
diff --git a/base/allocator/allocator_shim_default_dispatch_to_linker_wrapped_symbols.cc b/base/allocator/allocator_shim_default_dispatch_to_linker_wrapped_symbols.cc
index 3ad13ef98f..7955cb7877 100644
--- a/base/allocator/allocator_shim_default_dispatch_to_linker_wrapped_symbols.cc
+++ b/base/allocator/allocator_shim_default_dispatch_to_linker_wrapped_symbols.cc
@@ -2,15 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <malloc.h>
-
#include "base/allocator/allocator_shim.h"
-#include "base/compiler_specific.h"
-#include "build/build_config.h"
-
-#if defined(OS_ANDROID) && __ANDROID_API__ < 17
-#include <dlfcn.h>
-#endif
// This translation unit defines a default dispatch for the allocator shim which
// routes allocations to the original libc functions when using the link-time
@@ -33,86 +25,33 @@ namespace {
using base::allocator::AllocatorDispatch;
-void* RealMalloc(const AllocatorDispatch*, size_t size, void* context) {
- ALLOW_UNUSED_PARAM(context);
+void* RealMalloc(const AllocatorDispatch*, size_t size) {
return __real_malloc(size);
}
-void* RealCalloc(const AllocatorDispatch*,
- size_t n,
- size_t size,
- void* context) {
- ALLOW_UNUSED_PARAM(context);
+void* RealCalloc(const AllocatorDispatch*, size_t n, size_t size) {
return __real_calloc(n, size);
}
-void* RealRealloc(const AllocatorDispatch*,
- void* address,
- size_t size,
- void* context) {
- ALLOW_UNUSED_PARAM(context);
+void* RealRealloc(const AllocatorDispatch*, void* address, size_t size) {
return __real_realloc(address, size);
}
-void* RealMemalign(const AllocatorDispatch*,
- size_t alignment,
- size_t size,
- void* context) {
- ALLOW_UNUSED_PARAM(context);
+void* RealMemalign(const AllocatorDispatch*, size_t alignment, size_t size) {
return __real_memalign(alignment, size);
}
-void RealFree(const AllocatorDispatch*, void* address, void* context) {
- ALLOW_UNUSED_PARAM(context);
+void RealFree(const AllocatorDispatch*, void* address) {
__real_free(address);
}
-#if defined(OS_ANDROID) && __ANDROID_API__ < 17
-size_t DummyMallocUsableSize(const void*) { return 0; }
-#endif
-
-size_t RealSizeEstimate(const AllocatorDispatch*,
- void* address,
- void* context) {
- ALLOW_UNUSED_PARAM(address);
- ALLOW_UNUSED_PARAM(context);
-#if defined(OS_ANDROID)
-#if __ANDROID_API__ < 17
- // malloc_usable_size() is available only starting from API 17.
- // TODO(dskiba): remove once we start building against 17+.
- using MallocUsableSizeFunction = decltype(malloc_usable_size)*;
- static MallocUsableSizeFunction usable_size_function = nullptr;
- if (!usable_size_function) {
- void* function_ptr = dlsym(RTLD_DEFAULT, "malloc_usable_size");
- if (function_ptr) {
- usable_size_function = reinterpret_cast<MallocUsableSizeFunction>(
- function_ptr);
- } else {
- usable_size_function = &DummyMallocUsableSize;
- }
- }
- return usable_size_function(address);
-#else
- return malloc_usable_size(address);
-#endif
-#endif // OS_ANDROID
-
- // TODO(primiano): This should be redirected to malloc_usable_size or
- // the like.
- return 0;
-}
-
} // namespace
const AllocatorDispatch AllocatorDispatch::default_dispatch = {
- &RealMalloc, /* alloc_function */
- &RealCalloc, /* alloc_zero_initialized_function */
- &RealMemalign, /* alloc_aligned_function */
- &RealRealloc, /* realloc_function */
- &RealFree, /* free_function */
- &RealSizeEstimate, /* get_size_estimate_function */
- nullptr, /* batch_malloc_function */
- nullptr, /* batch_free_function */
- nullptr, /* free_definite_size_function */
- nullptr, /* next */
+ &RealMalloc, /* alloc_function */
+ &RealCalloc, /* alloc_zero_initialized_function */
+ &RealMemalign, /* alloc_aligned_function */
+ &RealRealloc, /* realloc_function */
+ &RealFree, /* free_function */
+ nullptr, /* next */
};
diff --git a/base/allocator/allocator_shim_internals.h b/base/allocator/allocator_shim_internals.h
index 82624ee45b..fc3624c596 100644
--- a/base/allocator/allocator_shim_internals.h
+++ b/base/allocator/allocator_shim_internals.h
@@ -20,6 +20,8 @@
// Shim layer symbols need to be ALWAYS exported, regardless of component build.
#define SHIM_ALWAYS_EXPORT __attribute__((visibility("default")))
+#define SHIM_ALIAS_SYMBOL(fn) __attribute__((alias(#fn)))
+
#endif // __GNUC__
#endif // BASE_ALLOCATOR_ALLOCATOR_SHIM_INTERNALS_H_
diff --git a/base/allocator/allocator_shim_override_cpp_symbols.h b/base/allocator/allocator_shim_override_cpp_symbols.h
index 3313687250..616716fb96 100644
--- a/base/allocator/allocator_shim_override_cpp_symbols.h
+++ b/base/allocator/allocator_shim_override_cpp_symbols.h
@@ -7,45 +7,36 @@
#endif
#define BASE_ALLOCATOR_ALLOCATOR_SHIM_OVERRIDE_CPP_SYMBOLS_H_
-// Preempt the default new/delete C++ symbols so they call the shim entry
-// points. This file is strongly inspired by tcmalloc's
-// libc_override_redefine.h.
+// Alias the default new/delete C++ symbols to the shim entry points.
+// This file is strongly inspired by tcmalloc's libc_override_redefine.h.
#include <new>
#include "base/allocator/allocator_shim_internals.h"
-SHIM_ALWAYS_EXPORT void* operator new(size_t size) {
- return ShimCppNew(size);
-}
+SHIM_ALWAYS_EXPORT void* operator new(size_t size)
+ SHIM_ALIAS_SYMBOL(ShimCppNew);
-SHIM_ALWAYS_EXPORT void operator delete(void* p) __THROW {
- ShimCppDelete(p);
-}
+SHIM_ALWAYS_EXPORT void operator delete(void* p) __THROW
+ SHIM_ALIAS_SYMBOL(ShimCppDelete);
-SHIM_ALWAYS_EXPORT void* operator new[](size_t size) {
- return ShimCppNew(size);
-}
+SHIM_ALWAYS_EXPORT void* operator new[](size_t size)
+ SHIM_ALIAS_SYMBOL(ShimCppNew);
-SHIM_ALWAYS_EXPORT void operator delete[](void* p) __THROW {
- ShimCppDelete(p);
-}
+SHIM_ALWAYS_EXPORT void operator delete[](void* p) __THROW
+ SHIM_ALIAS_SYMBOL(ShimCppDelete);
SHIM_ALWAYS_EXPORT void* operator new(size_t size,
- const std::nothrow_t&) __THROW {
- return ShimCppNew(size);
-}
+ const std::nothrow_t&) __THROW
+ SHIM_ALIAS_SYMBOL(ShimCppNew);
SHIM_ALWAYS_EXPORT void* operator new[](size_t size,
- const std::nothrow_t&) __THROW {
- return ShimCppNew(size);
-}
+ const std::nothrow_t&) __THROW
+ SHIM_ALIAS_SYMBOL(ShimCppNew);
-SHIM_ALWAYS_EXPORT void operator delete(void* p, const std::nothrow_t&) __THROW {
- ShimCppDelete(p);
-}
+SHIM_ALWAYS_EXPORT void operator delete(void* p, const std::nothrow_t&) __THROW
+ SHIM_ALIAS_SYMBOL(ShimCppDelete);
SHIM_ALWAYS_EXPORT void operator delete[](void* p,
- const std::nothrow_t&) __THROW {
- ShimCppDelete(p);
-}
+ const std::nothrow_t&) __THROW
+ SHIM_ALIAS_SYMBOL(ShimCppDelete);
diff --git a/base/allocator/allocator_shim_override_libc_symbols.h b/base/allocator/allocator_shim_override_libc_symbols.h
index b77cbb1fe9..37b3b4eb12 100644
--- a/base/allocator/allocator_shim_override_libc_symbols.h
+++ b/base/allocator/allocator_shim_override_libc_symbols.h
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// Its purpose is to preempt the Libc symbols for malloc/new so they call the
+// Its purpose is to SHIM_ALIAS_SYMBOL the Libc symbols for malloc/new to the
// shim layer entry points.
#ifdef BASE_ALLOCATOR_ALLOCATOR_SHIM_OVERRIDE_LIBC_SYMBOLS_H_
@@ -16,41 +16,32 @@
extern "C" {
-SHIM_ALWAYS_EXPORT void* malloc(size_t size) __THROW {
- return ShimMalloc(size, nullptr);
-}
+SHIM_ALWAYS_EXPORT void* malloc(size_t size) __THROW
+ SHIM_ALIAS_SYMBOL(ShimMalloc);
-SHIM_ALWAYS_EXPORT void free(void* ptr) __THROW {
- ShimFree(ptr, nullptr);
-}
+SHIM_ALWAYS_EXPORT void free(void* ptr) __THROW
+ SHIM_ALIAS_SYMBOL(ShimFree);
-SHIM_ALWAYS_EXPORT void* realloc(void* ptr, size_t size) __THROW {
- return ShimRealloc(ptr, size, nullptr);
-}
+SHIM_ALWAYS_EXPORT void* realloc(void* ptr, size_t size) __THROW
+ SHIM_ALIAS_SYMBOL(ShimRealloc);
-SHIM_ALWAYS_EXPORT void* calloc(size_t n, size_t size) __THROW {
- return ShimCalloc(n, size, nullptr);
-}
+SHIM_ALWAYS_EXPORT void* calloc(size_t n, size_t size) __THROW
+ SHIM_ALIAS_SYMBOL(ShimCalloc);
-SHIM_ALWAYS_EXPORT void cfree(void* ptr) __THROW {
- ShimFree(ptr, nullptr);
-}
+SHIM_ALWAYS_EXPORT void cfree(void* ptr) __THROW
+ SHIM_ALIAS_SYMBOL(ShimFree);
-SHIM_ALWAYS_EXPORT void* memalign(size_t align, size_t s) __THROW {
- return ShimMemalign(align, s, nullptr);
-}
+SHIM_ALWAYS_EXPORT void* memalign(size_t align, size_t s) __THROW
+ SHIM_ALIAS_SYMBOL(ShimMemalign);
-SHIM_ALWAYS_EXPORT void* valloc(size_t size) __THROW {
- return ShimValloc(size, nullptr);
-}
+SHIM_ALWAYS_EXPORT void* valloc(size_t size) __THROW
+ SHIM_ALIAS_SYMBOL(ShimValloc);
-SHIM_ALWAYS_EXPORT void* pvalloc(size_t size) __THROW {
- return ShimPvalloc(size);
-}
+SHIM_ALWAYS_EXPORT void* pvalloc(size_t size) __THROW
+ SHIM_ALIAS_SYMBOL(ShimPvalloc);
-SHIM_ALWAYS_EXPORT int posix_memalign(void** r, size_t a, size_t s) __THROW {
- return ShimPosixMemalign(r, a, s);
-}
+SHIM_ALWAYS_EXPORT int posix_memalign(void** r, size_t a, size_t s) __THROW
+ SHIM_ALIAS_SYMBOL(ShimPosixMemalign);
// The default dispatch translation unit has to define also the following
// symbols (unless they are ultimately routed to the system symbols):
diff --git a/base/allocator/allocator_shim_override_linker_wrapped_symbols.h b/base/allocator/allocator_shim_override_linker_wrapped_symbols.h
index 6bf73c39f2..5b85d6ee2f 100644
--- a/base/allocator/allocator_shim_override_linker_wrapped_symbols.h
+++ b/base/allocator/allocator_shim_override_linker_wrapped_symbols.h
@@ -17,38 +17,28 @@
extern "C" {
-SHIM_ALWAYS_EXPORT void* __wrap_calloc(size_t n, size_t size) {
- return ShimCalloc(n, size, nullptr);
-}
-
-SHIM_ALWAYS_EXPORT void __wrap_free(void* ptr) {
- ShimFree(ptr, nullptr);
-}
-
-SHIM_ALWAYS_EXPORT void* __wrap_malloc(size_t size) {
- return ShimMalloc(size, nullptr);
-}
-
-SHIM_ALWAYS_EXPORT void* __wrap_memalign(size_t align, size_t size) {
- return ShimMemalign(align, size, nullptr);
-}
-
-SHIM_ALWAYS_EXPORT int __wrap_posix_memalign(void** res,
- size_t align,
- size_t size) {
- return ShimPosixMemalign(res, align, size);
-}
-
-SHIM_ALWAYS_EXPORT void* __wrap_pvalloc(size_t size) {
- return ShimPvalloc(size);
-}
-
-SHIM_ALWAYS_EXPORT void* __wrap_realloc(void* address, size_t size) {
- return ShimRealloc(address, size, nullptr);
-}
-
-SHIM_ALWAYS_EXPORT void* __wrap_valloc(size_t size) {
- return ShimValloc(size, nullptr);
-}
+SHIM_ALWAYS_EXPORT void* __wrap_calloc(size_t, size_t)
+ SHIM_ALIAS_SYMBOL(ShimCalloc);
+
+SHIM_ALWAYS_EXPORT void __wrap_free(void*)
+ SHIM_ALIAS_SYMBOL(ShimFree);
+
+SHIM_ALWAYS_EXPORT void* __wrap_malloc(size_t)
+ SHIM_ALIAS_SYMBOL(ShimMalloc);
+
+SHIM_ALWAYS_EXPORT void* __wrap_memalign(size_t, size_t)
+ SHIM_ALIAS_SYMBOL(ShimMemalign);
+
+SHIM_ALWAYS_EXPORT int __wrap_posix_memalign(void**, size_t, size_t)
+ SHIM_ALIAS_SYMBOL(ShimPosixMemalign);
+
+SHIM_ALWAYS_EXPORT void* __wrap_pvalloc(size_t)
+ SHIM_ALIAS_SYMBOL(ShimPvalloc);
+
+SHIM_ALWAYS_EXPORT void* __wrap_realloc(void*, size_t)
+ SHIM_ALIAS_SYMBOL(ShimRealloc);
+
+SHIM_ALWAYS_EXPORT void* __wrap_valloc(size_t)
+ SHIM_ALIAS_SYMBOL(ShimValloc);
} // extern "C"
diff --git a/base/at_exit.cc b/base/at_exit.cc
index 5dcc83cb2f..cfe4cf9a58 100644
--- a/base/at_exit.cc
+++ b/base/at_exit.cc
@@ -22,8 +22,6 @@ namespace base {
// this for thread-safe access, since it will only be modified in testing.
static AtExitManager* g_top_manager = NULL;
-static bool g_disable_managers = false;
-
AtExitManager::AtExitManager()
: processing_callbacks_(false), next_manager_(g_top_manager) {
// If multiple modules instantiate AtExitManagers they'll end up living in this
@@ -41,8 +39,7 @@ AtExitManager::~AtExitManager() {
}
DCHECK_EQ(this, g_top_manager);
- if (!g_disable_managers)
- ProcessCallbacksNow();
+ ProcessCallbacksNow();
g_top_manager = next_manager_;
}
@@ -91,11 +88,6 @@ void AtExitManager::ProcessCallbacksNow() {
DCHECK(g_top_manager->stack_.empty());
}
-void AtExitManager::DisableAllAtExitManagers() {
- AutoLock lock(g_top_manager->lock_);
- g_disable_managers = true;
-}
-
AtExitManager::AtExitManager(bool shadow)
: processing_callbacks_(false), next_manager_(g_top_manager) {
DCHECK(shadow || !g_top_manager);
diff --git a/base/at_exit.h b/base/at_exit.h
index 6bf3f50350..02e18ed9eb 100644
--- a/base/at_exit.h
+++ b/base/at_exit.h
@@ -49,10 +49,6 @@ class BASE_EXPORT AtExitManager {
// is possible to register new callbacks after calling this function.
static void ProcessCallbacksNow();
- // Disable all registered at-exit callbacks. This is used only in a single-
- // process mode.
- static void DisableAllAtExitManagers();
-
protected:
// This constructor will allow this instance of AtExitManager to be created
// even if one already exists. This should only be used for testing!
diff --git a/base/atomic_ref_count.h b/base/atomic_ref_count.h
index 93c1f0dfd4..2ab7242002 100644
--- a/base/atomic_ref_count.h
+++ b/base/atomic_ref_count.h
@@ -12,7 +12,7 @@
namespace base {
-typedef subtle::AtomicWord AtomicRefCount;
+typedef subtle::Atomic32 AtomicRefCount;
// Increment a reference count by "increment", which must exceed 0.
inline void AtomicRefCountIncN(volatile AtomicRefCount *ptr,
diff --git a/base/base.gyp b/base/base.gyp
new file mode 100644
index 0000000000..a534d5ccb7
--- /dev/null
+++ b/base/base.gyp
@@ -0,0 +1,1801 @@
+# 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.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'includes': [
+ '../build/win_precompile.gypi',
+ 'base.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'base',
+ 'type': '<(component)',
+ 'toolsets': ['host', 'target'],
+ 'variables': {
+ 'base_target': 1,
+ 'enable_wexit_time_destructors': 1,
+ 'optimize': 'max',
+ },
+ 'dependencies': [
+ 'allocator/allocator.gyp:allocator',
+ 'allocator/allocator.gyp:allocator_features#target',
+ 'base_debugging_flags#target',
+ 'base_win_features#target',
+ 'base_static',
+ 'base_build_date#target',
+ '../testing/gtest.gyp:gtest_prod',
+ '../third_party/modp_b64/modp_b64.gyp:modp_b64',
+ 'third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ ],
+ # TODO(gregoryd): direct_dependent_settings should be shared with the
+ # 64-bit target, but it doesn't work due to a bug in gyp
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ 'conditions': [
+ ['desktop_linux == 1 or chromeos == 1', {
+ 'conditions': [
+ ['chromeos==1', {
+ 'sources/': [ ['include', '_chromeos\\.cc$'] ]
+ }],
+ ],
+ 'dependencies': [
+ 'symbolize',
+ 'xdg_mime',
+ ],
+ 'defines': [
+ 'USE_SYMBOLIZE',
+ ],
+ }, { # desktop_linux == 0 and chromeos == 0
+ 'sources/': [
+ ['exclude', '/xdg_user_dirs/'],
+ ['exclude', '_nss\\.cc$'],
+ ],
+ }],
+ ['use_glib==1', {
+ 'dependencies': [
+ '../build/linux/system.gyp:glib',
+ ],
+ 'export_dependent_settings': [
+ '../build/linux/system.gyp:glib',
+ ],
+ }],
+ ['OS == "android" and _toolset == "host"', {
+ # Always build base as a static_library for host toolset, even if
+ # we're doing a component build. Specifically, we only care about the
+ # target toolset using components since that's what developers are
+ # focusing on. In theory we should do this more generally for all
+ # targets when building for host, but getting the gyp magic
+ # per-toolset for the "component" variable is hard, and we really only
+ # need base on host.
+ 'type': 'static_library',
+ # Base for host support is the minimum required to run the
+ # ssl false start blacklist tool. It requires further changes
+ # to generically support host builds (and tests).
+ # Note: when building for host, gyp has OS == "android",
+ # hence the *_android.cc files are included but the actual code
+ # doesn't have OS_ANDROID / ANDROID defined.
+ 'conditions': [
+ ['host_os == "mac"', {
+ 'sources/': [
+ ['exclude', '^native_library_linux\\.cc$'],
+ ['exclude', '^process_util_linux\\.cc$'],
+ ['exclude', '^sys_info_linux\\.cc$'],
+ ['exclude', '^sys_string_conversions_linux\\.cc$'],
+ ['exclude', '^worker_pool_linux\\.cc$'],
+ ],
+ }],
+ ],
+ }],
+ ['OS == "android" and _toolset == "target"', {
+ 'dependencies': [
+ 'base_java',
+ 'base_jni_headers',
+ '../build/android/ndk.gyp:cpu_features',
+ '../third_party/ashmem/ashmem.gyp:ashmem',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '-llog',
+ ],
+ },
+ 'sources!': [
+ 'debug/stack_trace_posix.cc',
+ ],
+ }],
+ ['os_bsd==1', {
+ 'include_dirs': [
+ '/usr/local/include',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '-L/usr/local/lib -lexecinfo',
+ ],
+ },
+ }],
+ ['OS == "linux"', {
+ 'link_settings': {
+ 'libraries': [
+ # We need rt for clock_gettime().
+ '-lrt',
+ # For 'native_library_linux.cc'
+ '-ldl',
+ ],
+ },
+ 'conditions': [
+ ['use_allocator!="tcmalloc"', {
+ 'defines': [
+ 'NO_TCMALLOC',
+ ],
+ 'direct_dependent_settings': {
+ 'defines': [
+ 'NO_TCMALLOC',
+ ],
+ },
+ }],
+ ],
+ }],
+ ['use_sysroot==0 and (OS == "android" or OS == "linux")', {
+ 'link_settings': {
+ 'libraries': [
+ # Needed for <atomic> when building with newer C++ library.
+ '-latomic',
+ ],
+ },
+ }],
+ ['OS == "win"', {
+ # Specify delayload for base.dll.
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'DelayLoadDLLs': [
+ 'cfgmgr32.dll',
+ 'powrprof.dll',
+ 'setupapi.dll',
+ ],
+ 'AdditionalDependencies': [
+ 'cfgmgr32.lib',
+ 'powrprof.lib',
+ 'setupapi.lib',
+ 'userenv.lib',
+ 'winmm.lib',
+ ],
+ },
+ },
+ # Specify delayload for components that link with base.lib.
+ 'all_dependent_settings': {
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'DelayLoadDLLs': [
+ 'cfgmgr32.dll',
+ 'powrprof.dll',
+ 'setupapi.dll',
+ ],
+ 'AdditionalDependencies': [
+ 'cfgmgr32.lib',
+ 'powrprof.lib',
+ 'setupapi.lib',
+ 'userenv.lib',
+ 'winmm.lib',
+ ],
+ },
+ },
+ },
+ 'dependencies': [
+ 'trace_event/etw_manifest/etw_manifest.gyp:etw_manifest',
+ ],
+ }],
+ ['OS == "mac" or (OS == "ios" and _toolset == "host")', {
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/AppKit.framework',
+ '$(SDKROOT)/System/Library/Frameworks/ApplicationServices.framework',
+ '$(SDKROOT)/System/Library/Frameworks/Carbon.framework',
+ '$(SDKROOT)/System/Library/Frameworks/CoreFoundation.framework',
+ '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+ '$(SDKROOT)/System/Library/Frameworks/IOKit.framework',
+ '$(SDKROOT)/System/Library/Frameworks/Security.framework',
+ '$(SDKROOT)/usr/lib/libbsm.dylib',
+ ],
+ },
+ }],
+ ['OS == "ios" and _toolset != "host"', {
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/CoreFoundation.framework',
+ '$(SDKROOT)/System/Library/Frameworks/CoreGraphics.framework',
+ '$(SDKROOT)/System/Library/Frameworks/CoreText.framework',
+ '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+ '$(SDKROOT)/System/Library/Frameworks/UIKit.framework',
+ ],
+ },
+ }],
+ ['OS != "win" and (OS != "ios" or _toolset == "host")', {
+ 'dependencies': ['third_party/libevent/libevent.gyp:libevent'],
+ },],
+ ['component=="shared_library"', {
+ 'conditions': [
+ ['OS=="win"', {
+ 'sources!': [
+ 'debug/debug_on_start_win.cc',
+ ],
+ }],
+ ],
+ }],
+ ['OS=="ios"', {
+ 'sources!': [
+ 'sync_socket.h',
+ 'sync_socket_posix.cc',
+ ]
+ }],
+ ['use_experimental_allocator_shim==1', {
+ 'dependencies': [ 'allocator/allocator.gyp:unified_allocator_shim']
+ }],
+ ],
+ 'sources': [
+ 'auto_reset.h',
+ 'linux_util.cc',
+ 'linux_util.h',
+ 'message_loop/message_pump_android.cc',
+ 'message_loop/message_pump_android.h',
+ 'message_loop/message_pump_glib.cc',
+ 'message_loop/message_pump_glib.h',
+ 'message_loop/message_pump_io_ios.cc',
+ 'message_loop/message_pump_io_ios.h',
+ 'message_loop/message_pump_libevent.cc',
+ 'message_loop/message_pump_libevent.h',
+ 'message_loop/message_pump_mac.h',
+ 'message_loop/message_pump_mac.mm',
+ 'metrics/field_trial.cc',
+ 'metrics/field_trial.h',
+ 'posix/file_descriptor_shuffle.cc',
+ 'posix/file_descriptor_shuffle.h',
+ 'sync_socket.h',
+ 'sync_socket_posix.cc',
+ 'sync_socket_win.cc',
+ 'third_party/xdg_user_dirs/xdg_user_dir_lookup.cc',
+ 'third_party/xdg_user_dirs/xdg_user_dir_lookup.h',
+ ],
+ 'includes': [
+ '../build/android/increase_size_for_speed.gypi',
+ ],
+ },
+ {
+ 'target_name': 'base_i18n',
+ 'type': '<(component)',
+ 'variables': {
+ 'enable_wexit_time_destructors': 1,
+ 'optimize': 'max',
+ 'base_i18n_target': 1,
+ },
+ 'dependencies': [
+ 'base',
+ 'third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ '../third_party/icu/icu.gyp:icui18n',
+ '../third_party/icu/icu.gyp:icuuc',
+ ],
+ 'conditions': [
+ ['OS == "win"', {
+ # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+ 'msvs_disabled_warnings': [
+ 4267,
+ ],
+ }],
+ ['icu_use_data_file_flag==1', {
+ 'defines': ['ICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_FILE'],
+ }, { # else icu_use_data_file_flag !=1
+ 'conditions': [
+ ['OS=="win"', {
+ 'defines': ['ICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_SHARED'],
+ }, {
+ 'defines': ['ICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_STATIC'],
+ }],
+ ],
+ }],
+ ['OS == "ios"', {
+ 'toolsets': ['host', 'target'],
+ }],
+ ],
+ 'export_dependent_settings': [
+ 'base',
+ '../third_party/icu/icu.gyp:icuuc',
+ '../third_party/icu/icu.gyp:icui18n',
+ ],
+ 'includes': [
+ '../build/android/increase_size_for_speed.gypi',
+ ],
+ },
+ {
+ 'target_name': 'base_message_loop_tests',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'base',
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'sources': [
+ 'message_loop/message_loop_test.cc',
+ 'message_loop/message_loop_test.h',
+ ],
+ },
+ {
+ # This is the subset of files from base that should not be used with a
+ # dynamic library. Note that this library cannot depend on base because
+ # base depends on base_static.
+ 'target_name': 'base_static',
+ 'type': 'static_library',
+ 'variables': {
+ 'enable_wexit_time_destructors': 1,
+ 'optimize': 'max',
+ },
+ 'toolsets': ['host', 'target'],
+ 'sources': [
+ 'base_switches.cc',
+ 'base_switches.h',
+ 'win/pe_image.cc',
+ 'win/pe_image.h',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'includes': [
+ '../build/android/increase_size_for_speed.gypi',
+ ],
+ },
+ # Include this target for a main() function that simply instantiates
+ # and runs a base::TestSuite.
+ {
+ 'target_name': 'run_all_unittests',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'test_support_base',
+ ],
+ 'sources': [
+ 'test/run_all_unittests.cc',
+ ],
+ },
+ {
+ 'target_name': 'base_unittests',
+ 'type': '<(gtest_target_type)',
+ 'sources': [
+ 'allocator/tcmalloc_unittest.cc',
+ 'android/application_status_listener_unittest.cc',
+ 'android/content_uri_utils_unittest.cc',
+ 'android/jni_android_unittest.cc',
+ 'android/jni_array_unittest.cc',
+ 'android/jni_string_unittest.cc',
+ 'android/library_loader/library_prefetcher_unittest.cc',
+ 'android/path_utils_unittest.cc',
+ 'android/scoped_java_ref_unittest.cc',
+ 'android/sys_utils_unittest.cc',
+ 'at_exit_unittest.cc',
+ 'atomicops_unittest.cc',
+ 'barrier_closure_unittest.cc',
+ 'base64_unittest.cc',
+ 'base64url_unittest.cc',
+ 'big_endian_unittest.cc',
+ 'bind_unittest.cc',
+ 'bind_unittest.nc',
+ 'bit_cast_unittest.cc',
+ 'bits_unittest.cc',
+ 'build_time_unittest.cc',
+ 'callback_helpers_unittest.cc',
+ 'callback_list_unittest.cc',
+ 'callback_list_unittest.nc',
+ 'callback_unittest.cc',
+ 'callback_unittest.nc',
+ 'cancelable_callback_unittest.cc',
+ 'command_line_unittest.cc',
+ 'containers/adapters_unittest.cc',
+ 'containers/hash_tables_unittest.cc',
+ 'containers/linked_list_unittest.cc',
+ 'containers/mru_cache_unittest.cc',
+ 'containers/scoped_ptr_hash_map_unittest.cc',
+ 'containers/small_map_unittest.cc',
+ 'containers/stack_container_unittest.cc',
+ 'cpu_unittest.cc',
+ 'debug/crash_logging_unittest.cc',
+ 'debug/debugger_unittest.cc',
+ 'debug/leak_tracker_unittest.cc',
+ 'debug/proc_maps_linux_unittest.cc',
+ 'debug/stack_trace_unittest.cc',
+ 'debug/task_annotator_unittest.cc',
+ 'deferred_sequenced_task_runner_unittest.cc',
+ 'environment_unittest.cc',
+ 'feature_list_unittest.cc',
+ 'file_version_info_win_unittest.cc',
+ 'files/dir_reader_posix_unittest.cc',
+ 'files/file_locking_unittest.cc',
+ 'files/file_path_unittest.cc',
+ 'files/file_path_watcher_unittest.cc',
+ 'files/file_proxy_unittest.cc',
+ 'files/file_unittest.cc',
+ 'files/file_util_proxy_unittest.cc',
+ 'files/file_util_unittest.cc',
+ 'files/important_file_writer_unittest.cc',
+ 'files/memory_mapped_file_unittest.cc',
+ 'files/scoped_temp_dir_unittest.cc',
+ 'gmock_unittest.cc',
+ 'guid_unittest.cc',
+ 'hash_unittest.cc',
+ 'i18n/break_iterator_unittest.cc',
+ 'i18n/case_conversion_unittest.cc',
+ 'i18n/char_iterator_unittest.cc',
+ 'i18n/file_util_icu_unittest.cc',
+ 'i18n/icu_string_conversions_unittest.cc',
+ 'i18n/message_formatter_unittest.cc',
+ 'i18n/number_formatting_unittest.cc',
+ 'i18n/rtl_unittest.cc',
+ 'i18n/streaming_utf8_validator_unittest.cc',
+ 'i18n/string_search_unittest.cc',
+ 'i18n/time_formatting_unittest.cc',
+ 'i18n/timezone_unittest.cc',
+ 'id_map_unittest.cc',
+ 'ios/crb_protocol_observers_unittest.mm',
+ 'ios/device_util_unittest.mm',
+ 'ios/weak_nsobject_unittest.mm',
+ 'json/json_parser_unittest.cc',
+ 'json/json_reader_unittest.cc',
+ 'json/json_value_converter_unittest.cc',
+ 'json/json_value_serializer_unittest.cc',
+ 'json/json_writer_unittest.cc',
+ 'json/string_escape_unittest.cc',
+ 'lazy_instance_unittest.cc',
+ 'logging_unittest.cc',
+ 'mac/bind_objc_block_unittest.mm',
+ 'mac/call_with_eh_frame_unittest.mm',
+ 'mac/dispatch_source_mach_unittest.cc',
+ 'mac/foundation_util_unittest.mm',
+ 'mac/mac_util_unittest.mm',
+ 'mac/mach_port_broker_unittest.cc',
+ 'mac/objc_property_releaser_unittest.mm',
+ 'mac/scoped_nsobject_unittest.mm',
+ 'mac/scoped_objc_class_swizzler_unittest.mm',
+ 'mac/scoped_sending_event_unittest.mm',
+ 'md5_unittest.cc',
+ 'memory/aligned_memory_unittest.cc',
+ 'memory/discardable_shared_memory_unittest.cc',
+ 'memory/linked_ptr_unittest.cc',
+ 'memory/memory_pressure_listener_unittest.cc',
+ 'memory/memory_pressure_monitor_chromeos_unittest.cc',
+ 'memory/memory_pressure_monitor_mac_unittest.cc',
+ 'memory/memory_pressure_monitor_win_unittest.cc',
+ 'memory/ptr_util_unittest.cc',
+ 'memory/ref_counted_memory_unittest.cc',
+ 'memory/ref_counted_unittest.cc',
+ 'memory/scoped_vector_unittest.cc',
+ 'memory/shared_memory_mac_unittest.cc',
+ 'memory/shared_memory_unittest.cc',
+ 'memory/shared_memory_win_unittest.cc',
+ 'memory/singleton_unittest.cc',
+ 'memory/weak_ptr_unittest.cc',
+ 'memory/weak_ptr_unittest.nc',
+ 'message_loop/message_loop_task_runner_unittest.cc',
+ 'message_loop/message_loop_unittest.cc',
+ 'message_loop/message_pump_glib_unittest.cc',
+ 'message_loop/message_pump_io_ios_unittest.cc',
+ 'message_loop/message_pump_libevent_unittest.cc',
+ 'metrics/bucket_ranges_unittest.cc',
+ 'metrics/field_trial_unittest.cc',
+ 'metrics/histogram_base_unittest.cc',
+ 'metrics/histogram_delta_serialization_unittest.cc',
+ 'metrics/histogram_macros_unittest.cc',
+ 'metrics/histogram_snapshot_manager_unittest.cc',
+ 'metrics/histogram_unittest.cc',
+ 'metrics/metrics_hashes_unittest.cc',
+ 'metrics/persistent_histogram_allocator_unittest.cc',
+ 'metrics/persistent_memory_allocator_unittest.cc',
+ 'metrics/persistent_sample_map_unittest.cc',
+ 'metrics/sample_map_unittest.cc',
+ 'metrics/sample_vector_unittest.cc',
+ 'metrics/sparse_histogram_unittest.cc',
+ 'metrics/statistics_recorder_unittest.cc',
+ 'native_library_unittest.cc',
+ 'numerics/safe_numerics_unittest.cc',
+ 'observer_list_unittest.cc',
+ 'optional_unittest.cc',
+ 'os_compat_android_unittest.cc',
+ 'path_service_unittest.cc',
+ 'pickle_unittest.cc',
+ 'posix/file_descriptor_shuffle_unittest.cc',
+ 'posix/unix_domain_socket_linux_unittest.cc',
+ 'power_monitor/power_monitor_unittest.cc',
+ 'process/memory_unittest.cc',
+ 'process/memory_unittest_mac.h',
+ 'process/memory_unittest_mac.mm',
+ 'process/process_metrics_unittest.cc',
+ 'process/process_metrics_unittest_ios.cc',
+ 'process/process_unittest.cc',
+ 'process/process_util_unittest.cc',
+ 'profiler/stack_sampling_profiler_unittest.cc',
+ 'profiler/tracked_time_unittest.cc',
+ 'rand_util_unittest.cc',
+ 'run_loop_unittest.cc',
+ 'scoped_clear_errno_unittest.cc',
+ 'scoped_generic_unittest.cc',
+ 'scoped_native_library_unittest.cc',
+ 'security_unittest.cc',
+ 'sequence_checker_unittest.cc',
+ 'sha1_unittest.cc',
+ 'stl_util_unittest.cc',
+ 'strings/nullable_string16_unittest.cc',
+ 'strings/pattern_unittest.cc',
+ 'strings/safe_sprintf_unittest.cc',
+ 'strings/string16_unittest.cc',
+ 'strings/string_number_conversions_unittest.cc',
+ 'strings/string_piece_unittest.cc',
+ 'strings/string_split_unittest.cc',
+ 'strings/string_tokenizer_unittest.cc',
+ 'strings/string_util_unittest.cc',
+ 'strings/stringize_macros_unittest.cc',
+ 'strings/stringprintf_unittest.cc',
+ 'strings/sys_string_conversions_mac_unittest.mm',
+ 'strings/sys_string_conversions_unittest.cc',
+ 'strings/utf_offset_string_conversions_unittest.cc',
+ 'strings/utf_string_conversions_unittest.cc',
+ 'supports_user_data_unittest.cc',
+ 'sync_socket_unittest.cc',
+ 'synchronization/cancellation_flag_unittest.cc',
+ 'synchronization/condition_variable_unittest.cc',
+ 'synchronization/lock_unittest.cc',
+ 'synchronization/read_write_lock_unittest.cc',
+ 'synchronization/waitable_event_unittest.cc',
+ 'synchronization/waitable_event_watcher_unittest.cc',
+ 'sys_byteorder_unittest.cc',
+ 'sys_info_unittest.cc',
+ 'system_monitor/system_monitor_unittest.cc',
+ 'task/cancelable_task_tracker_unittest.cc',
+ 'task_runner_util_unittest.cc',
+ 'task_scheduler/delayed_task_manager_unittest.cc',
+ 'task_scheduler/priority_queue_unittest.cc',
+ 'task_scheduler/scheduler_lock_unittest.cc',
+ 'task_scheduler/scheduler_service_thread_unittest.cc',
+ 'task_scheduler/scheduler_worker_unittest.cc',
+ 'task_scheduler/scheduler_worker_pool_impl_unittest.cc',
+ 'task_scheduler/scheduler_worker_stack_unittest.cc',
+ 'task_scheduler/sequence_sort_key_unittest.cc',
+ 'task_scheduler/sequence_unittest.cc',
+ 'task_scheduler/task_scheduler_impl_unittest.cc',
+ 'task_scheduler/task_tracker_unittest.cc',
+ 'task_scheduler/test_task_factory.cc',
+ 'task_scheduler/test_task_factory.h',
+ 'task_scheduler/test_utils.h',
+ 'template_util_unittest.cc',
+ 'test/histogram_tester_unittest.cc',
+ 'test/test_pending_task_unittest.cc',
+ 'test/test_reg_util_win_unittest.cc',
+ 'test/trace_event_analyzer_unittest.cc',
+ 'test/user_action_tester_unittest.cc',
+ 'threading/non_thread_safe_unittest.cc',
+ 'threading/platform_thread_unittest.cc',
+ 'threading/sequenced_worker_pool_unittest.cc',
+ 'threading/sequenced_task_runner_handle_unittest.cc',
+ 'threading/simple_thread_unittest.cc',
+ 'threading/thread_checker_unittest.cc',
+ 'threading/thread_collision_warner_unittest.cc',
+ 'threading/thread_id_name_manager_unittest.cc',
+ 'threading/thread_local_storage_unittest.cc',
+ 'threading/thread_local_unittest.cc',
+ 'threading/thread_unittest.cc',
+ 'threading/watchdog_unittest.cc',
+ 'threading/worker_pool_posix_unittest.cc',
+ 'threading/worker_pool_unittest.cc',
+ 'time/pr_time_unittest.cc',
+ 'time/time_unittest.cc',
+ 'time/time_win_unittest.cc',
+ 'timer/hi_res_timer_manager_unittest.cc',
+ 'timer/mock_timer_unittest.cc',
+ 'timer/timer_unittest.cc',
+ 'tools_sanity_unittest.cc',
+ 'tracked_objects_unittest.cc',
+ 'tuple_unittest.cc',
+ 'values_unittest.cc',
+ 'version_unittest.cc',
+ 'vlog_unittest.cc',
+ 'win/dllmain.cc',
+ 'win/enum_variant_unittest.cc',
+ 'win/event_trace_consumer_unittest.cc',
+ 'win/event_trace_controller_unittest.cc',
+ 'win/event_trace_provider_unittest.cc',
+ 'win/i18n_unittest.cc',
+ 'win/iunknown_impl_unittest.cc',
+ 'win/message_window_unittest.cc',
+ 'win/object_watcher_unittest.cc',
+ 'win/pe_image_unittest.cc',
+ 'win/registry_unittest.cc',
+ 'win/scoped_bstr_unittest.cc',
+ 'win/scoped_comptr_unittest.cc',
+ 'win/scoped_handle_unittest.cc',
+ 'win/scoped_process_information_unittest.cc',
+ 'win/scoped_variant_unittest.cc',
+ 'win/shortcut_unittest.cc',
+ 'win/startup_information_unittest.cc',
+ 'win/wait_chain_unittest.cc',
+ 'win/win_util_unittest.cc',
+ 'win/windows_version_unittest.cc',
+ 'win/wrapped_window_proc_unittest.cc',
+ '<@(trace_event_test_sources)',
+ ],
+ 'dependencies': [
+ 'base',
+ 'base_i18n',
+ 'base_message_loop_tests',
+ 'base_static',
+ 'run_all_unittests',
+ 'test_support_base',
+ 'third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ '../testing/gmock.gyp:gmock',
+ '../testing/gtest.gyp:gtest',
+ '../third_party/icu/icu.gyp:icui18n',
+ '../third_party/icu/icu.gyp:icuuc',
+ ],
+ 'includes': ['../build/nocompile.gypi'],
+ 'variables': {
+ # TODO(ajwong): Is there a way to autodetect this?
+ 'module_dir': 'base'
+ },
+ 'conditions': [
+ ['cfi_vptr==1 and cfi_cast==1', {
+ 'defines': [
+ # TODO(krasin): remove CFI_CAST_CHECK, see https://crbug.com/626794.
+ 'CFI_CAST_CHECK',
+ ],
+ }],
+ ['OS == "ios" or OS == "mac"', {
+ 'dependencies': [
+ 'base_unittests_arc',
+ ],
+ }],
+ ['OS == "android"', {
+ 'dependencies': [
+ 'android/jni_generator/jni_generator.gyp:jni_generator_tests',
+ '../testing/android/native_test.gyp:native_test_native_code',
+ ],
+ }],
+ ['OS == "ios" and _toolset != "host"', {
+ 'sources/': [
+ # This test needs multiple processes.
+ ['exclude', '^files/file_locking_unittest\\.cc$'],
+ # iOS does not support FilePathWatcher.
+ ['exclude', '^files/file_path_watcher_unittest\\.cc$'],
+ # Only test the iOS-meaningful portion of memory and process_utils.
+ ['exclude', '^memory/discardable_shared_memory_unittest\\.cc$'],
+ ['exclude', '^memory/shared_memory_unittest\\.cc$'],
+ ['exclude', '^process/memory_unittest'],
+ ['exclude', '^process/process_unittest\\.cc$'],
+ ['exclude', '^process/process_util_unittest\\.cc$'],
+ ['include', '^process/process_util_unittest_ios\\.cc$'],
+ # iOS does not use message_pump_libevent.
+ ['exclude', '^message_loop/message_pump_libevent_unittest\\.cc$'],
+ ],
+ 'actions': [
+ {
+ 'action_name': 'copy_test_data',
+ 'variables': {
+ 'test_data_files': [
+ 'test/data',
+ ],
+ 'test_data_prefix': 'base',
+ },
+ 'includes': [ '../build/copy_test_data_ios.gypi' ],
+ },
+ ],
+ }],
+ ['desktop_linux == 1 or chromeos == 1', {
+ 'defines': [
+ 'USE_SYMBOLIZE',
+ ],
+ 'conditions': [
+ [ 'desktop_linux==1', {
+ 'sources': [
+ 'nix/xdg_util_unittest.cc',
+ ],
+ }],
+ ],
+ }],
+ ['use_glib == 1', {
+ 'dependencies': [
+ '../build/linux/system.gyp:glib',
+ ],
+ }, { # use_glib == 0
+ 'sources!': [
+ 'message_loop/message_pump_glib_unittest.cc',
+ ]
+ }],
+ ['use_ozone == 1', {
+ 'sources!': [
+ 'message_loop/message_pump_glib_unittest.cc',
+ ]
+ }],
+ ['OS == "linux"', {
+ 'dependencies': [
+ 'malloc_wrapper',
+ ],
+ }],
+ [ 'OS == "win" and target_arch == "x64"', {
+ 'sources': [
+ 'profiler/win32_stack_frame_unwinder_unittest.cc',
+ ],
+ 'dependencies': [
+ 'base_profiler_test_support_library',
+ ],
+ }],
+ ['OS == "win"', {
+ 'dependencies': [
+ 'scoped_handle_test_dll'
+ ],
+ 'sources!': [
+ 'file_descriptor_shuffle_unittest.cc',
+ 'files/dir_reader_posix_unittest.cc',
+ 'message_loop/message_pump_libevent_unittest.cc',
+ 'threading/worker_pool_posix_unittest.cc',
+ ],
+ # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+ 'msvs_disabled_warnings': [
+ 4267,
+ ],
+ 'conditions': [
+ ['icu_use_data_file_flag==0', {
+ # This is needed to trigger the dll copy step on windows.
+ # TODO(mark): This should not be necessary.
+ 'dependencies': [
+ '../third_party/icu/icu.gyp:icudata',
+ ],
+ }],
+ ],
+ }, { # OS != "win"
+ 'dependencies': [
+ 'third_party/libevent/libevent.gyp:libevent'
+ ],
+ }],
+ ['use_experimental_allocator_shim==1', {
+ 'sources': [ 'allocator/allocator_shim_unittest.cc']
+ }],
+ ], # conditions
+ 'target_conditions': [
+ ['OS == "ios" and _toolset != "host"', {
+ 'sources/': [
+ # Pull in specific Mac files for iOS (which have been filtered out
+ # by file name rules).
+ ['include', '^mac/bind_objc_block_unittest\\.mm$'],
+ ['include', '^mac/foundation_util_unittest\\.mm$',],
+ ['include', '^mac/objc_property_releaser_unittest\\.mm$'],
+ ['include', '^mac/scoped_nsobject_unittest\\.mm$'],
+ ['include', '^sys_string_conversions_mac_unittest\\.mm$'],
+ ],
+ }],
+ ['OS == "android"', {
+ 'sources/': [
+ ['include', '^debug/proc_maps_linux_unittest\\.cc$'],
+ ],
+ }],
+ # Enable more direct string conversions on platforms with native utf8
+ # strings
+ ['OS=="mac" or OS=="ios" or <(chromeos)==1 or <(chromecast)==1', {
+ 'defines': ['SYSTEM_NATIVE_UTF8'],
+ }],
+ # SyncSocket isn't used on iOS
+ ['OS=="ios"', {
+ 'sources!': [
+ 'sync_socket_unittest.cc',
+ ],
+ }],
+ ], # target_conditions
+ },
+ {
+ # GN: //base:base_perftests
+ 'target_name': 'base_perftests',
+ 'type': '<(gtest_target_type)',
+ 'dependencies': [
+ 'base',
+ 'test_support_base',
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'sources': [
+ 'message_loop/message_pump_perftest.cc',
+ 'test/run_all_unittests.cc',
+ 'threading/thread_perftest.cc',
+ '../testing/perf/perf_test.cc'
+ ],
+ 'conditions': [
+ ['OS == "android"', {
+ 'dependencies': [
+ '../testing/android/native_test.gyp:native_test_native_code',
+ ],
+ }],
+ ],
+ },
+ {
+ # GN: //base:base_i18n_perftests
+ 'target_name': 'base_i18n_perftests',
+ 'type': '<(gtest_target_type)',
+ 'dependencies': [
+ 'test_support_base',
+ 'test_support_perf',
+ '../testing/gtest.gyp:gtest',
+ 'base_i18n',
+ 'base',
+ ],
+ 'sources': [
+ 'i18n/streaming_utf8_validator_perftest.cc',
+ ],
+ },
+ {
+ # GN: //base/test:test_support
+ 'target_name': 'test_support_base',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'base',
+ 'base_static',
+ 'base_i18n',
+ '../testing/gmock.gyp:gmock',
+ '../testing/gtest.gyp:gtest',
+ '../third_party/icu/icu.gyp:icuuc',
+ '../third_party/libxml/libxml.gyp:libxml',
+ 'third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ ],
+ 'export_dependent_settings': [
+ 'base',
+ ],
+ 'conditions': [
+ ['os_posix==0', {
+ 'sources!': [
+ 'test/scoped_locale.cc',
+ 'test/scoped_locale.h',
+ ],
+ }],
+ ['os_bsd==1', {
+ 'sources!': [
+ 'test/test_file_util_linux.cc',
+ ],
+ }],
+ ['OS == "android"', {
+ 'dependencies': [
+ 'base_unittests_jni_headers',
+ 'base_java_unittest_support',
+ ],
+ }],
+ ['OS == "ios"', {
+ 'toolsets': ['host', 'target'],
+ }],
+ ],
+ 'sources': [
+ 'test/gtest_util.cc',
+ 'test/gtest_util.h',
+ 'test/gtest_xml_unittest_result_printer.cc',
+ 'test/gtest_xml_unittest_result_printer.h',
+ 'test/gtest_xml_util.cc',
+ 'test/gtest_xml_util.h',
+ 'test/histogram_tester.cc',
+ 'test/histogram_tester.h',
+ 'test/icu_test_util.cc',
+ 'test/icu_test_util.h',
+ 'test/ios/wait_util.h',
+ 'test/ios/wait_util.mm',
+ 'test/launcher/test_launcher.cc',
+ 'test/launcher/test_launcher.h',
+ 'test/launcher/test_launcher_tracer.cc',
+ 'test/launcher/test_launcher_tracer.h',
+ 'test/launcher/test_result.cc',
+ 'test/launcher/test_result.h',
+ 'test/launcher/test_results_tracker.cc',
+ 'test/launcher/test_results_tracker.h',
+ 'test/launcher/unit_test_launcher.cc',
+ 'test/launcher/unit_test_launcher.h',
+ 'test/launcher/unit_test_launcher_ios.cc',
+ 'test/mock_chrome_application_mac.h',
+ 'test/mock_chrome_application_mac.mm',
+ 'test/mock_devices_changed_observer.cc',
+ 'test/mock_devices_changed_observer.h',
+ 'test/mock_entropy_provider.cc',
+ 'test/mock_entropy_provider.h',
+ 'test/mock_log.cc',
+ 'test/mock_log.h',
+ 'test/multiprocess_test.cc',
+ 'test/multiprocess_test.h',
+ 'test/multiprocess_test_android.cc',
+ 'test/null_task_runner.cc',
+ 'test/null_task_runner.h',
+ 'test/opaque_ref_counted.cc',
+ 'test/opaque_ref_counted.h',
+ 'test/perf_log.cc',
+ 'test/perf_log.h',
+ 'test/perf_test_suite.cc',
+ 'test/perf_test_suite.h',
+ 'test/perf_time_logger.cc',
+ 'test/perf_time_logger.h',
+ 'test/power_monitor_test_base.cc',
+ 'test/power_monitor_test_base.h',
+ 'test/scoped_command_line.cc',
+ 'test/scoped_command_line.h',
+ 'test/scoped_locale.cc',
+ 'test/scoped_locale.h',
+ 'test/scoped_path_override.cc',
+ 'test/scoped_path_override.h',
+ 'test/sequenced_task_runner_test_template.cc',
+ 'test/sequenced_task_runner_test_template.h',
+ 'test/sequenced_worker_pool_owner.cc',
+ 'test/sequenced_worker_pool_owner.h',
+ 'test/simple_test_clock.cc',
+ 'test/simple_test_clock.h',
+ 'test/simple_test_tick_clock.cc',
+ 'test/simple_test_tick_clock.h',
+ 'test/task_runner_test_template.cc',
+ 'test/task_runner_test_template.h',
+ 'test/test_discardable_memory_allocator.cc',
+ 'test/test_discardable_memory_allocator.h',
+ 'test/test_file_util.cc',
+ 'test/test_file_util.h',
+ 'test/test_file_util_android.cc',
+ 'test/test_file_util_linux.cc',
+ 'test/test_file_util_mac.cc',
+ 'test/test_file_util_posix.cc',
+ 'test/test_file_util_win.cc',
+ 'test/test_io_thread.cc',
+ 'test/test_io_thread.h',
+ 'test/test_listener_ios.h',
+ 'test/test_listener_ios.mm',
+ 'test/test_message_loop.cc',
+ 'test/test_message_loop.h',
+ 'test/test_mock_time_task_runner.cc',
+ 'test/test_mock_time_task_runner.h',
+ 'test/test_pending_task.cc',
+ 'test/test_pending_task.h',
+ 'test/test_reg_util_win.cc',
+ 'test/test_reg_util_win.h',
+ 'test/test_shortcut_win.cc',
+ 'test/test_shortcut_win.h',
+ 'test/test_simple_task_runner.cc',
+ 'test/test_simple_task_runner.h',
+ 'test/test_suite.cc',
+ 'test/test_suite.h',
+ 'test/test_support_android.cc',
+ 'test/test_support_android.h',
+ 'test/test_support_ios.h',
+ 'test/test_support_ios.mm',
+ 'test/test_switches.cc',
+ 'test/test_switches.h',
+ 'test/test_timeouts.cc',
+ 'test/test_timeouts.h',
+ 'test/test_ui_thread_android.cc',
+ 'test/test_ui_thread_android.h',
+ 'test/thread_test_helper.cc',
+ 'test/thread_test_helper.h',
+ 'test/trace_event_analyzer.cc',
+ 'test/trace_event_analyzer.h',
+ 'test/trace_to_file.cc',
+ 'test/trace_to_file.h',
+ 'test/user_action_tester.cc',
+ 'test/user_action_tester.h',
+ 'test/values_test_util.cc',
+ 'test/values_test_util.h',
+ ],
+ 'target_conditions': [
+ ['OS == "ios"', {
+ 'sources/': [
+ # Pull in specific Mac files for iOS (which have been filtered out
+ # by file name rules).
+ ['include', '^test/test_file_util_mac\\.cc$'],
+ ],
+ }],
+ ['OS == "ios" and _toolset == "target"', {
+ 'sources!': [
+ # iOS uses its own unit test launcher.
+ 'test/launcher/unit_test_launcher.cc',
+ ],
+ }],
+ ['OS == "ios" and _toolset == "host"', {
+ 'sources!': [
+ 'test/launcher/unit_test_launcher_ios.cc',
+ 'test/test_support_ios.h',
+ 'test/test_support_ios.mm',
+ ],
+ }],
+ ], # target_conditions
+ },
+ {
+ 'target_name': 'test_support_perf',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'base',
+ 'test_support_base',
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'sources': [
+ 'test/run_all_perftests.cc',
+ ],
+ 'direct_dependent_settings': {
+ 'defines': [
+ 'PERF_TEST',
+ ],
+ },
+ },
+ {
+ 'target_name': 'test_launcher_nacl_nonsfi',
+ 'conditions': [
+ ['disable_nacl==0 and disable_nacl_untrusted==0 and enable_nacl_nonsfi_test==1', {
+ 'type': 'static_library',
+ 'sources': [
+ 'test/launcher/test_launcher_nacl_nonsfi.cc',
+ ],
+ 'dependencies': [
+ 'test_support_base',
+ ],
+ }, {
+ 'type': 'none',
+ }],
+ ],
+ },
+ {
+ # GN version: //base/debug:debugging_flags
+ # Since this generates a file, it must only be referenced in the target
+ # toolchain or there will be multiple rules that generate the header.
+ # When referenced from a target that might be compiled in the host
+ # toolchain, always refer to 'base_debugging_flags#target'.
+ 'target_name': 'base_debugging_flags',
+ 'includes': [ '../build/buildflag_header.gypi' ],
+ 'variables': {
+ 'buildflag_header_path': 'base/debug/debugging_flags.h',
+ 'buildflag_flags': [
+ 'ENABLE_PROFILING=<(profiling)',
+ ],
+ },
+ },
+ {
+ # GN version: //base/win:base_win_features
+ # Since this generates a file, it must only be referenced in the target
+ # toolchain or there will be multiple rules that generate the header.
+ # When referenced from a target that might be compiled in the host
+ # toolchain, always refer to 'base_win_features#target'.
+ 'target_name': 'base_win_features',
+ 'conditions': [
+ ['OS=="win"', {
+ 'includes': [ '../build/buildflag_header.gypi' ],
+ 'variables': {
+ 'buildflag_header_path': 'base/win/base_features.h',
+ 'buildflag_flags': [
+ 'SINGLE_MODULE_MODE_HANDLE_VERIFIER=<(single_module_mode_handle_verifier)',
+ ],
+ },
+ }, {
+ 'type': 'none',
+ }],
+ ],
+ },
+ {
+ 'type': 'none',
+ 'target_name': 'base_build_date',
+ 'hard_dependency': 1,
+ 'actions': [{
+ 'action_name': 'generate_build_date_headers',
+ 'inputs': [
+ '<(DEPTH)/build/write_build_date_header.py',
+ '<(DEPTH)/build/util/LASTCHANGE'
+ ],
+ 'outputs': [ '<(SHARED_INTERMEDIATE_DIR)/base/generated_build_date.h' ],
+ 'action': [
+ 'python', '<(DEPTH)/build/write_build_date_header.py',
+ '<(SHARED_INTERMEDIATE_DIR)/base/generated_build_date.h',
+ '<(build_type)'
+ ]
+ }],
+ 'conditions': [
+ [ 'buildtype == "Official"', {
+ 'variables': {
+ 'build_type': 'official'
+ }
+ }, {
+ 'variables': {
+ 'build_type': 'default'
+ }
+ }],
+ ]
+ },
+ ],
+ 'conditions': [
+ ['OS=="ios" and "<(GENERATOR)"=="ninja"', {
+ 'targets': [
+ {
+ 'target_name': 'test_launcher',
+ 'toolsets': ['host'],
+ 'type': 'executable',
+ 'dependencies': [
+ 'test_support_base',
+ ],
+ 'sources': [
+ 'test/launcher/test_launcher_ios.cc',
+ ],
+ },
+ ],
+ }],
+ ['OS!="ios"', {
+ 'targets': [
+ {
+ # GN: //base:check_example
+ 'target_name': 'check_example',
+ 'type': 'executable',
+ 'sources': [
+ 'check_example.cc',
+ ],
+ 'dependencies': [
+ 'base',
+ ],
+ },
+ {
+ 'target_name': 'build_utf8_validator_tables',
+ 'type': 'executable',
+ 'toolsets': ['host'],
+ 'dependencies': [
+ 'base',
+ '../third_party/icu/icu.gyp:icuuc',
+ ],
+ 'sources': [
+ 'i18n/build_utf8_validator_tables.cc'
+ ],
+ },
+ ],
+ }],
+ ['OS == "win" and target_arch=="ia32"', {
+ 'targets': [
+ # The base_win64 target here allows us to use base for Win64 targets
+ # (the normal build is 32 bits).
+ {
+ 'target_name': 'base_win64',
+ 'type': '<(component)',
+ 'variables': {
+ 'base_target': 1,
+ },
+ 'dependencies': [
+ 'base_build_date',
+ 'base_debugging_flags#target',
+ 'base_static_win64',
+ '../third_party/modp_b64/modp_b64.gyp:modp_b64_win64',
+ 'third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations_win64',
+ 'trace_event/etw_manifest/etw_manifest.gyp:etw_manifest',
+ ],
+ # TODO(gregoryd): direct_dependent_settings should be shared with the
+ # 32-bit target, but it doesn't work due to a bug in gyp
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ 'defines': [
+ 'BASE_WIN64',
+ '<@(nacl_win64_defines)',
+ ],
+ 'configurations': {
+ 'Common_Base': {
+ 'msvs_target_platform': 'x64',
+ },
+ },
+ 'conditions': [
+ ['component == "shared_library"', {
+ 'sources!': [
+ 'debug/debug_on_start_win.cc',
+ ],
+ }],
+ ],
+ # Specify delayload for base_win64.dll.
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'DelayLoadDLLs': [
+ 'cfgmgr32.dll',
+ 'powrprof.dll',
+ 'setupapi.dll',
+ ],
+ 'AdditionalDependencies': [
+ 'cfgmgr32.lib',
+ 'powrprof.lib',
+ 'setupapi.lib',
+ 'userenv.lib',
+ 'winmm.lib',
+ ],
+ },
+ },
+ # Specify delayload for components that link with base_win64.lib.
+ 'all_dependent_settings': {
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'DelayLoadDLLs': [
+ 'cfgmgr32.dll',
+ 'powrprof.dll',
+ 'setupapi.dll',
+ ],
+ 'AdditionalDependencies': [
+ 'cfgmgr32.lib',
+ 'powrprof.lib',
+ 'setupapi.lib',
+ 'userenv.lib',
+ 'winmm.lib',
+ ],
+ },
+ },
+ },
+ # TODO(rvargas): Bug 78117. Remove this.
+ 'msvs_disabled_warnings': [
+ 4244,
+ 4996,
+ 4267,
+ ],
+ 'sources': [
+ 'auto_reset.h',
+ 'linux_util.cc',
+ 'linux_util.h',
+ 'md5.cc',
+ 'md5.h',
+ 'message_loop/message_pump_libevent.cc',
+ 'message_loop/message_pump_libevent.h',
+ 'metrics/field_trial.cc',
+ 'metrics/field_trial.h',
+ 'posix/file_descriptor_shuffle.cc',
+ 'posix/file_descriptor_shuffle.h',
+ 'sync_socket.h',
+ 'sync_socket_posix.cc',
+ 'sync_socket_win.cc',
+ 'third_party/xdg_user_dirs/xdg_user_dir_lookup.cc',
+ 'third_party/xdg_user_dirs/xdg_user_dir_lookup.h',
+ ],
+ },
+ {
+ 'target_name': 'base_i18n_nacl_win64',
+ 'type': '<(component)',
+ # TODO(gregoryd): direct_dependent_settings should be shared with the
+ # 32-bit target, but it doesn't work due to a bug in gyp
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ 'defines': [
+ '<@(nacl_win64_defines)',
+ 'BASE_I18N_IMPLEMENTATION',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'i18n/icu_util_nacl_win64.cc',
+ ],
+ 'configurations': {
+ 'Common_Base': {
+ 'msvs_target_platform': 'x64',
+ },
+ },
+ },
+ {
+ # TODO(rvargas): Remove this when gyp finally supports a clean model.
+ # See bug 36232.
+ 'target_name': 'base_static_win64',
+ 'type': 'static_library',
+ 'sources': [
+ 'base_switches.cc',
+ 'base_switches.h',
+ 'win/pe_image.cc',
+ 'win/pe_image.h',
+ ],
+ 'sources!': [
+ # base64.cc depends on modp_b64.
+ 'base64.cc',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'configurations': {
+ 'Common_Base': {
+ 'msvs_target_platform': 'x64',
+ },
+ },
+ 'defines': [
+ '<@(nacl_win64_defines)',
+ ],
+ # TODO(rvargas): Bug 78117. Remove this.
+ 'msvs_disabled_warnings': [
+ 4244,
+ ],
+ },
+ ],
+ }],
+ ['OS == "win" and target_arch=="x64"', {
+ 'targets': [
+ {
+ 'target_name': 'base_profiler_test_support_library',
+ # Must be a shared library so that it can be unloaded during testing.
+ 'type': 'shared_library',
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'profiler/test_support_library.cc',
+ ],
+ },
+ ]
+ }],
+ ['os_posix==1 and OS!="mac" and OS!="ios"', {
+ 'targets': [
+ {
+ 'target_name': 'symbolize',
+ 'type': 'static_library',
+ 'toolsets': ['host', 'target'],
+ 'variables': {
+ 'chromium_code': 0,
+ },
+ 'conditions': [
+ ['OS == "solaris"', {
+ 'include_dirs': [
+ '/usr/gnu/include',
+ '/usr/gnu/include/libelf',
+ ],
+ },],
+ ],
+ 'cflags': [
+ '-Wno-sign-compare',
+ ],
+ 'cflags!': [
+ '-Wextra',
+ ],
+ 'defines': [
+ 'GLOG_BUILD_CONFIG_INCLUDE="build/build_config.h"',
+ ],
+ 'sources': [
+ 'third_party/symbolize/config.h',
+ 'third_party/symbolize/demangle.cc',
+ 'third_party/symbolize/demangle.h',
+ 'third_party/symbolize/glog/logging.h',
+ 'third_party/symbolize/glog/raw_logging.h',
+ 'third_party/symbolize/symbolize.cc',
+ 'third_party/symbolize/symbolize.h',
+ 'third_party/symbolize/utilities.h',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'includes': [
+ '../build/android/increase_size_for_speed.gypi',
+ ],
+ },
+ {
+ 'target_name': 'xdg_mime',
+ 'type': 'static_library',
+ 'toolsets': ['host', 'target'],
+ 'variables': {
+ 'chromium_code': 0,
+ },
+ 'cflags!': [
+ '-Wextra',
+ ],
+ 'sources': [
+ 'third_party/xdg_mime/xdgmime.c',
+ 'third_party/xdg_mime/xdgmime.h',
+ 'third_party/xdg_mime/xdgmimealias.c',
+ 'third_party/xdg_mime/xdgmimealias.h',
+ 'third_party/xdg_mime/xdgmimecache.c',
+ 'third_party/xdg_mime/xdgmimecache.h',
+ 'third_party/xdg_mime/xdgmimeglob.c',
+ 'third_party/xdg_mime/xdgmimeglob.h',
+ 'third_party/xdg_mime/xdgmimeicon.c',
+ 'third_party/xdg_mime/xdgmimeicon.h',
+ 'third_party/xdg_mime/xdgmimeint.c',
+ 'third_party/xdg_mime/xdgmimeint.h',
+ 'third_party/xdg_mime/xdgmimemagic.c',
+ 'third_party/xdg_mime/xdgmimemagic.h',
+ 'third_party/xdg_mime/xdgmimeparent.c',
+ 'third_party/xdg_mime/xdgmimeparent.h',
+ ],
+ 'includes': [
+ '../build/android/increase_size_for_speed.gypi',
+ ],
+ },
+ ],
+ }],
+ ['OS == "linux"', {
+ 'targets': [
+ {
+ 'target_name': 'malloc_wrapper',
+ 'type': 'shared_library',
+ 'dependencies': [
+ 'base',
+ ],
+ 'sources': [
+ 'test/malloc_wrapper.cc',
+ ],
+ }
+ ],
+ }],
+ ['OS == "android"', {
+ 'targets': [
+ {
+ # GN: //base:base_jni_headers
+ 'target_name': 'base_jni_headers',
+ 'type': 'none',
+ 'sources': [
+ 'android/java/src/org/chromium/base/ApkAssets.java',
+ 'android/java/src/org/chromium/base/ApplicationStatus.java',
+ 'android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java',
+ 'android/java/src/org/chromium/base/BuildInfo.java',
+ 'android/java/src/org/chromium/base/Callback.java',
+ 'android/java/src/org/chromium/base/CommandLine.java',
+ 'android/java/src/org/chromium/base/ContentUriUtils.java',
+ 'android/java/src/org/chromium/base/ContextUtils.java',
+ 'android/java/src/org/chromium/base/CpuFeatures.java',
+ 'android/java/src/org/chromium/base/EventLog.java',
+ 'android/java/src/org/chromium/base/FieldTrialList.java',
+ 'android/java/src/org/chromium/base/ImportantFileWriterAndroid.java',
+ 'android/java/src/org/chromium/base/JNIUtils.java',
+ 'android/java/src/org/chromium/base/JavaHandlerThread.java',
+ 'android/java/src/org/chromium/base/LocaleUtils.java',
+ 'android/java/src/org/chromium/base/MemoryPressureListener.java',
+ 'android/java/src/org/chromium/base/PathService.java',
+ 'android/java/src/org/chromium/base/PathUtils.java',
+ 'android/java/src/org/chromium/base/PowerMonitor.java',
+ 'android/java/src/org/chromium/base/SysUtils.java',
+ 'android/java/src/org/chromium/base/SystemMessageHandler.java',
+ 'android/java/src/org/chromium/base/ThreadUtils.java',
+ 'android/java/src/org/chromium/base/TraceEvent.java',
+ 'android/java/src/org/chromium/base/library_loader/LibraryLoader.java',
+ 'android/java/src/org/chromium/base/metrics/RecordHistogram.java',
+ 'android/java/src/org/chromium/base/metrics/RecordUserAction.java',
+ ],
+ 'variables': {
+ 'jni_gen_package': 'base',
+ },
+ 'dependencies': [
+ 'android_runtime_jni_headers',
+ ],
+ 'includes': [ '../build/jni_generator.gypi' ],
+ },
+ {
+ # GN: //base:android_runtime_jni_headers
+ 'target_name': 'android_runtime_jni_headers',
+ 'type': 'none',
+ 'variables': {
+ 'jni_gen_package': 'base',
+ 'input_java_class': 'java/lang/Runtime.class',
+ },
+ 'includes': [ '../build/jar_file_jni_generator.gypi' ],
+ },
+ {
+ # GN: //base:base_unittests_jni_headers
+ 'target_name': 'base_unittests_jni_headers',
+ 'type': 'none',
+ 'sources': [
+ 'test/android/java/src/org/chromium/base/ContentUriTestUtils.java',
+ 'test/android/java/src/org/chromium/base/TestUiThread.java',
+ ],
+ 'variables': {
+ 'jni_gen_package': 'base',
+ },
+ 'includes': [ '../build/jni_generator.gypi' ],
+ },
+ {
+ # GN: //base:base_native_libraries_gen
+ 'target_name': 'base_native_libraries_gen',
+ 'type': 'none',
+ 'sources': [
+ 'android/java/templates/NativeLibraries.template',
+ ],
+ 'variables': {
+ 'package_name': 'org/chromium/base/library_loader',
+ 'template_deps': [],
+ },
+ 'includes': [ '../build/android/java_cpp_template.gypi' ],
+ },
+ {
+ # GN: //base:base_build_config_gen
+ 'target_name': 'base_build_config_gen',
+ 'type': 'none',
+ 'sources': [
+ 'android/java/templates/BuildConfig.template',
+ ],
+ 'variables': {
+ 'package_name': 'org/chromium/base',
+ 'template_deps': [],
+ },
+ 'includes': ['../build/android/java_cpp_template.gypi'],
+ },
+ {
+ # GN: //base:base_android_java_enums_srcjar
+ 'target_name': 'base_java_library_process_type',
+ 'type': 'none',
+ 'variables': {
+ 'source_file': 'android/library_loader/library_loader_hooks.h',
+ },
+ 'includes': [ '../build/android/java_cpp_enum.gypi' ],
+ },
+ {
+ # GN: //base:base_java
+ 'target_name': 'base_java',
+ 'type': 'none',
+ 'variables': {
+ 'java_in_dir': 'android/java',
+ 'jar_excluded_classes': [
+ '*/BuildConfig.class',
+ '*/NativeLibraries.class',
+ ],
+ },
+ 'dependencies': [
+ 'base_java_application_state',
+ 'base_java_library_load_from_apk_status_codes',
+ 'base_java_library_process_type',
+ 'base_java_memory_pressure_level',
+ 'base_build_config_gen',
+ 'base_native_libraries_gen',
+ '../third_party/android_tools/android_tools.gyp:android_support_multidex_javalib',
+ '../third_party/jsr-305/jsr-305.gyp:jsr_305_javalib',
+ ],
+ 'all_dependent_settings': {
+ 'variables': {
+ 'generate_build_config': 1,
+ },
+ },
+ 'includes': [ '../build/java.gypi' ],
+ },
+ {
+ # GN: //base:base_java_unittest_support
+ 'target_name': 'base_java_unittest_support',
+ 'type': 'none',
+ 'dependencies': [
+ 'base_java',
+ ],
+ 'variables': {
+ 'java_in_dir': '../base/test/android/java',
+ },
+ 'includes': [ '../build/java.gypi' ],
+ },
+ {
+ # GN: //base:base_android_java_enums_srcjar
+ 'target_name': 'base_java_application_state',
+ 'type': 'none',
+ 'variables': {
+ 'source_file': 'android/application_status_listener.h',
+ },
+ 'includes': [ '../build/android/java_cpp_enum.gypi' ],
+ },
+ {
+ # GN: //base:base_android_java_enums_srcjar
+ 'target_name': 'base_java_library_load_from_apk_status_codes',
+ 'type': 'none',
+ 'variables': {
+ 'source_file': 'android/library_loader/library_load_from_apk_status_codes.h'
+ },
+ 'includes': [ '../build/android/java_cpp_enum.gypi' ],
+ },
+ {
+ # GN: //base:base_android_java_enums_srcjar
+ 'target_name': 'base_java_memory_pressure_level',
+ 'type': 'none',
+ 'variables': {
+ 'source_file': 'memory/memory_pressure_listener.h',
+ },
+ 'includes': [ '../build/android/java_cpp_enum.gypi' ],
+ },
+ {
+ # GN: //base:base_java_test_support
+ 'target_name': 'base_java_test_support',
+ 'type': 'none',
+ 'dependencies': [
+ 'base_java',
+ '../testing/android/on_device_instrumentation.gyp:reporter_java',
+ ],
+ 'variables': {
+ 'java_in_dir': '../base/test/android/javatests',
+ },
+ 'includes': [ '../build/java.gypi' ],
+ },
+ {
+ # TODO(jbudorick): Remove this once we roll to robolectric 3.0 and pull
+ # in the multidex shadow library. crbug.com/522043
+ # GN: //base:base_junit_test_support
+ 'target_name': 'base_junit_test_support',
+ 'type': 'none',
+ 'dependencies': [
+ 'base_build_config_gen',
+ '../testing/android/junit/junit_test.gyp:junit_test_support',
+ '../third_party/android_tools/android_tools.gyp:android_support_multidex_javalib',
+ ],
+ 'variables': {
+ 'src_paths': [
+ '../base/test/android/junit/src/org/chromium/base/test/shadows/ShadowMultiDex.java',
+ ],
+ },
+ 'includes': [ '../build/host_jar.gypi' ]
+ },
+ {
+ # GN: //base:base_junit_tests
+ 'target_name': 'base_junit_tests',
+ 'type': 'none',
+ 'dependencies': [
+ 'base_java',
+ 'base_java_test_support',
+ 'base_junit_test_support',
+ '../testing/android/junit/junit_test.gyp:junit_test_support',
+ ],
+ 'variables': {
+ 'main_class': 'org.chromium.testing.local.JunitTestMain',
+ 'src_paths': [
+ '../base/android/junit/',
+ '../base/test/android/junit/src/org/chromium/base/test/util/DisableIfTest.java',
+ '../base/test/android/junit/src/org/chromium/base/test/util/MinAndroidSdkLevelSkipCheckTest.java',
+ '../base/test/android/junit/src/org/chromium/base/test/util/RestrictionSkipCheckTest.java',
+ '../base/test/android/junit/src/org/chromium/base/test/util/SkipCheckTest.java',
+ ],
+ 'test_type': 'junit',
+ 'wrapper_script_name': 'helper/<(_target_name)',
+ },
+ 'includes': [
+ '../build/android/test_runner.gypi',
+ '../build/host_jar.gypi',
+ ],
+ },
+ {
+ # GN: //base:base_javatests
+ 'target_name': 'base_javatests',
+ 'type': 'none',
+ 'dependencies': [
+ 'base_java',
+ 'base_java_test_support',
+ ],
+ 'variables': {
+ 'java_in_dir': '../base/android/javatests',
+ },
+ 'includes': [ '../build/java.gypi' ],
+ },
+ {
+ # GN: //base/android/linker:chromium_android_linker
+ 'target_name': 'chromium_android_linker',
+ 'type': 'shared_library',
+ 'sources': [
+ 'android/linker/android_dlext.h',
+ 'android/linker/legacy_linker_jni.cc',
+ 'android/linker/legacy_linker_jni.h',
+ 'android/linker/linker_jni.cc',
+ 'android/linker/linker_jni.h',
+ 'android/linker/modern_linker_jni.cc',
+ 'android/linker/modern_linker_jni.h',
+ ],
+ # The crazy linker is never instrumented.
+ 'cflags!': [
+ '-finstrument-functions',
+ ],
+ 'dependencies': [
+ # The NDK contains the crazy_linker here:
+ # '<(android_ndk_root)/crazy_linker.gyp:crazy_linker'
+ # However, we use our own fork. See bug 384700.
+ '../third_party/android_crazy_linker/crazy_linker.gyp:crazy_linker',
+ ],
+ },
+ {
+ # GN: //base:base_perftests_apk
+ 'target_name': 'base_perftests_apk',
+ 'type': 'none',
+ 'dependencies': [
+ 'base_perftests',
+ ],
+ 'variables': {
+ 'test_suite_name': 'base_perftests',
+ },
+ 'includes': [ '../build/apk_test.gypi' ],
+ },
+ {
+ # GN: //base:base_unittests_apk
+ 'target_name': 'base_unittests_apk',
+ 'type': 'none',
+ 'dependencies': [
+ 'base_java',
+ 'base_unittests',
+ ],
+ 'variables': {
+ 'test_suite_name': 'base_unittests',
+ 'isolate_file': 'base_unittests.isolate',
+ },
+ 'includes': [ '../build/apk_test.gypi' ],
+ },
+ ],
+ 'conditions': [
+ ['test_isolation_mode != "noop"',
+ {
+ 'targets': [
+ {
+ 'target_name': 'base_unittests_apk_run',
+ 'type': 'none',
+ 'dependencies': [
+ 'base_unittests_apk',
+ ],
+ 'includes': [
+ '../build/isolate.gypi',
+ ],
+ 'sources': [
+ 'base_unittests_apk.isolate',
+ ],
+ },
+ ]
+ }
+ ],
+ ],
+ }],
+ ['OS == "win"', {
+ 'targets': [
+ {
+ # Target to manually rebuild pe_image_test.dll which is checked into
+ # base/test/data/pe_image.
+ 'target_name': 'pe_image_test',
+ 'type': 'shared_library',
+ 'sources': [
+ 'win/pe_image_test.cc',
+ ],
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'SubSystem': '2', # Set /SUBSYSTEM:WINDOWS
+ 'DelayLoadDLLs': [
+ 'cfgmgr32.dll',
+ 'shell32.dll',
+ ],
+ 'AdditionalDependencies': [
+ 'cfgmgr32.lib',
+ 'shell32.lib',
+ ],
+ },
+ },
+ },
+ {
+ 'target_name': 'scoped_handle_test_dll',
+ 'type': 'loadable_module',
+ 'dependencies': [
+ 'base',
+ ],
+ 'sources': [
+ 'win/scoped_handle_test_dll.cc',
+ ],
+ },
+ ],
+ }],
+ ['test_isolation_mode != "noop"', {
+ 'targets': [
+ {
+ 'target_name': 'base_unittests_run',
+ 'type': 'none',
+ 'dependencies': [
+ 'base_unittests',
+ ],
+ 'includes': [
+ '../build/isolate.gypi',
+ ],
+ 'sources': [
+ 'base_unittests.isolate',
+ ],
+ },
+ ],
+ }],
+ ['OS == "ios" or OS == "mac"', {
+ 'targets': [
+ {
+ 'target_name': 'base_unittests_arc',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'base',
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'sources': [
+ 'mac/bind_objc_block_unittest_arc.mm',
+ 'mac/scoped_nsobject_unittest_arc.mm'
+ ],
+ 'xcode_settings': {
+ 'CLANG_ENABLE_OBJC_ARC': 'YES',
+ },
+ 'target_conditions': [
+ ['OS == "ios" and _toolset != "host"', {
+ 'sources/': [
+ ['include', 'mac/bind_objc_block_unittest_arc\\.mm$'],
+ ['include', 'mac/scoped_nsobject_unittest_arc\\.mm$'],
+ ],
+ }]
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/base/base.gypi b/base/base.gypi
new file mode 100644
index 0000000000..cb41e79310
--- /dev/null
+++ b/base/base.gypi
@@ -0,0 +1,1106 @@
+# 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.
+
+{
+ 'includes': [
+ 'trace_event/trace_event.gypi',
+ ],
+ 'target_defaults': {
+ 'variables': {
+ 'base_target': 0,
+ 'base_i18n_target': 0,
+ },
+ 'target_conditions': [
+ # This part is shared between the targets defined below.
+ ['base_target==1', {
+ 'sources': [
+ '../build/build_config.h',
+ 'allocator/allocator_check.cc',
+ 'allocator/allocator_check.h',
+ 'allocator/allocator_extension.cc',
+ 'allocator/allocator_extension.h',
+ 'android/animation_frame_time_histogram.cc',
+ 'android/animation_frame_time_histogram.h',
+ 'android/apk_assets.cc',
+ 'android/apk_assets.h',
+ 'android/application_status_listener.cc',
+ 'android/application_status_listener.h',
+ 'android/base_jni_onload.cc',
+ 'android/base_jni_onload.h',
+ 'android/base_jni_registrar.cc',
+ 'android/base_jni_registrar.h',
+ 'android/build_info.cc',
+ 'android/build_info.h',
+ 'android/callback_android.cc',
+ 'android/callback_android.h',
+ 'android/command_line_android.cc',
+ 'android/command_line_android.h',
+ 'android/content_uri_utils.cc',
+ 'android/content_uri_utils.h',
+ 'android/context_utils.cc',
+ 'android/context_utils.h',
+ 'android/cpu_features.cc',
+ 'android/cxa_demangle_stub.cc',
+ 'android/event_log.cc',
+ 'android/event_log.h',
+ 'android/field_trial_list.cc',
+ 'android/field_trial_list.h',
+ 'android/fifo_utils.cc',
+ 'android/fifo_utils.h',
+ 'android/important_file_writer_android.cc',
+ 'android/important_file_writer_android.h',
+ 'android/java_handler_thread.cc',
+ 'android/java_handler_thread.h',
+ 'android/java_runtime.cc',
+ 'android/java_runtime.h',
+ 'android/jni_android.cc',
+ 'android/jni_android.h',
+ 'android/jni_array.cc',
+ 'android/jni_array.h',
+ 'android/jni_registrar.cc',
+ 'android/jni_registrar.h',
+ 'android/jni_string.cc',
+ 'android/jni_string.h',
+ 'android/jni_utils.cc',
+ 'android/jni_utils.h',
+ 'android/jni_weak_ref.cc',
+ 'android/jni_weak_ref.h',
+ 'android/library_loader/library_load_from_apk_status_codes.h',
+ 'android/library_loader/library_loader_hooks.cc',
+ 'android/library_loader/library_loader_hooks.h',
+ 'android/library_loader/library_prefetcher.cc',
+ 'android/library_loader/library_prefetcher.h',
+ 'android/locale_utils.cc',
+ 'android/locale_utils.h',
+ 'android/memory_pressure_listener_android.cc',
+ 'android/memory_pressure_listener_android.h',
+ 'android/path_service_android.cc',
+ 'android/path_service_android.h',
+ 'android/path_utils.cc',
+ 'android/path_utils.h',
+ 'android/record_histogram.cc',
+ 'android/record_histogram.h',
+ 'android/record_user_action.cc',
+ 'android/record_user_action.h',
+ 'android/scoped_java_ref.cc',
+ 'android/scoped_java_ref.h',
+ 'android/sys_utils.cc',
+ 'android/sys_utils.h',
+ 'android/thread_utils.h',
+ 'android/trace_event_binding.cc',
+ 'android/trace_event_binding.h',
+ 'at_exit.cc',
+ 'at_exit.h',
+ 'atomic_ref_count.h',
+ 'atomic_sequence_num.h',
+ 'atomicops.h',
+ 'atomicops_internals_portable.h',
+ 'atomicops_internals_x86_msvc.h',
+ 'barrier_closure.cc',
+ 'barrier_closure.h',
+ 'base64.cc',
+ 'base64.h',
+ 'base64url.cc',
+ 'base64url.h',
+ 'base_export.h',
+ 'base_paths.cc',
+ 'base_paths.h',
+ 'base_paths_android.cc',
+ 'base_paths_android.h',
+ 'base_paths_mac.h',
+ 'base_paths_mac.mm',
+ 'base_paths_posix.cc',
+ 'base_paths_posix.h',
+ 'base_paths_win.cc',
+ 'base_paths_win.h',
+ 'base_switches.h',
+ 'big_endian.cc',
+ 'big_endian.h',
+ 'bind.h',
+ 'bind_helpers.cc',
+ 'bind_helpers.h',
+ 'bind_internal.h',
+ 'bit_cast.h',
+ 'bits.h',
+ 'build_time.cc',
+ 'build_time.h',
+ 'callback.h',
+ 'callback_helpers.cc',
+ 'callback_helpers.h',
+ 'callback_internal.cc',
+ 'callback_internal.h',
+ 'callback_list.h',
+ 'cancelable_callback.h',
+ 'command_line.cc',
+ 'command_line.h',
+ 'compiler_specific.h',
+ 'containers/adapters.h',
+ 'containers/hash_tables.h',
+ 'containers/linked_list.h',
+ 'containers/mru_cache.h',
+ 'containers/scoped_ptr_hash_map.h',
+ 'containers/small_map.h',
+ 'containers/stack_container.h',
+ 'cpu.cc',
+ 'cpu.h',
+ 'critical_closure.h',
+ 'critical_closure_internal_ios.mm',
+ 'debug/alias.cc',
+ 'debug/alias.h',
+ 'debug/asan_invalid_access.cc',
+ 'debug/asan_invalid_access.h',
+ 'debug/close_handle_hook_win.cc',
+ 'debug/close_handle_hook_win.h',
+ 'debug/crash_logging.cc',
+ 'debug/crash_logging.h',
+ 'debug/debugger.cc',
+ 'debug/debugger.h',
+ 'debug/debugger_posix.cc',
+ 'debug/debugger_win.cc',
+ 'debug/dump_without_crashing.cc',
+ 'debug/dump_without_crashing.h',
+ 'debug/gdi_debug_util_win.cc',
+ 'debug/gdi_debug_util_win.h',
+ # This file depends on files from the 'allocator' target,
+ # but this target does not depend on 'allocator' (see
+ # allocator.gyp for details).
+ 'debug/leak_annotations.h',
+ 'debug/leak_tracker.h',
+ 'debug/proc_maps_linux.cc',
+ 'debug/proc_maps_linux.h',
+ 'debug/profiler.cc',
+ 'debug/profiler.h',
+ 'debug/stack_trace.cc',
+ 'debug/stack_trace.h',
+ 'debug/stack_trace_android.cc',
+ 'debug/stack_trace_posix.cc',
+ 'debug/stack_trace_win.cc',
+ 'debug/task_annotator.cc',
+ 'debug/task_annotator.h',
+ 'deferred_sequenced_task_runner.cc',
+ 'deferred_sequenced_task_runner.h',
+ 'environment.cc',
+ 'environment.h',
+ 'feature_list.cc',
+ 'feature_list.h',
+ 'file_descriptor_posix.h',
+ 'file_version_info.h',
+ 'file_version_info_mac.h',
+ 'file_version_info_mac.mm',
+ 'file_version_info_win.cc',
+ 'file_version_info_win.h',
+ 'files/dir_reader_fallback.h',
+ 'files/dir_reader_linux.h',
+ 'files/dir_reader_posix.h',
+ 'files/file.cc',
+ 'files/file.h',
+ 'files/file_enumerator.cc',
+ 'files/file_enumerator.h',
+ 'files/file_enumerator_posix.cc',
+ 'files/file_enumerator_win.cc',
+ 'files/file_path.cc',
+ 'files/file_path.h',
+ 'files/file_path_constants.cc',
+ 'files/file_path_watcher.cc',
+ 'files/file_path_watcher.h',
+ 'files/file_path_watcher_fsevents.cc',
+ 'files/file_path_watcher_fsevents.h',
+ 'files/file_path_watcher_kqueue.cc',
+ 'files/file_path_watcher_kqueue.h',
+ 'files/file_path_watcher_linux.cc',
+ 'files/file_path_watcher_mac.cc',
+ 'files/file_path_watcher_stub.cc',
+ 'files/file_path_watcher_win.cc',
+ 'files/file_posix.cc',
+ 'files/file_proxy.cc',
+ 'files/file_proxy.h',
+ 'files/file_tracing.cc',
+ 'files/file_tracing.h',
+ 'files/file_util.cc',
+ 'files/file_util.h',
+ 'files/file_util_android.cc',
+ 'files/file_util_linux.cc',
+ 'files/file_util_mac.mm',
+ 'files/file_util_posix.cc',
+ 'files/file_util_proxy.cc',
+ 'files/file_util_proxy.h',
+ 'files/file_util_win.cc',
+ 'files/file_win.cc',
+ 'files/important_file_writer.cc',
+ 'files/important_file_writer.h',
+ 'files/memory_mapped_file.cc',
+ 'files/memory_mapped_file.h',
+ 'files/memory_mapped_file_posix.cc',
+ 'files/memory_mapped_file_win.cc',
+ 'files/scoped_file.cc',
+ 'files/scoped_file.h',
+ 'files/scoped_temp_dir.cc',
+ 'files/scoped_temp_dir.h',
+ 'format_macros.h',
+ 'gtest_prod_util.h',
+ 'guid.cc',
+ 'guid.h',
+ 'hash.cc',
+ 'hash.h',
+ 'id_map.h',
+ 'ios/block_types.h',
+ 'ios/crb_protocol_observers.h',
+ 'ios/crb_protocol_observers.mm',
+ 'ios/device_util.h',
+ 'ios/device_util.mm',
+ 'ios/ios_util.h',
+ 'ios/ios_util.mm',
+ 'ios/ns_error_util.h',
+ 'ios/ns_error_util.mm',
+ 'ios/scoped_critical_action.h',
+ 'ios/scoped_critical_action.mm',
+ 'ios/weak_nsobject.h',
+ 'ios/weak_nsobject.mm',
+ 'json/json_file_value_serializer.cc',
+ 'json/json_file_value_serializer.h',
+ 'json/json_parser.cc',
+ 'json/json_parser.h',
+ 'json/json_reader.cc',
+ 'json/json_reader.h',
+ 'json/json_string_value_serializer.cc',
+ 'json/json_string_value_serializer.h',
+ 'json/json_value_converter.cc',
+ 'json/json_value_converter.h',
+ 'json/json_writer.cc',
+ 'json/json_writer.h',
+ 'json/string_escape.cc',
+ 'json/string_escape.h',
+ 'lazy_instance.cc',
+ 'lazy_instance.h',
+ 'location.cc',
+ 'location.h',
+ 'logging.cc',
+ 'logging.h',
+ 'logging_win.cc',
+ 'logging_win.h',
+ 'mac/authorization_util.h',
+ 'mac/authorization_util.mm',
+ 'mac/bind_objc_block.h',
+ 'mac/bundle_locations.h',
+ 'mac/bundle_locations.mm',
+ 'mac/call_with_eh_frame.cc',
+ 'mac/call_with_eh_frame.h',
+ 'mac/call_with_eh_frame_asm.S',
+ 'mac/close_nocancel.cc',
+ 'mac/cocoa_protocols.h',
+ 'mac/dispatch_source_mach.cc',
+ 'mac/dispatch_source_mach.h',
+ 'mac/foundation_util.h',
+ 'mac/foundation_util.mm',
+ 'mac/launch_services_util.cc',
+ 'mac/launch_services_util.h',
+ 'mac/launchd.cc',
+ 'mac/launchd.h',
+ 'mac/mac_logging.h',
+ 'mac/mac_logging.mm',
+ 'mac/mac_util.h',
+ 'mac/mac_util.mm',
+ 'mac/mach_logging.cc',
+ 'mac/mach_logging.h',
+ 'mac/mach_port_broker.h',
+ 'mac/mach_port_broker.mm',
+ 'mac/mach_port_util.cc',
+ 'mac/mach_port_util.h',
+ 'mac/objc_property_releaser.h',
+ 'mac/objc_property_releaser.mm',
+ 'mac/os_crash_dumps.cc',
+ 'mac/os_crash_dumps.h',
+ 'mac/scoped_aedesc.h',
+ 'mac/scoped_authorizationref.h',
+ 'mac/scoped_block.h',
+ 'mac/scoped_cftyperef.h',
+ 'mac/scoped_dispatch_object.h',
+ 'mac/scoped_ioobject.h',
+ 'mac/scoped_ioplugininterface.h',
+ 'mac/scoped_launch_data.h',
+ 'mac/scoped_mach_port.cc',
+ 'mac/scoped_mach_port.h',
+ 'mac/scoped_mach_vm.cc',
+ 'mac/scoped_mach_vm.h',
+ 'mac/scoped_nsautorelease_pool.h',
+ 'mac/scoped_nsautorelease_pool.mm',
+ 'mac/scoped_nsobject.h',
+ 'mac/scoped_nsobject.mm',
+ 'mac/scoped_objc_class_swizzler.h',
+ 'mac/scoped_objc_class_swizzler.mm',
+ 'mac/scoped_sending_event.h',
+ 'mac/scoped_sending_event.mm',
+ 'mac/scoped_typeref.h',
+ 'mac/sdk_forward_declarations.h',
+ 'mac/sdk_forward_declarations.mm',
+ 'macros.h',
+ 'md5.cc',
+ 'md5.h',
+ 'memory/aligned_memory.cc',
+ 'memory/aligned_memory.h',
+ 'memory/discardable_memory.cc',
+ 'memory/discardable_memory.h',
+ 'memory/discardable_memory_allocator.cc',
+ 'memory/discardable_memory_allocator.h',
+ 'memory/discardable_shared_memory.cc',
+ 'memory/discardable_shared_memory.h',
+ 'memory/free_deleter.h',
+ 'memory/linked_ptr.h',
+ 'memory/manual_constructor.h',
+ 'memory/memory_pressure_listener.cc',
+ 'memory/memory_pressure_listener.h',
+ 'memory/memory_pressure_monitor.cc',
+ 'memory/memory_pressure_monitor.h',
+ 'memory/memory_pressure_monitor_chromeos.cc',
+ 'memory/memory_pressure_monitor_chromeos.h',
+ 'memory/memory_pressure_monitor_mac.cc',
+ 'memory/memory_pressure_monitor_mac.h',
+ 'memory/memory_pressure_monitor_win.cc',
+ 'memory/memory_pressure_monitor_win.h',
+ 'memory/ptr_util.h',
+ 'memory/raw_scoped_refptr_mismatch_checker.h',
+ 'memory/ref_counted.cc',
+ 'memory/ref_counted.h',
+ 'memory/ref_counted_delete_on_message_loop.h',
+ 'memory/ref_counted_memory.cc',
+ 'memory/ref_counted_memory.h',
+ 'memory/scoped_policy.h',
+ 'memory/scoped_vector.h',
+ 'memory/shared_memory.h',
+ 'memory/shared_memory_android.cc',
+ 'memory/shared_memory_handle.h',
+ 'memory/shared_memory_handle_mac.cc',
+ 'memory/shared_memory_handle_win.cc',
+ 'memory/shared_memory_mac.cc',
+ 'memory/shared_memory_nacl.cc',
+ 'memory/shared_memory_posix.cc',
+ 'memory/shared_memory_win.cc',
+ 'memory/singleton.cc',
+ 'memory/singleton.h',
+ 'memory/weak_ptr.cc',
+ 'memory/weak_ptr.h',
+ 'message_loop/incoming_task_queue.cc',
+ 'message_loop/incoming_task_queue.h',
+ 'message_loop/message_loop.cc',
+ 'message_loop/message_loop.h',
+ 'message_loop/message_loop_task_runner.cc',
+ 'message_loop/message_loop_task_runner.h',
+ 'message_loop/message_pump.cc',
+ 'message_loop/message_pump.h',
+ 'message_loop/message_pump_android.cc',
+ 'message_loop/message_pump_android.h',
+ 'message_loop/message_pump_default.cc',
+ 'message_loop/message_pump_default.h',
+ 'message_loop/message_pump_win.cc',
+ 'message_loop/message_pump_win.h',
+ 'message_loop/timer_slack.h',
+ 'metrics/bucket_ranges.cc',
+ 'metrics/bucket_ranges.h',
+ 'metrics/histogram.cc',
+ 'metrics/histogram.h',
+ 'metrics/histogram_base.cc',
+ 'metrics/histogram_base.h',
+ 'metrics/histogram_delta_serialization.cc',
+ 'metrics/histogram_delta_serialization.h',
+ 'metrics/histogram_flattener.h',
+ 'metrics/histogram_macros.h',
+ 'metrics/histogram_samples.cc',
+ 'metrics/histogram_samples.h',
+ 'metrics/histogram_snapshot_manager.cc',
+ 'metrics/histogram_snapshot_manager.h',
+ 'metrics/metrics_hashes.cc',
+ 'metrics/metrics_hashes.h',
+ 'metrics/persistent_histogram_allocator.cc',
+ 'metrics/persistent_histogram_allocator.h',
+ 'metrics/persistent_memory_allocator.cc',
+ 'metrics/persistent_memory_allocator.h',
+ 'metrics/persistent_sample_map.cc',
+ 'metrics/persistent_sample_map.h',
+ 'metrics/sample_map.cc',
+ 'metrics/sample_map.h',
+ 'metrics/sample_vector.cc',
+ 'metrics/sample_vector.h',
+ 'metrics/sparse_histogram.cc',
+ 'metrics/sparse_histogram.h',
+ 'metrics/statistics_recorder.cc',
+ 'metrics/statistics_recorder.h',
+ 'metrics/user_metrics.cc',
+ 'metrics/user_metrics.h',
+ 'metrics/user_metrics_action.h',
+ 'native_library.h',
+ 'native_library_ios.mm',
+ 'native_library_mac.mm',
+ 'native_library_posix.cc',
+ 'native_library_win.cc',
+ 'nix/mime_util_xdg.cc',
+ 'nix/mime_util_xdg.h',
+ 'nix/xdg_util.cc',
+ 'nix/xdg_util.h',
+ 'numerics/safe_conversions.h',
+ 'numerics/safe_conversions_impl.h',
+ 'numerics/safe_math.h',
+ 'numerics/safe_math_impl.h',
+ 'observer_list.h',
+ 'observer_list_threadsafe.h',
+ 'optional.h',
+ 'os_compat_android.cc',
+ 'os_compat_android.h',
+ 'os_compat_nacl.cc',
+ 'os_compat_nacl.h',
+ 'path_service.cc',
+ 'path_service.h',
+ 'pending_task.cc',
+ 'pending_task.h',
+ 'pickle.cc',
+ 'pickle.h',
+ 'posix/eintr_wrapper.h',
+ 'posix/global_descriptors.cc',
+ 'posix/global_descriptors.h',
+ 'posix/safe_strerror.cc',
+ 'posix/safe_strerror.h',
+ 'posix/unix_domain_socket_linux.cc',
+ 'posix/unix_domain_socket_linux.h',
+ 'power_monitor/power_monitor.cc',
+ 'power_monitor/power_monitor.h',
+ 'power_monitor/power_monitor_device_source.cc',
+ 'power_monitor/power_monitor_device_source.h',
+ 'power_monitor/power_monitor_device_source_android.cc',
+ 'power_monitor/power_monitor_device_source_android.h',
+ 'power_monitor/power_monitor_device_source_chromeos.cc',
+ 'power_monitor/power_monitor_device_source_ios.mm',
+ 'power_monitor/power_monitor_device_source_mac.mm',
+ 'power_monitor/power_monitor_device_source_posix.cc',
+ 'power_monitor/power_monitor_device_source_win.cc',
+ 'power_monitor/power_monitor_source.cc',
+ 'power_monitor/power_monitor_source.h',
+ 'power_monitor/power_observer.h',
+ 'process/internal_linux.cc',
+ 'process/internal_linux.h',
+ 'process/kill.cc',
+ 'process/kill.h',
+ 'process/kill_mac.cc',
+ 'process/kill_posix.cc',
+ 'process/kill_win.cc',
+ 'process/launch.cc',
+ 'process/launch.h',
+ 'process/launch_ios.cc',
+ 'process/launch_mac.cc',
+ 'process/launch_posix.cc',
+ 'process/launch_win.cc',
+ 'process/memory.cc',
+ 'process/memory.h',
+ 'process/memory_linux.cc',
+ 'process/memory_mac.mm',
+ 'process/memory_win.cc',
+ 'process/port_provider_mac.cc',
+ 'process/port_provider_mac.h',
+ 'process/process.h',
+ 'process/process_handle.cc',
+ 'process/process_handle_freebsd.cc',
+ 'process/process_handle_linux.cc',
+ 'process/process_handle_mac.cc',
+ 'process/process_handle_openbsd.cc',
+ 'process/process_handle_posix.cc',
+ 'process/process_handle_win.cc',
+ 'process/process_info.h',
+ 'process/process_info_linux.cc',
+ 'process/process_info_mac.cc',
+ 'process/process_info_win.cc',
+ 'process/process_iterator.cc',
+ 'process/process_iterator.h',
+ 'process/process_iterator_freebsd.cc',
+ 'process/process_iterator_linux.cc',
+ 'process/process_iterator_mac.cc',
+ 'process/process_iterator_openbsd.cc',
+ 'process/process_iterator_win.cc',
+ 'process/process_linux.cc',
+ 'process/process_metrics.cc',
+ 'process/process_metrics.h',
+ 'process/process_metrics_freebsd.cc',
+ 'process/process_metrics_ios.cc',
+ 'process/process_metrics_linux.cc',
+ 'process/process_metrics_mac.cc',
+ 'process/process_metrics_nacl.cc',
+ 'process/process_metrics_openbsd.cc',
+ 'process/process_metrics_posix.cc',
+ 'process/process_metrics_win.cc',
+ 'process/process_posix.cc',
+ 'process/process_win.cc',
+ 'profiler/native_stack_sampler.cc',
+ 'profiler/native_stack_sampler.h',
+ 'profiler/native_stack_sampler_posix.cc',
+ 'profiler/native_stack_sampler_win.cc',
+ 'profiler/scoped_profile.cc',
+ 'profiler/scoped_profile.h',
+ 'profiler/scoped_tracker.cc',
+ 'profiler/scoped_tracker.h',
+ 'profiler/stack_sampling_profiler.cc',
+ 'profiler/stack_sampling_profiler.h',
+ 'profiler/tracked_time.cc',
+ 'profiler/tracked_time.h',
+ 'rand_util.cc',
+ 'rand_util.h',
+ 'rand_util_nacl.cc',
+ 'rand_util_posix.cc',
+ 'rand_util_win.cc',
+ 'run_loop.cc',
+ 'run_loop.h',
+ 'scoped_generic.h',
+ 'scoped_native_library.cc',
+ 'scoped_native_library.h',
+ 'scoped_observer.h',
+ 'sequence_checker.h',
+ 'sequence_checker_impl.cc',
+ 'sequence_checker_impl.h',
+ 'sequenced_task_runner.cc',
+ 'sequenced_task_runner.h',
+ 'sequenced_task_runner_helpers.h',
+ 'sha1.cc',
+ 'sha1.h',
+ 'single_thread_task_runner.h',
+ 'stl_util.h',
+ 'strings/latin1_string_conversions.cc',
+ 'strings/latin1_string_conversions.h',
+ 'strings/nullable_string16.cc',
+ 'strings/nullable_string16.h',
+ 'strings/pattern.cc',
+ 'strings/pattern.h',
+ 'strings/safe_sprintf.cc',
+ 'strings/safe_sprintf.h',
+ 'strings/string16.cc',
+ 'strings/string16.h',
+ 'strings/string_number_conversions.cc',
+ 'strings/string_number_conversions.h',
+ 'strings/string_piece.cc',
+ 'strings/string_piece.h',
+ 'strings/string_split.cc',
+ 'strings/string_split.h',
+ 'strings/string_tokenizer.h',
+ 'strings/string_util.cc',
+ 'strings/string_util.h',
+ 'strings/string_util_constants.cc',
+ 'strings/string_util_posix.h',
+ 'strings/string_util_win.h',
+ 'strings/stringize_macros.h',
+ 'strings/stringprintf.cc',
+ 'strings/stringprintf.h',
+ 'strings/sys_string_conversions.h',
+ 'strings/sys_string_conversions_mac.mm',
+ 'strings/sys_string_conversions_posix.cc',
+ 'strings/sys_string_conversions_win.cc',
+ 'strings/utf_offset_string_conversions.cc',
+ 'strings/utf_offset_string_conversions.h',
+ 'strings/utf_string_conversion_utils.cc',
+ 'strings/utf_string_conversion_utils.h',
+ 'strings/utf_string_conversions.cc',
+ 'strings/utf_string_conversions.h',
+ 'supports_user_data.cc',
+ 'supports_user_data.h',
+ 'synchronization/cancellation_flag.cc',
+ 'synchronization/cancellation_flag.h',
+ 'synchronization/condition_variable.h',
+ 'synchronization/condition_variable_posix.cc',
+ 'synchronization/condition_variable_win.cc',
+ 'synchronization/lock.cc',
+ 'synchronization/lock.h',
+ 'synchronization/lock_impl.h',
+ 'synchronization/lock_impl_posix.cc',
+ 'synchronization/lock_impl_win.cc',
+ 'synchronization/read_write_lock.h',
+ 'synchronization/read_write_lock_nacl.cc',
+ 'synchronization/read_write_lock_posix.cc',
+ 'synchronization/read_write_lock_win.cc',
+ 'synchronization/spin_wait.h',
+ 'synchronization/waitable_event.h',
+ 'synchronization/waitable_event_posix.cc',
+ 'synchronization/waitable_event_watcher.h',
+ 'synchronization/waitable_event_watcher_posix.cc',
+ 'synchronization/waitable_event_watcher_win.cc',
+ 'synchronization/waitable_event_win.cc',
+ 'sys_byteorder.h',
+ 'sys_info.cc',
+ 'sys_info.h',
+ 'sys_info_android.cc',
+ 'sys_info_chromeos.cc',
+ 'sys_info_freebsd.cc',
+ 'sys_info_internal.h',
+ 'sys_info_ios.mm',
+ 'sys_info_linux.cc',
+ 'sys_info_mac.mm',
+ 'sys_info_openbsd.cc',
+ 'sys_info_posix.cc',
+ 'sys_info_win.cc',
+ 'system_monitor/system_monitor.cc',
+ 'system_monitor/system_monitor.h',
+ 'task/cancelable_task_tracker.cc',
+ 'task/cancelable_task_tracker.h',
+ 'task_runner.cc',
+ 'task_runner.h',
+ 'task_runner_util.h',
+ 'task_scheduler/delayed_task_manager.cc',
+ 'task_scheduler/delayed_task_manager.h',
+ 'task_scheduler/priority_queue.cc',
+ 'task_scheduler/priority_queue.h',
+ 'task_scheduler/scheduler_lock.h',
+ 'task_scheduler/scheduler_lock_impl.cc',
+ 'task_scheduler/scheduler_lock_impl.h',
+ 'task_scheduler/scheduler_service_thread.cc',
+ 'task_scheduler/scheduler_service_thread.h',
+ 'task_scheduler/scheduler_worker.cc',
+ 'task_scheduler/scheduler_worker.h',
+ 'task_scheduler/scheduler_worker_pool.h',
+ 'task_scheduler/scheduler_worker_pool_impl.cc',
+ 'task_scheduler/scheduler_worker_pool_impl.h',
+ 'task_scheduler/scheduler_worker_stack.cc',
+ 'task_scheduler/scheduler_worker_stack.h',
+ 'task_scheduler/sequence.cc',
+ 'task_scheduler/sequence.h',
+ 'task_scheduler/sequence_sort_key.cc',
+ 'task_scheduler/sequence_sort_key.h',
+ 'task_scheduler/task.cc',
+ 'task_scheduler/task.h',
+ 'task_scheduler/task_scheduler.cc',
+ 'task_scheduler/task_scheduler.h',
+ 'task_scheduler/task_scheduler_impl.cc',
+ 'task_scheduler/task_scheduler_impl.h',
+ 'task_scheduler/task_tracker.cc',
+ 'task_scheduler/task_tracker.h',
+ 'task_scheduler/task_traits.cc',
+ 'task_scheduler/task_traits.h',
+ 'template_util.h',
+ 'third_party/dmg_fp/dmg_fp.h',
+ 'third_party/dmg_fp/dtoa_wrapper.cc',
+ 'third_party/dmg_fp/g_fmt.cc',
+ 'third_party/icu/icu_utf.cc',
+ 'third_party/icu/icu_utf.h',
+ 'third_party/nspr/prtime.cc',
+ 'third_party/nspr/prtime.h',
+ 'third_party/superfasthash/superfasthash.c',
+ 'third_party/xdg_mime/xdgmime.h',
+ 'threading/non_thread_safe.h',
+ 'threading/non_thread_safe_impl.cc',
+ 'threading/non_thread_safe_impl.h',
+ 'threading/platform_thread.h',
+ 'threading/platform_thread_android.cc',
+ 'threading/platform_thread_internal_posix.cc',
+ 'threading/platform_thread_internal_posix.h',
+ 'threading/platform_thread_linux.cc',
+ 'threading/platform_thread_mac.mm',
+ 'threading/platform_thread_posix.cc',
+ 'threading/platform_thread_win.cc',
+ 'threading/post_task_and_reply_impl.cc',
+ 'threading/post_task_and_reply_impl.h',
+ 'threading/sequenced_task_runner_handle.cc',
+ 'threading/sequenced_task_runner_handle.h',
+ 'threading/sequenced_worker_pool.cc',
+ 'threading/sequenced_worker_pool.h',
+ 'threading/simple_thread.cc',
+ 'threading/simple_thread.h',
+ 'threading/thread.cc',
+ 'threading/thread.h',
+ 'threading/thread_checker.h',
+ 'threading/thread_checker_impl.cc',
+ 'threading/thread_checker_impl.h',
+ 'threading/thread_collision_warner.cc',
+ 'threading/thread_collision_warner.h',
+ 'threading/thread_id_name_manager.cc',
+ 'threading/thread_id_name_manager.h',
+ 'threading/thread_local.h',
+ 'threading/thread_local_android.cc',
+ 'threading/thread_local_posix.cc',
+ 'threading/thread_local_storage.cc',
+ 'threading/thread_local_storage.h',
+ 'threading/thread_local_storage_posix.cc',
+ 'threading/thread_local_storage_win.cc',
+ 'threading/thread_local_win.cc',
+ 'threading/thread_restrictions.cc',
+ 'threading/thread_restrictions.h',
+ 'threading/thread_task_runner_handle.cc',
+ 'threading/thread_task_runner_handle.h',
+ 'threading/watchdog.cc',
+ 'threading/watchdog.h',
+ 'threading/worker_pool.cc',
+ 'threading/worker_pool.h',
+ 'threading/worker_pool_posix.cc',
+ 'threading/worker_pool_posix.h',
+ 'threading/worker_pool_win.cc',
+ 'time/clock.cc',
+ 'time/clock.h',
+ 'time/default_clock.cc',
+ 'time/default_clock.h',
+ 'time/default_tick_clock.cc',
+ 'time/default_tick_clock.h',
+ 'time/tick_clock.cc',
+ 'time/tick_clock.h',
+ 'time/time.cc',
+ 'time/time.h',
+ 'time/time_mac.cc',
+ 'time/time_posix.cc',
+ 'time/time_win.cc',
+ 'timer/elapsed_timer.cc',
+ 'timer/elapsed_timer.h',
+ 'timer/hi_res_timer_manager.h',
+ 'timer/hi_res_timer_manager_posix.cc',
+ 'timer/hi_res_timer_manager_win.cc',
+ 'timer/mock_timer.cc',
+ 'timer/mock_timer.h',
+ 'timer/timer.cc',
+ 'timer/timer.h',
+ 'tracked_objects.cc',
+ 'tracked_objects.h',
+ 'tracking_info.cc',
+ 'tracking_info.h',
+ 'tuple.h',
+ 'value_conversions.cc',
+ 'value_conversions.h',
+ 'values.cc',
+ 'values.h',
+ 'version.cc',
+ 'version.h',
+ 'vlog.cc',
+ 'vlog.h',
+ 'win/enum_variant.cc',
+ 'win/enum_variant.h',
+ 'win/event_trace_consumer.h',
+ 'win/event_trace_controller.cc',
+ 'win/event_trace_controller.h',
+ 'win/event_trace_provider.cc',
+ 'win/event_trace_provider.h',
+ 'win/i18n.cc',
+ 'win/i18n.h',
+ 'win/iat_patch_function.cc',
+ 'win/iat_patch_function.h',
+ 'win/iunknown_impl.cc',
+ 'win/iunknown_impl.h',
+ 'win/message_window.cc',
+ 'win/message_window.h',
+ 'win/object_watcher.cc',
+ 'win/object_watcher.h',
+ 'win/process_startup_helper.cc',
+ 'win/process_startup_helper.h',
+ 'win/registry.cc',
+ 'win/registry.h',
+ 'win/resource_util.cc',
+ 'win/resource_util.h',
+ 'win/scoped_bstr.cc',
+ 'win/scoped_bstr.h',
+ 'win/scoped_co_mem.h',
+ 'win/scoped_com_initializer.h',
+ 'win/scoped_comptr.h',
+ 'win/scoped_gdi_object.h',
+ 'win/scoped_handle.cc',
+ 'win/scoped_handle.h',
+ 'win/scoped_hdc.h',
+ 'win/scoped_hglobal.h',
+ 'win/scoped_process_information.cc',
+ 'win/scoped_process_information.h',
+ 'win/scoped_propvariant.h',
+ 'win/scoped_select_object.h',
+ 'win/scoped_variant.cc',
+ 'win/scoped_variant.h',
+ 'win/shortcut.cc',
+ 'win/shortcut.h',
+ 'win/startup_information.cc',
+ 'win/startup_information.h',
+ 'win/wait_chain.cc',
+ 'win/wait_chain.h',
+ 'win/win_util.cc',
+ 'win/win_util.h',
+ 'win/windows_version.cc',
+ 'win/windows_version.h',
+ 'win/wrapped_window_proc.cc',
+ 'win/wrapped_window_proc.h',
+ '<@(trace_event_sources)',
+ ],
+ 'defines': [
+ 'BASE_IMPLEMENTATION',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'target_conditions': [
+ ['OS == "mac" or OS == "ios"', {
+ 'sources!': [
+ 'memory/shared_memory_posix.cc',
+ ],
+ }],
+ ['OS == "ios"', {
+ 'sources!': [
+ 'memory/discardable_shared_memory.cc',
+ 'memory/discardable_shared_memory.h',
+ ],
+ }],
+ ['(<(desktop_linux) == 0 and <(chromeos) == 0) or >(nacl_untrusted_build)==1', {
+ 'sources/': [
+ ['exclude', '^nix/'],
+ ],
+ }],
+ ['<(use_glib)==0 or >(nacl_untrusted_build)==1', {
+ 'sources!': [
+ 'message_loop/message_pump_glib.cc',
+ ],
+ }],
+ ['(OS != "linux" and <(os_bsd) != 1 and OS != "android") or >(nacl_untrusted_build)==1', {
+ 'sources!': [
+ # Not automatically excluded by the *linux.cc rules.
+ 'linux_util.cc',
+ ],
+ },
+ ],
+ ['>(nacl_untrusted_build)==1', {
+ 'sources!': [
+ 'base_paths.cc',
+ 'cpu.cc',
+ 'debug/stack_trace.cc',
+ 'debug/stack_trace_posix.cc',
+ 'files/file_enumerator_posix.cc',
+ 'files/file_path_watcher_fsevents.cc',
+ 'files/file_path_watcher_fsevents.h',
+ 'files/file_path_watcher_kqueue.cc',
+ 'files/file_path_watcher_kqueue.h',
+ 'files/file_proxy.cc',
+ 'files/file_util.cc',
+ 'files/file_util_posix.cc',
+ 'files/file_util_proxy.cc',
+ 'files/important_file_writer.cc',
+ 'files/scoped_temp_dir.cc',
+ 'memory/shared_memory_posix.cc',
+ 'native_library_posix.cc',
+ 'path_service.cc',
+ 'posix/unix_domain_socket_linux.cc',
+ 'process/kill.cc',
+ 'process/kill_posix.cc',
+ 'process/launch.cc',
+ 'process/launch_posix.cc',
+ 'process/process_metrics.cc',
+ 'process/process_metrics_posix.cc',
+ 'process/process_posix.cc',
+ 'rand_util_posix.cc',
+ 'scoped_native_library.cc',
+ 'synchronization/read_write_lock_posix.cc',
+ 'sys_info.cc',
+ 'sys_info_posix.cc',
+ 'third_party/dynamic_annotations/dynamic_annotations.c',
+ ],
+ 'sources/': [
+ ['include', '^threading/platform_thread_linux\\.cc$'],
+ ],
+ }],
+ ['OS == "android" and >(nacl_untrusted_build)==0', {
+ 'sources!': [
+ 'base_paths_posix.cc',
+ 'files/file_path_watcher_fsevents.cc',
+ 'files/file_path_watcher_fsevents.h',
+ 'files/file_path_watcher_kqueue.cc',
+ 'files/file_path_watcher_kqueue.h',
+ 'files/file_path_watcher_stub.cc',
+ 'power_monitor/power_monitor_device_source_posix.cc',
+ ],
+ 'sources/': [
+ ['include', '^debug/proc_maps_linux\\.cc$'],
+ ['include', '^files/file_path_watcher_linux\\.cc$'],
+ ['include', '^process/memory_linux\\.cc$'],
+ ['include', '^process/internal_linux\\.cc$'],
+ ['include', '^process/process_handle_linux\\.cc$'],
+ ['include', '^process/process_iterator\\.cc$'],
+ ['include', '^process/process_iterator_linux\\.cc$'],
+ ['include', '^process/process_metrics_linux\\.cc$'],
+ ['include', '^posix/unix_domain_socket_linux\\.cc$'],
+ ['include', '^strings/sys_string_conversions_posix\\.cc$'],
+ ['include', '^sys_info_linux\\.cc$'],
+ ['include', '^worker_pool_linux\\.cc$'],
+ ],
+ }],
+ ['OS == "android" and _toolset == "host" and host_os == "linux"', {
+ 'sources/': [
+ # Pull in specific files for host builds.
+ ['include', '^threading/platform_thread_linux\\.cc$'],
+ ],
+ }],
+ ['<(chromeos) == 1', {
+ 'sources!': [
+ 'power_monitor/power_monitor_device_source_posix.cc',
+ ],
+ }],
+ ['OS == "ios" and _toolset != "host"', {
+ 'sources/': [
+ # Pull in specific Mac files for iOS (which have been filtered out
+ # by file name rules).
+ ['include', '^base_paths_mac\\.'],
+ ['include', '^files/file_util_mac\\.'],
+ ['include', '^file_version_info_mac\\.'],
+ ['include', '^mac/bundle_locations\\.'],
+ ['include', '^mac/call_with_eh_frame\\.'],
+ ['include', '^mac/foundation_util\\.'],
+ ['include', '^mac/mac_logging\\.'],
+ ['include', '^mac/mach_logging\\.'],
+ ['include', '^mac/objc_property_releaser\\.'],
+ ['include', '^mac/scoped_block\\.'],
+ ['include', '^mac/scoped_mach_port\\.'],
+ ['include', '^mac/scoped_mach_vm\\.'],
+ ['include', '^mac/scoped_nsautorelease_pool\\.'],
+ ['include', '^mac/scoped_nsobject\\.'],
+ ['include', '^mac/scoped_objc_class_swizzler\\.'],
+ ['include', '^memory/shared_memory_posix\\.'],
+ ['include', '^message_loop/message_pump_mac\\.'],
+ ['include', '^strings/sys_string_conversions_mac\\.'],
+ ['include', '^threading/platform_thread_mac\\.'],
+ ['include', '^time/time_mac\\.'],
+ ['include', '^worker_pool_mac\\.'],
+ # Exclude all process/ except the minimal implementation
+ # needed on iOS (mostly for unit tests).
+ ['exclude', '^process/.*'],
+ ['include', '^process/.*_ios\.(cc|mm)$'],
+ ['include', '^process/memory_stubs\.cc$'],
+ ['include', '^process/process_handle_posix\.cc$'],
+ ['include', '^process/process_metrics\\.cc$'],
+ # Exclude unsupported features on iOS.
+ ['exclude', '^files/file_path_watcher.*'],
+ ['exclude', '^threading/platform_thread_internal_posix\\.(h|cc)'],
+ ['exclude', '^trace_event/malloc_dump_provider\\.(h|cc)$'],
+ ],
+ 'sources': [
+ 'process/memory_stubs.cc',
+ ],
+ 'sources!': [
+ 'message_loop/message_pump_libevent.cc'
+ ],
+ }],
+ ['OS == "ios" and _toolset == "host"', {
+ 'sources/': [
+ # Copied filename_rules to switch from iOS to Mac inclusions.
+ ['include', '_(cocoa|mac)(_unittest)?\\.(h|cc|mm?)$'],
+ ['include', '(^|/)(cocoa|mac)/'],
+ ['exclude', '_ios(_unittest)?\\.(h|cc|mm?)$'],
+ ['exclude', '(^|/)ios/'],
+ ['exclude', 'files/file_path_watcher_fsevents.cc'],
+ ['exclude', 'files/file_path_watcher_fsevents.h'],
+ ['include', 'files/file_path_watcher_mac.cc'],
+ ]
+ }],
+ # For now, just test the *BSD platforms enough to exclude them.
+ # Subsequent changes will include them further.
+ ['OS != "freebsd" or >(nacl_untrusted_build)==1', {
+ 'sources/': [ ['exclude', '_freebsd\\.cc$'] ],
+ },
+ ],
+ ['OS != "openbsd" or >(nacl_untrusted_build)==1', {
+ 'sources/': [ ['exclude', '_openbsd\\.cc$'] ],
+ },
+ ],
+ ['OS == "win" and >(nacl_untrusted_build)==0', {
+ 'include_dirs': [
+ '<(DEPTH)/third_party/wtl/include',
+ ],
+ 'sources': [
+ 'profiler/win32_stack_frame_unwinder.cc',
+ 'profiler/win32_stack_frame_unwinder.h',
+ ],
+ 'sources!': [
+ 'files/file_path_watcher_fsevents.cc',
+ 'files/file_path_watcher_fsevents.h',
+ 'files/file_path_watcher_kqueue.cc',
+ 'files/file_path_watcher_kqueue.h',
+ 'files/file_path_watcher_stub.cc',
+ 'message_loop/message_pump_libevent.cc',
+ 'posix/file_descriptor_shuffle.cc',
+ 'strings/string16.cc',
+ ],
+ },],
+ ['<(use_ozone) == 1', {
+ 'sources!': [
+ 'message_loop/message_pump_glib.cc',
+ ]
+ }],
+ ['OS == "linux" and >(nacl_untrusted_build)==0', {
+ 'sources!': [
+ 'files/file_path_watcher_fsevents.cc',
+ 'files/file_path_watcher_fsevents.h',
+ 'files/file_path_watcher_kqueue.cc',
+ 'files/file_path_watcher_kqueue.h',
+ 'files/file_path_watcher_stub.cc',
+ ],
+ }],
+ ['(OS == "mac" or OS == "ios") and >(nacl_untrusted_build)==0', {
+ 'sources/': [
+ ['exclude', '^base_paths_posix\\.cc$'],
+ ['exclude', '^files/file_path_watcher_stub\\.cc$'],
+ ['exclude', '^native_library_posix\\.cc$'],
+ ['exclude', '^strings/sys_string_conversions_posix\\.cc$'],
+ ['exclude', '^threading/platform_thread_internal_posix\\.cc$'],
+ ],
+ }],
+ ['<(os_bsd)==1 and >(nacl_untrusted_build)==0', {
+ 'sources': [
+ 'process/memory_stubs.cc',
+ ],
+ 'sources/': [
+ ['exclude', '^files/file_path_watcher_linux\\.cc$'],
+ ['exclude', '^files/file_path_watcher_stub\\.cc$'],
+ ['exclude', '^files/file_util_linux\\.cc$'],
+ ['exclude', '^process/process_linux\\.cc$'],
+ ['exclude', '^sys_info_linux\\.cc$'],
+ ],
+ }],
+ # Remove all unnecessary files for build_nexe.py to avoid exceeding
+ # command-line-string limitation when building NaCl on Windows.
+ ['OS == "win" and >(nacl_untrusted_build)==1', {
+ 'sources/': [ ['exclude', '\\.h$'] ],
+ }],
+ # Enable more direct string conversions on platforms with native utf8
+ # strings
+ ['OS=="mac" or OS=="ios" or <(chromeos)==1 or <(chromecast)==1', {
+ 'defines': ['SYSTEM_NATIVE_UTF8'],
+ }],
+ ],
+ }],
+ ['base_i18n_target==1', {
+ 'defines': [
+ 'BASE_I18N_IMPLEMENTATION',
+ ],
+ 'sources': [
+ 'i18n/base_i18n_export.h',
+ 'i18n/base_i18n_switches.cc',
+ 'i18n/base_i18n_switches.h',
+ 'i18n/bidi_line_iterator.cc',
+ 'i18n/bidi_line_iterator.h',
+ 'i18n/break_iterator.cc',
+ 'i18n/break_iterator.h',
+ 'i18n/case_conversion.cc',
+ 'i18n/case_conversion.h',
+ 'i18n/char_iterator.cc',
+ 'i18n/char_iterator.h',
+ 'i18n/file_util_icu.cc',
+ 'i18n/file_util_icu.h',
+ 'i18n/i18n_constants.cc',
+ 'i18n/i18n_constants.h',
+ 'i18n/icu_encoding_detection.cc',
+ 'i18n/icu_encoding_detection.h',
+ 'i18n/icu_string_conversions.cc',
+ 'i18n/icu_string_conversions.h',
+ 'i18n/icu_util.cc',
+ 'i18n/icu_util.h',
+ 'i18n/message_formatter.cc',
+ 'i18n/message_formatter.h',
+ 'i18n/number_formatting.cc',
+ 'i18n/number_formatting.h',
+ 'i18n/rtl.cc',
+ 'i18n/rtl.h',
+ 'i18n/streaming_utf8_validator.cc',
+ 'i18n/streaming_utf8_validator.h',
+ 'i18n/string_compare.cc',
+ 'i18n/string_compare.h',
+ 'i18n/string_search.cc',
+ 'i18n/string_search.h',
+ 'i18n/time_formatting.cc',
+ 'i18n/time_formatting.h',
+ 'i18n/timezone.cc',
+ 'i18n/timezone.h',
+ 'i18n/utf8_validator_tables.cc',
+ 'i18n/utf8_validator_tables.h',
+ ],
+ }]
+ ],
+ },
+}
diff --git a/base/base_nacl.gyp b/base/base_nacl.gyp
new file mode 100644
index 0000000000..30763d4813
--- /dev/null
+++ b/base/base_nacl.gyp
@@ -0,0 +1,158 @@
+# 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.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'includes': [
+ # base.gypi must be included before common_untrusted.gypi.
+ #
+ # TODO(sergeyu): Replace the target_defaults magic in base.gypi with a
+ # sources variables lists. That way order of includes will not matter.
+ 'base.gypi',
+ '../build/common_untrusted.gypi',
+ ],
+ 'conditions': [
+ ['disable_nacl==0 and disable_nacl_untrusted==0', {
+ 'targets': [
+ {
+ 'target_name': 'base_nacl',
+ 'type': 'none',
+ 'variables': {
+ 'base_target': 1,
+ 'nacl_untrusted_build': 1,
+ 'nlib_target': 'libbase_nacl.a',
+ 'build_glibc': 0,
+ 'build_newlib': 0,
+ 'build_irt': 1,
+ 'build_pnacl_newlib': 1,
+ 'sources': [
+ 'base_switches.cc',
+ 'base_switches.h',
+ 'strings/string16.cc',
+ 'sync_socket_nacl.cc',
+ 'time/time_posix.cc',
+ ],
+ 'compile_flags': [
+ '-fno-strict-aliasing',
+ ],
+ },
+ 'dependencies': [
+ 'allocator/allocator.gyp:allocator_features#target',
+ 'base.gyp:base_debugging_flags',
+ 'base.gyp:base_build_date',
+ ],
+ },
+ {
+ 'target_name': 'base_i18n_nacl',
+ 'type': 'none',
+ 'variables': {
+ 'base_i18n_target': 1,
+ 'nacl_untrusted_build': 1,
+ 'nlib_target': 'libbase_i18n_nacl.a',
+ 'build_glibc': 0,
+ 'build_newlib': 0,
+ 'build_irt': 0,
+ 'build_pnacl_newlib': 1,
+ 'sources': [
+ 'base_switches.cc',
+ 'base_switches.h',
+ 'strings/string16.cc',
+ 'sync_socket_nacl.cc',
+ 'time/time_posix.cc',
+ ],
+ },
+ 'dependencies': [
+ 'allocator/allocator.gyp:allocator_features#target',
+ 'base.gyp:base_build_date',
+ '../third_party/icu/icu_nacl.gyp:icudata_nacl',
+ '../third_party/icu/icu_nacl.gyp:icui18n_nacl',
+ '../third_party/icu/icu_nacl.gyp:icuuc_nacl',
+ ],
+ },
+ {
+ 'target_name': 'base_nacl_nonsfi',
+ 'type': 'none',
+ 'variables': {
+ 'base_target': 1,
+ 'nacl_untrusted_build': 1,
+ 'nlib_target': 'libbase_nacl_nonsfi.a',
+ 'build_glibc': 0,
+ 'build_newlib': 0,
+ 'build_irt': 0,
+ 'build_pnacl_newlib': 0,
+ 'build_nonsfi_helper': 1,
+
+ 'sources': [
+ 'base_switches.cc',
+ 'base_switches.h',
+
+ # For PathExists and ReadFromFD.
+ 'files/file_util.cc',
+ 'files/file_util_posix.cc',
+
+ # For MessageLoopForIO based on libevent.
+ 'message_loop/message_pump_libevent.cc',
+ 'message_loop/message_pump_libevent.h',
+
+ # For UnixDomainSocket::SendMsg and RecvMsg.
+ 'posix/unix_domain_socket_linux.cc',
+
+ # For GetKnownDeadTerminationStatus and GetTerminationStatus.
+ 'process/kill_posix.cc',
+
+ # For ForkWithFlags.
+ 'process/launch.h',
+ 'process/launch_posix.cc',
+
+ # Unlike libbase_nacl, for Non-SFI build, we need to use
+ # rand_util_posix for random implementation, instead of
+ # rand_util_nacl.cc, which is based on IRT. rand_util_nacl.cc is
+ # excluded below.
+ 'rand_util_posix.cc',
+
+ # For CancelableSyncSocket.
+ 'sync_socket_nacl.cc',
+ ],
+ },
+ 'sources!': [
+ 'rand_util_nacl.cc',
+ ],
+ 'dependencies': [
+ 'allocator/allocator.gyp:allocator_features#target',
+ 'base.gyp:base_debugging_flags',
+ 'base.gyp:base_build_date',
+ 'third_party/libevent/libevent_nacl_nonsfi.gyp:event_nacl_nonsfi',
+ ],
+ },
+ {
+ 'target_name': 'test_support_base_nacl_nonsfi',
+ 'type': 'none',
+ 'variables': {
+ 'nacl_untrusted_build': 1,
+ 'nlib_target': 'libtest_support_base_nacl_nonsfi.a',
+ 'build_glibc': 0,
+ 'build_newlib': 0,
+ 'build_irt': 0,
+ 'build_pnacl_newlib': 0,
+ 'build_nonsfi_helper': 1,
+
+ 'sources': [
+ 'test/gtest_util.cc',
+ 'test/launcher/unit_test_launcher_nacl_nonsfi.cc',
+ 'test/gtest_xml_unittest_result_printer.cc',
+ 'test/test_switches.cc',
+ ],
+ },
+ 'dependencies': [
+ 'base.gyp:base_build_date',
+ 'base_nacl_nonsfi',
+ '../testing/gtest_nacl.gyp:gtest_nacl',
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/base/base_switches.cc b/base/base_switches.cc
index e8aa5cbc4d..f5c6eb3f59 100644
--- a/base/base_switches.cc
+++ b/base/base_switches.cc
@@ -24,12 +24,6 @@ const char kEnableHeapProfiling[] = "enable-heap-profiling";
// derived from trace events are reported.
const char kEnableHeapProfilingModeNative[] = "native";
-// Report per-task heap usage and churn in the task profiler.
-// Does not keep track of individual allocations unlike the default and native
-// mode. Keeps only track of summarized churn stats in the task profiler
-// (chrome://profiler).
-const char kEnableHeapProfilingTaskProfiler[] = "task-profiler";
-
// Generates full memory crash dump.
const char kFullMemoryCrashReport[] = "full-memory-crash-report";
diff --git a/base/base_switches.h b/base/base_switches.h
index 04b0773057..0585186038 100644
--- a/base/base_switches.h
+++ b/base/base_switches.h
@@ -16,7 +16,6 @@ extern const char kDisableLowEndDeviceMode[];
extern const char kEnableCrashReporter[];
extern const char kEnableHeapProfiling[];
extern const char kEnableHeapProfilingModeNative[];
-extern const char kEnableHeapProfilingTaskProfiler[];
extern const char kEnableLowEndDeviceMode[];
extern const char kForceFieldTrials[];
extern const char kFullMemoryCrashReport[];
diff --git a/base/base_unittests.isolate b/base/base_unittests.isolate
new file mode 100644
index 0000000000..208501fce8
--- /dev/null
+++ b/base/base_unittests.isolate
@@ -0,0 +1,56 @@
+# 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.
+{
+ 'variables': {
+ 'command': [
+ '../testing/test_env.py',
+ '<(PRODUCT_DIR)/base_unittests<(EXECUTABLE_SUFFIX)',
+ '--brave-new-test-launcher',
+ '--test-launcher-bot-mode',
+ '--asan=<(asan)',
+ '--msan=<(msan)',
+ '--tsan=<(tsan)',
+ ],
+ },
+ 'conditions': [
+ ['OS=="android" or OS=="linux" or OS=="mac" or OS=="win"', {
+ 'variables': {
+ 'files': [
+ 'test/data/',
+ ],
+ },
+ }],
+ ['OS=="linux" or OS=="mac" or OS=="win"', {
+ 'variables': {
+ 'files': [
+ '../testing/test_env.py',
+ ],
+ },
+ }],
+ ['OS=="linux"', {
+ 'variables': {
+ 'files': [
+ '<(PRODUCT_DIR)/lib/libmalloc_wrapper.so',
+ ],
+ },
+ }],
+ ['OS=="mac" and asan==1 and fastbuild==0', {
+ 'variables': {
+ 'files': [
+ '<(PRODUCT_DIR)/base_unittests.dSYM/',
+ ],
+ },
+ }],
+ ['OS=="win" and (fastbuild==0 or fastbuild==1)', {
+ 'variables': {
+ 'files': [
+ '<(PRODUCT_DIR)/base_unittests.exe.pdb',
+ ],
+ },
+ }],
+ ],
+ 'includes': [
+ 'base.isolate',
+ ],
+}
diff --git a/base/bind.h b/base/bind.h
index ce717972e2..9cf65b6776 100644
--- a/base/bind.h
+++ b/base/bind.h
@@ -11,7 +11,7 @@
// Usage documentation
// -----------------------------------------------------------------------------
//
-// See //docs/callback.md for documentation.
+// See base/callback.h for documentation.
//
//
// -----------------------------------------------------------------------------
@@ -24,58 +24,18 @@
namespace base {
-// Bind as OnceCallback.
template <typename Functor, typename... Args>
-inline OnceCallback<MakeUnboundRunType<Functor, Args...>>
-BindOnce(Functor&& functor, Args&&... args) {
+inline base::Callback<MakeUnboundRunType<Functor, Args...>> Bind(
+ Functor&& functor,
+ Args&&... args) {
using BindState = internal::MakeBindStateType<Functor, Args...>;
using UnboundRunType = MakeUnboundRunType<Functor, Args...>;
using Invoker = internal::Invoker<BindState, UnboundRunType>;
- using CallbackType = OnceCallback<UnboundRunType>;
- // Store the invoke func into PolymorphicInvoke before casting it to
- // InvokeFuncStorage, so that we can ensure its type matches to
- // PolymorphicInvoke, to which CallbackType will cast back.
- using PolymorphicInvoke = typename CallbackType::PolymorphicInvoke;
- PolymorphicInvoke invoke_func = &Invoker::RunOnce;
-
- using InvokeFuncStorage = internal::BindStateBase::InvokeFuncStorage;
- return CallbackType(new BindState(
- reinterpret_cast<InvokeFuncStorage>(invoke_func),
- std::forward<Functor>(functor),
- std::forward<Args>(args)...));
-}
-
-// Bind as RepeatingCallback.
-template <typename Functor, typename... Args>
-inline RepeatingCallback<MakeUnboundRunType<Functor, Args...>>
-BindRepeating(Functor&& functor, Args&&... args) {
- using BindState = internal::MakeBindStateType<Functor, Args...>;
- using UnboundRunType = MakeUnboundRunType<Functor, Args...>;
- using Invoker = internal::Invoker<BindState, UnboundRunType>;
- using CallbackType = RepeatingCallback<UnboundRunType>;
-
- // Store the invoke func into PolymorphicInvoke before casting it to
- // InvokeFuncStorage, so that we can ensure its type matches to
- // PolymorphicInvoke, to which CallbackType will cast back.
- using PolymorphicInvoke = typename CallbackType::PolymorphicInvoke;
- PolymorphicInvoke invoke_func = &Invoker::Run;
-
- using InvokeFuncStorage = internal::BindStateBase::InvokeFuncStorage;
- return CallbackType(new BindState(
- reinterpret_cast<InvokeFuncStorage>(invoke_func),
- std::forward<Functor>(functor),
- std::forward<Args>(args)...));
-}
-
-// Unannotated Bind.
-// TODO(tzik): Deprecate this and migrate to OnceCallback and
-// RepeatingCallback, once they get ready.
-template <typename Functor, typename... Args>
-inline Callback<MakeUnboundRunType<Functor, Args...>>
-Bind(Functor&& functor, Args&&... args) {
- return BindRepeating(std::forward<Functor>(functor),
- std::forward<Args>(args)...);
+ using CallbackType = Callback<UnboundRunType>;
+ return CallbackType(new BindState(std::forward<Functor>(functor),
+ std::forward<Args>(args)...),
+ &Invoker::Run);
}
} // namespace base
diff --git a/base/bind_helpers.h b/base/bind_helpers.h
index 7b3d7d3474..93d02e37a9 100644
--- a/base/bind_helpers.h
+++ b/base/bind_helpers.h
@@ -21,7 +21,7 @@
// Owned() transfers ownership of an object to the Callback resulting from
// bind; the object will be deleted when the Callback is deleted.
//
-// Passed() is for transferring movable-but-not-copyable types (eg. unique_ptr)
+// Passed() is for transferring movable-but-not-copyable types (eg. scoped_ptr)
// through a Callback. Logically, this signifies a destructive transfer of
// the state of the argument into the target function. Invoking
// Callback::Run() twice on a Callback that was created with a Passed()
@@ -174,14 +174,8 @@ namespace base {
template <typename T>
struct IsWeakReceiver;
-template <typename>
-struct BindUnwrapTraits;
-
namespace internal {
-template <typename Functor, typename SFINAE = void>
-struct FunctorTraits;
-
template <typename T>
class UnretainedWrapper {
public:
@@ -281,12 +275,35 @@ class PassedWrapper {
mutable T scoper_;
};
+// Unwrap the stored parameters for the wrappers above.
+template <typename T>
+T&& Unwrap(T&& o) {
+ return std::forward<T>(o);
+}
+
+template <typename T>
+T* Unwrap(const UnretainedWrapper<T>& unretained) {
+ return unretained.get();
+}
+
+template <typename T>
+const T& Unwrap(const ConstRefWrapper<T>& const_ref) {
+ return const_ref.get();
+}
+
template <typename T>
-using Unwrapper = BindUnwrapTraits<typename std::decay<T>::type>;
+T* Unwrap(const RetainedRefWrapper<T>& o) {
+ return o.get();
+}
template <typename T>
-auto Unwrap(T&& o) -> decltype(Unwrapper<T>::Unwrap(std::forward<T>(o))) {
- return Unwrapper<T>::Unwrap(std::forward<T>(o));
+T* Unwrap(const OwnedWrapper<T>& o) {
+ return o.get();
+}
+
+template <typename T>
+T Unwrap(const PassedWrapper<T>& o) {
+ return o.Take();
}
// IsWeakMethod is a helper that determine if we are binding a WeakPtr<> to a
@@ -480,92 +497,6 @@ struct IsWeakReceiver<internal::ConstRefWrapper<T>> : IsWeakReceiver<T> {};
template <typename T>
struct IsWeakReceiver<WeakPtr<T>> : std::true_type {};
-// An injection point to control how bound objects passed to the target
-// function. BindUnwrapTraits<>::Unwrap() is called for each bound objects right
-// before the target function is invoked.
-template <typename>
-struct BindUnwrapTraits {
- template <typename T>
- static T&& Unwrap(T&& o) { return std::forward<T>(o); }
-};
-
-template <typename T>
-struct BindUnwrapTraits<internal::UnretainedWrapper<T>> {
- static T* Unwrap(const internal::UnretainedWrapper<T>& o) {
- return o.get();
- }
-};
-
-template <typename T>
-struct BindUnwrapTraits<internal::ConstRefWrapper<T>> {
- static const T& Unwrap(const internal::ConstRefWrapper<T>& o) {
- return o.get();
- }
-};
-
-template <typename T>
-struct BindUnwrapTraits<internal::RetainedRefWrapper<T>> {
- static T* Unwrap(const internal::RetainedRefWrapper<T>& o) {
- return o.get();
- }
-};
-
-template <typename T>
-struct BindUnwrapTraits<internal::OwnedWrapper<T>> {
- static T* Unwrap(const internal::OwnedWrapper<T>& o) {
- return o.get();
- }
-};
-
-template <typename T>
-struct BindUnwrapTraits<internal::PassedWrapper<T>> {
- static T Unwrap(const internal::PassedWrapper<T>& o) {
- return o.Take();
- }
-};
-
-// CallbackCancellationTraits allows customization of Callback's cancellation
-// semantics. By default, callbacks are not cancellable. A specialization should
-// set is_cancellable = true and implement an IsCancelled() that returns if the
-// callback should be cancelled.
-template <typename Functor, typename BoundArgsTuple, typename SFINAE = void>
-struct CallbackCancellationTraits {
- static constexpr bool is_cancellable = false;
-};
-
-// Specialization for method bound to weak pointer receiver.
-template <typename Functor, typename... BoundArgs>
-struct CallbackCancellationTraits<
- Functor,
- std::tuple<BoundArgs...>,
- typename std::enable_if<
- internal::IsWeakMethod<internal::FunctorTraits<Functor>::is_method,
- BoundArgs...>::value>::type> {
- static constexpr bool is_cancellable = true;
-
- template <typename Receiver, typename... Args>
- static bool IsCancelled(const Functor&,
- const Receiver& receiver,
- const Args&...) {
- return !receiver;
- }
-};
-
-// Specialization for a nested bind.
-template <typename Signature,
- typename... BoundArgs,
- internal::CopyMode copy_mode,
- internal::RepeatMode repeat_mode>
-struct CallbackCancellationTraits<Callback<Signature, copy_mode, repeat_mode>,
- std::tuple<BoundArgs...>> {
- static constexpr bool is_cancellable = true;
-
- template <typename Functor>
- static bool IsCancelled(const Functor& functor, const BoundArgs&...) {
- return functor.IsCancelled();
- }
-};
-
} // namespace base
#endif // BASE_BIND_HELPERS_H_
diff --git a/base/bind_internal.h b/base/bind_internal.h
index 8988bdca22..3d6ca09c41 100644
--- a/base/bind_internal.h
+++ b/base/bind_internal.h
@@ -130,7 +130,7 @@ struct ForceVoidReturn<R(Args...)> {
// FunctorTraits<>
//
// See description at top of file.
-template <typename Functor, typename SFINAE>
+template <typename Functor, typename SFINAE = void>
struct FunctorTraits;
// For a callable type that is convertible to the corresponding function type.
@@ -244,16 +244,14 @@ struct FunctorTraits<IgnoreResultHelper<T>> : FunctorTraits<T> {
template <typename IgnoreResultType, typename... RunArgs>
static void Invoke(IgnoreResultType&& ignore_result_helper,
RunArgs&&... args) {
- FunctorTraits<T>::Invoke(
- std::forward<IgnoreResultType>(ignore_result_helper).functor_,
- std::forward<RunArgs>(args)...);
+ FunctorTraits<T>::Invoke(ignore_result_helper.functor_,
+ std::forward<RunArgs>(args)...);
}
};
// For Callbacks.
-template <typename R, typename... Args,
- CopyMode copy_mode, RepeatMode repeat_mode>
-struct FunctorTraits<Callback<R(Args...), copy_mode, repeat_mode>> {
+template <typename R, typename... Args, CopyMode copy_mode>
+struct FunctorTraits<Callback<R(Args...), copy_mode>> {
using RunType = R(Args...);
static constexpr bool is_method = false;
static constexpr bool is_nullable = true;
@@ -316,19 +314,6 @@ struct Invoker;
template <typename StorageType, typename R, typename... UnboundArgs>
struct Invoker<StorageType, R(UnboundArgs...)> {
- static R RunOnce(BindStateBase* base, UnboundArgs&&... unbound_args) {
- // Local references to make debugger stepping easier. If in a debugger,
- // you really want to warp ahead and step through the
- // InvokeHelper<>::MakeItSo() call below.
- StorageType* storage = static_cast<StorageType*>(base);
- static constexpr size_t num_bound_args =
- std::tuple_size<decltype(storage->bound_args_)>::value;
- return RunImpl(std::move(storage->functor_),
- std::move(storage->bound_args_),
- MakeIndexSequence<num_bound_args>(),
- std::forward<UnboundArgs>(unbound_args)...);
- }
-
static R Run(BindStateBase* base, UnboundArgs&&... unbound_args) {
// Local references to make debugger stepping easier. If in a debugger,
// you really want to warp ahead and step through the
@@ -387,102 +372,27 @@ IsNull(const Functor&) {
return false;
}
-// Used by ApplyCancellationTraits below.
-template <typename Functor, typename BoundArgsTuple, size_t... indices>
-bool ApplyCancellationTraitsImpl(const Functor& functor,
- const BoundArgsTuple& bound_args,
- IndexSequence<indices...>) {
- return CallbackCancellationTraits<Functor, BoundArgsTuple>::IsCancelled(
- functor, base::get<indices>(bound_args)...);
-}
-
-// Relays |base| to corresponding CallbackCancellationTraits<>::Run(). Returns
-// true if the callback |base| represents is canceled.
-template <typename BindStateType>
-bool ApplyCancellationTraits(const BindStateBase* base) {
- const BindStateType* storage = static_cast<const BindStateType*>(base);
- static constexpr size_t num_bound_args =
- std::tuple_size<decltype(storage->bound_args_)>::value;
- return ApplyCancellationTraitsImpl(storage->functor_, storage->bound_args_,
- MakeIndexSequence<num_bound_args>());
-};
-
-// Template helpers to detect using Bind() on a base::Callback without any
-// additional arguments. In that case, the original base::Callback object should
-// just be directly used.
-template <typename Functor, typename... BoundArgs>
-struct BindingCallbackWithNoArgs {
- static constexpr bool value = false;
-};
-
-template <typename Signature,
- typename... BoundArgs,
- CopyMode copy_mode,
- RepeatMode repeat_mode>
-struct BindingCallbackWithNoArgs<Callback<Signature, copy_mode, repeat_mode>,
- BoundArgs...> {
- static constexpr bool value = sizeof...(BoundArgs) == 0;
-};
-
// BindState<>
//
// This stores all the state passed into Bind().
template <typename Functor, typename... BoundArgs>
struct BindState final : BindStateBase {
- using IsCancellable = std::integral_constant<
- bool,
- CallbackCancellationTraits<Functor,
- std::tuple<BoundArgs...>>::is_cancellable>;
-
template <typename ForwardFunctor, typename... ForwardBoundArgs>
- explicit BindState(BindStateBase::InvokeFuncStorage invoke_func,
- ForwardFunctor&& functor,
- ForwardBoundArgs&&... bound_args)
- // IsCancellable is std::false_type if
- // CallbackCancellationTraits<>::IsCancelled returns always false.
- // Otherwise, it's std::true_type.
- : BindState(IsCancellable{},
- invoke_func,
- std::forward<ForwardFunctor>(functor),
- std::forward<ForwardBoundArgs>(bound_args)...) {
- static_assert(!BindingCallbackWithNoArgs<Functor, BoundArgs...>::value,
- "Attempting to bind a base::Callback with no additional "
- "arguments: save a heap allocation and use the original "
- "base::Callback object");
+ explicit BindState(ForwardFunctor&& functor, ForwardBoundArgs&&... bound_args)
+ : BindStateBase(&Destroy),
+ functor_(std::forward<ForwardFunctor>(functor)),
+ bound_args_(std::forward<ForwardBoundArgs>(bound_args)...) {
+ DCHECK(!IsNull(functor_));
}
Functor functor_;
std::tuple<BoundArgs...> bound_args_;
private:
- template <typename ForwardFunctor, typename... ForwardBoundArgs>
- explicit BindState(std::true_type,
- BindStateBase::InvokeFuncStorage invoke_func,
- ForwardFunctor&& functor,
- ForwardBoundArgs&&... bound_args)
- : BindStateBase(invoke_func,
- &Destroy,
- &ApplyCancellationTraits<BindState>),
- functor_(std::forward<ForwardFunctor>(functor)),
- bound_args_(std::forward<ForwardBoundArgs>(bound_args)...) {
- DCHECK(!IsNull(functor_));
- }
-
- template <typename ForwardFunctor, typename... ForwardBoundArgs>
- explicit BindState(std::false_type,
- BindStateBase::InvokeFuncStorage invoke_func,
- ForwardFunctor&& functor,
- ForwardBoundArgs&&... bound_args)
- : BindStateBase(invoke_func, &Destroy),
- functor_(std::forward<ForwardFunctor>(functor)),
- bound_args_(std::forward<ForwardBoundArgs>(bound_args)...) {
- DCHECK(!IsNull(functor_));
- }
-
~BindState() {}
- static void Destroy(const BindStateBase* self) {
- delete static_cast<const BindState*>(self);
+ static void Destroy(BindStateBase* self) {
+ delete static_cast<BindState*>(self);
}
};
diff --git a/base/bind_unittest.cc b/base/bind_unittest.cc
index a9ca9d2538..ba5113b507 100644
--- a/base/bind_unittest.cc
+++ b/base/bind_unittest.cc
@@ -13,14 +13,11 @@
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
-#include "base/test/gtest_util.h"
#include "build/build_config.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
-using ::testing::_;
using ::testing::Mock;
-using ::testing::ByMove;
using ::testing::Return;
using ::testing::StrictMock;
@@ -39,9 +36,6 @@ class NoRef {
MOCK_METHOD0(IntMethod0, int());
MOCK_CONST_METHOD0(IntConstMethod0, int());
- MOCK_METHOD1(VoidMethodWithIntArg, void(int));
- MOCK_METHOD0(UniquePtrMethod0, std::unique_ptr<int>());
-
private:
// Particularly important in this test to ensure no copies are made.
DISALLOW_COPY_AND_ASSIGN(NoRef);
@@ -351,28 +345,53 @@ class BindTest : public ::testing::Test {
};
StrictMock<NoRef>* BindTest::static_func_mock_ptr;
-StrictMock<NoRef>* g_func_mock_ptr;
-void VoidFunc0() {
- g_func_mock_ptr->VoidMethod0();
-}
+// Sanity check that we can instantiate a callback for each arity.
+TEST_F(BindTest, ArityTest) {
+ Callback<int()> c0 = Bind(&Sum, 32, 16, 8, 4, 2, 1);
+ EXPECT_EQ(63, c0.Run());
+
+ Callback<int(int)> c1 = Bind(&Sum, 32, 16, 8, 4, 2);
+ EXPECT_EQ(75, c1.Run(13));
+
+ Callback<int(int,int)> c2 = Bind(&Sum, 32, 16, 8, 4);
+ EXPECT_EQ(85, c2.Run(13, 12));
+
+ Callback<int(int,int,int)> c3 = Bind(&Sum, 32, 16, 8);
+ EXPECT_EQ(92, c3.Run(13, 12, 11));
-int IntFunc0() {
- return g_func_mock_ptr->IntMethod0();
+ Callback<int(int,int,int,int)> c4 = Bind(&Sum, 32, 16);
+ EXPECT_EQ(94, c4.Run(13, 12, 11, 10));
+
+ Callback<int(int,int,int,int,int)> c5 = Bind(&Sum, 32);
+ EXPECT_EQ(87, c5.Run(13, 12, 11, 10, 9));
+
+ Callback<int(int,int,int,int,int,int)> c6 = Bind(&Sum);
+ EXPECT_EQ(69, c6.Run(13, 12, 11, 10, 9, 14));
}
-TEST_F(BindTest, BasicTest) {
- Callback<int(int, int, int)> cb = Bind(&Sum, 32, 16, 8);
- EXPECT_EQ(92, cb.Run(13, 12, 11));
+// Test the Currying ability of the Callback system.
+TEST_F(BindTest, CurryingTest) {
+ Callback<int(int,int,int,int,int,int)> c6 = Bind(&Sum);
+ EXPECT_EQ(69, c6.Run(13, 12, 11, 10, 9, 14));
+
+ Callback<int(int,int,int,int,int)> c5 = Bind(c6, 32);
+ EXPECT_EQ(87, c5.Run(13, 12, 11, 10, 9));
+
+ Callback<int(int,int,int,int)> c4 = Bind(c5, 16);
+ EXPECT_EQ(94, c4.Run(13, 12, 11, 10));
- Callback<int(int, int, int, int, int, int)> c1 = Bind(&Sum);
- EXPECT_EQ(69, c1.Run(14, 13, 12, 11, 10, 9));
+ Callback<int(int,int,int)> c3 = Bind(c4, 8);
+ EXPECT_EQ(92, c3.Run(13, 12, 11));
- Callback<int(int, int, int)> c2 = Bind(c1, 32, 16, 8);
- EXPECT_EQ(86, c2.Run(11, 10, 9));
+ Callback<int(int,int)> c2 = Bind(c3, 4);
+ EXPECT_EQ(85, c2.Run(13, 12));
- Callback<int()> c3 = Bind(c2, 4, 2, 1);
- EXPECT_EQ(63, c3.Run());
+ Callback<int(int)> c1 = Bind(c2, 2);
+ EXPECT_EQ(75, c1.Run(13));
+
+ Callback<int()> c0 = Bind(c1, 1);
+ EXPECT_EQ(63, c0.Run());
}
// Test that currying the rvalue result of another Bind() works correctly.
@@ -380,8 +399,7 @@ TEST_F(BindTest, BasicTest) {
// - multiple runs of resulting Callback remain valid.
TEST_F(BindTest, CurryingRvalueResultOfBind) {
int n = 0;
- RepeatingClosure cb = BindRepeating(&TakesACallback,
- BindRepeating(&PtrArgSet, &n));
+ Closure cb = base::Bind(&TakesACallback, base::Bind(&PtrArgSet, &n));
// If we implement Bind() such that the return value has auto_ptr-like
// semantics, the second call here will fail because ownership of
@@ -395,45 +413,76 @@ TEST_F(BindTest, CurryingRvalueResultOfBind) {
EXPECT_EQ(2, n);
}
-TEST_F(BindTest, RepeatingCallbackBasicTest) {
- RepeatingCallback<int(int)> c0 = BindRepeating(&Sum, 1, 2, 4, 8, 16);
-
- // RepeatingCallback can run via a lvalue-reference.
- EXPECT_EQ(63, c0.Run(32));
-
- // It is valid to call a RepeatingCallback more than once.
- EXPECT_EQ(54, c0.Run(23));
-
- // BindRepeating can handle a RepeatingCallback as the target functor.
- RepeatingCallback<int()> c1 = BindRepeating(c0, 11);
+// Function type support.
+// - Normal function.
+// - Normal function bound with non-refcounted first argument.
+// - Method bound to non-const object.
+// - Method bound to scoped_refptr.
+// - Const method bound to non-const object.
+// - Const method bound to const object.
+// - Derived classes can be used with pointers to non-virtual base functions.
+// - Derived classes can be used with pointers to virtual base functions (and
+// preserve virtual dispatch).
+TEST_F(BindTest, FunctionTypeSupport) {
+ EXPECT_CALL(static_func_mock_, VoidMethod0());
+ EXPECT_CALL(has_ref_, AddRef()).Times(4);
+ EXPECT_CALL(has_ref_, Release()).Times(4);
+ EXPECT_CALL(has_ref_, VoidMethod0()).Times(2);
+ EXPECT_CALL(has_ref_, VoidConstMethod0()).Times(2);
+
+ Closure normal_cb = Bind(&VoidFunc0);
+ Callback<NoRef*()> normal_non_refcounted_cb =
+ Bind(&PolymorphicIdentity<NoRef*>, &no_ref_);
+ normal_cb.Run();
+ EXPECT_EQ(&no_ref_, normal_non_refcounted_cb.Run());
+
+ Closure method_cb = Bind(&HasRef::VoidMethod0, &has_ref_);
+ Closure method_refptr_cb = Bind(&HasRef::VoidMethod0,
+ make_scoped_refptr(&has_ref_));
+ Closure const_method_nonconst_obj_cb = Bind(&HasRef::VoidConstMethod0,
+ &has_ref_);
+ Closure const_method_const_obj_cb = Bind(&HasRef::VoidConstMethod0,
+ const_has_ref_ptr_);
+ method_cb.Run();
+ method_refptr_cb.Run();
+ const_method_nonconst_obj_cb.Run();
+ const_method_const_obj_cb.Run();
- // RepeatingCallback can run via a rvalue-reference.
- EXPECT_EQ(42, std::move(c1).Run());
+ Child child;
+ child.value = 0;
+ Closure virtual_set_cb = Bind(&Parent::VirtualSet, &child);
+ virtual_set_cb.Run();
+ EXPECT_EQ(kChildValue, child.value);
- // BindRepeating can handle a rvalue-reference of RepeatingCallback.
- EXPECT_EQ(32, BindRepeating(std::move(c0), 1).Run());
+ child.value = 0;
+ Closure non_virtual_set_cb = Bind(&Parent::NonVirtualSet, &child);
+ non_virtual_set_cb.Run();
+ EXPECT_EQ(kParentValue, child.value);
}
-TEST_F(BindTest, OnceCallbackBasicTest) {
- OnceCallback<int(int)> c0 = BindOnce(&Sum, 1, 2, 4, 8, 16);
-
- // OnceCallback can run via a rvalue-reference.
- EXPECT_EQ(63, std::move(c0).Run(32));
-
- // After running via the rvalue-reference, the value of the OnceCallback
- // is undefined. The implementation simply clears the instance after the
- // invocation.
- EXPECT_TRUE(c0.is_null());
-
- c0 = BindOnce(&Sum, 2, 3, 5, 7, 11);
-
- // BindOnce can handle a rvalue-reference of OnceCallback as the target
- // functor.
- OnceCallback<int()> c1 = BindOnce(std::move(c0), 13);
- EXPECT_EQ(41, std::move(c1).Run());
+// Return value support.
+// - Function with return value.
+// - Method with return value.
+// - Const method with return value.
+TEST_F(BindTest, ReturnValues) {
+ EXPECT_CALL(static_func_mock_, IntMethod0()).WillOnce(Return(1337));
+ EXPECT_CALL(has_ref_, AddRef()).Times(3);
+ EXPECT_CALL(has_ref_, Release()).Times(3);
+ EXPECT_CALL(has_ref_, IntMethod0()).WillOnce(Return(31337));
+ EXPECT_CALL(has_ref_, IntConstMethod0())
+ .WillOnce(Return(41337))
+ .WillOnce(Return(51337));
- RepeatingCallback<int(int)> c2 = BindRepeating(&Sum, 2, 3, 5, 7, 11);
- EXPECT_EQ(41, BindOnce(c2, 13).Run());
+ Callback<int()> normal_cb = Bind(&IntFunc0);
+ Callback<int()> method_cb = Bind(&HasRef::IntMethod0, &has_ref_);
+ Callback<int()> const_method_nonconst_obj_cb =
+ Bind(&HasRef::IntConstMethod0, &has_ref_);
+ Callback<int()> const_method_const_obj_cb =
+ Bind(&HasRef::IntConstMethod0, const_has_ref_ptr_);
+ EXPECT_EQ(1337, normal_cb.Run());
+ EXPECT_EQ(31337, method_cb.Run());
+ EXPECT_EQ(41337, const_method_nonconst_obj_cb.Run());
+ EXPECT_EQ(51337, const_method_const_obj_cb.Run());
}
// IgnoreResult adapter test.
@@ -442,7 +491,7 @@ TEST_F(BindTest, OnceCallbackBasicTest) {
// - Const Method with return.
// - Method with return value bound to WeakPtr<>.
// - Const Method with return bound to WeakPtr<>.
-TEST_F(BindTest, IgnoreResultForRepeating) {
+TEST_F(BindTest, IgnoreResult) {
EXPECT_CALL(static_func_mock_, IntMethod0()).WillOnce(Return(1337));
EXPECT_CALL(has_ref_, AddRef()).Times(2);
EXPECT_CALL(has_ref_, Release()).Times(2);
@@ -451,28 +500,26 @@ TEST_F(BindTest, IgnoreResultForRepeating) {
EXPECT_CALL(no_ref_, IntMethod0()).WillOnce(Return(12));
EXPECT_CALL(no_ref_, IntConstMethod0()).WillOnce(Return(13));
- RepeatingClosure normal_func_cb = BindRepeating(IgnoreResult(&IntFunc0));
+ Closure normal_func_cb = Bind(IgnoreResult(&IntFunc0));
normal_func_cb.Run();
- RepeatingClosure non_void_method_cb =
- BindRepeating(IgnoreResult(&HasRef::IntMethod0), &has_ref_);
+ Closure non_void_method_cb =
+ Bind(IgnoreResult(&HasRef::IntMethod0), &has_ref_);
non_void_method_cb.Run();
- RepeatingClosure non_void_const_method_cb =
- BindRepeating(IgnoreResult(&HasRef::IntConstMethod0), &has_ref_);
+ Closure non_void_const_method_cb =
+ Bind(IgnoreResult(&HasRef::IntConstMethod0), &has_ref_);
non_void_const_method_cb.Run();
WeakPtrFactory<NoRef> weak_factory(&no_ref_);
WeakPtrFactory<const NoRef> const_weak_factory(const_no_ref_ptr_);
- RepeatingClosure non_void_weak_method_cb =
- BindRepeating(IgnoreResult(&NoRef::IntMethod0),
- weak_factory.GetWeakPtr());
+ Closure non_void_weak_method_cb =
+ Bind(IgnoreResult(&NoRef::IntMethod0), weak_factory.GetWeakPtr());
non_void_weak_method_cb.Run();
- RepeatingClosure non_void_weak_const_method_cb =
- BindRepeating(IgnoreResult(&NoRef::IntConstMethod0),
- weak_factory.GetWeakPtr());
+ Closure non_void_weak_const_method_cb =
+ Bind(IgnoreResult(&NoRef::IntConstMethod0), weak_factory.GetWeakPtr());
non_void_weak_const_method_cb.Run();
weak_factory.InvalidateWeakPtrs();
@@ -480,86 +527,128 @@ TEST_F(BindTest, IgnoreResultForRepeating) {
non_void_weak_method_cb.Run();
}
-TEST_F(BindTest, IgnoreResultForOnce) {
- EXPECT_CALL(static_func_mock_, IntMethod0()).WillOnce(Return(1337));
- EXPECT_CALL(has_ref_, AddRef()).Times(2);
- EXPECT_CALL(has_ref_, Release()).Times(2);
- EXPECT_CALL(has_ref_, IntMethod0()).WillOnce(Return(10));
- EXPECT_CALL(has_ref_, IntConstMethod0()).WillOnce(Return(11));
+// Argument binding tests.
+// - Argument binding to primitive.
+// - Argument binding to primitive pointer.
+// - Argument binding to a literal integer.
+// - Argument binding to a literal string.
+// - Argument binding with template function.
+// - Argument binding to an object.
+// - Argument binding to pointer to incomplete type.
+// - Argument gets type converted.
+// - Pointer argument gets converted.
+// - Const Reference forces conversion.
+TEST_F(BindTest, ArgumentBinding) {
+ int n = 2;
- OnceClosure normal_func_cb = BindOnce(IgnoreResult(&IntFunc0));
- std::move(normal_func_cb).Run();
+ Callback<int()> bind_primitive_cb = Bind(&Identity, n);
+ EXPECT_EQ(n, bind_primitive_cb.Run());
- OnceClosure non_void_method_cb =
- BindOnce(IgnoreResult(&HasRef::IntMethod0), &has_ref_);
- std::move(non_void_method_cb).Run();
+ Callback<int*()> bind_primitive_pointer_cb =
+ Bind(&PolymorphicIdentity<int*>, &n);
+ EXPECT_EQ(&n, bind_primitive_pointer_cb.Run());
- OnceClosure non_void_const_method_cb =
- BindOnce(IgnoreResult(&HasRef::IntConstMethod0), &has_ref_);
- std::move(non_void_const_method_cb).Run();
+ Callback<int()> bind_int_literal_cb = Bind(&Identity, 3);
+ EXPECT_EQ(3, bind_int_literal_cb.Run());
- WeakPtrFactory<NoRef> weak_factory(&no_ref_);
- WeakPtrFactory<const NoRef> const_weak_factory(const_no_ref_ptr_);
+ Callback<const char*()> bind_string_literal_cb =
+ Bind(&CStringIdentity, "hi");
+ EXPECT_STREQ("hi", bind_string_literal_cb.Run());
- OnceClosure non_void_weak_method_cb =
- BindOnce(IgnoreResult(&NoRef::IntMethod0),
- weak_factory.GetWeakPtr());
- OnceClosure non_void_weak_const_method_cb =
- BindOnce(IgnoreResult(&NoRef::IntConstMethod0),
- weak_factory.GetWeakPtr());
+ Callback<int()> bind_template_function_cb =
+ Bind(&PolymorphicIdentity<int>, 4);
+ EXPECT_EQ(4, bind_template_function_cb.Run());
- weak_factory.InvalidateWeakPtrs();
- std::move(non_void_weak_const_method_cb).Run();
- std::move(non_void_weak_method_cb).Run();
+ NoRefParent p;
+ p.value = 5;
+ Callback<int()> bind_object_cb = Bind(&UnwrapNoRefParent, p);
+ EXPECT_EQ(5, bind_object_cb.Run());
+
+ IncompleteType* incomplete_ptr = reinterpret_cast<IncompleteType*>(123);
+ Callback<IncompleteType*()> bind_incomplete_ptr_cb =
+ Bind(&PolymorphicIdentity<IncompleteType*>, incomplete_ptr);
+ EXPECT_EQ(incomplete_ptr, bind_incomplete_ptr_cb.Run());
+
+ NoRefChild c;
+ c.value = 6;
+ Callback<int()> bind_promotes_cb = Bind(&UnwrapNoRefParent, c);
+ EXPECT_EQ(6, bind_promotes_cb.Run());
+
+ c.value = 7;
+ Callback<int()> bind_pointer_promotes_cb =
+ Bind(&UnwrapNoRefParentPtr, &c);
+ EXPECT_EQ(7, bind_pointer_promotes_cb.Run());
+
+ c.value = 8;
+ Callback<int()> bind_const_reference_promotes_cb =
+ Bind(&UnwrapNoRefParentConstRef, c);
+ EXPECT_EQ(8, bind_const_reference_promotes_cb.Run());
+}
+
+// Unbound argument type support tests.
+// - Unbound value.
+// - Unbound pointer.
+// - Unbound reference.
+// - Unbound const reference.
+// - Unbound unsized array.
+// - Unbound sized array.
+// - Unbound array-of-arrays.
+TEST_F(BindTest, UnboundArgumentTypeSupport) {
+ Callback<void(int)> unbound_value_cb = Bind(&VoidPolymorphic<int>::Run);
+ Callback<void(int*)> unbound_pointer_cb = Bind(&VoidPolymorphic<int*>::Run);
+ Callback<void(int&)> unbound_ref_cb = Bind(&VoidPolymorphic<int&>::Run);
+ Callback<void(const int&)> unbound_const_ref_cb =
+ Bind(&VoidPolymorphic<const int&>::Run);
+ Callback<void(int[])> unbound_unsized_array_cb =
+ Bind(&VoidPolymorphic<int[]>::Run);
+ Callback<void(int[2])> unbound_sized_array_cb =
+ Bind(&VoidPolymorphic<int[2]>::Run);
+ Callback<void(int[][2])> unbound_array_of_arrays_cb =
+ Bind(&VoidPolymorphic<int[][2]>::Run);
+
+ Callback<void(int&)> unbound_ref_with_bound_arg =
+ Bind(&VoidPolymorphic<int, int&>::Run, 1);
+}
+
+// Function with unbound reference parameter.
+// - Original parameter is modified by callback.
+TEST_F(BindTest, UnboundReferenceSupport) {
+ int n = 0;
+ Callback<void(int&)> unbound_ref_cb = Bind(&RefArgSet);
+ unbound_ref_cb.Run(n);
+ EXPECT_EQ(2, n);
}
// Functions that take reference parameters.
// - Forced reference parameter type still stores a copy.
// - Forced const reference parameter type still stores a copy.
-TEST_F(BindTest, ReferenceArgumentBindingForRepeating) {
+TEST_F(BindTest, ReferenceArgumentBinding) {
int n = 1;
int& ref_n = n;
const int& const_ref_n = n;
- RepeatingCallback<int()> ref_copies_cb = BindRepeating(&Identity, ref_n);
+ Callback<int()> ref_copies_cb = Bind(&Identity, ref_n);
EXPECT_EQ(n, ref_copies_cb.Run());
n++;
EXPECT_EQ(n - 1, ref_copies_cb.Run());
- RepeatingCallback<int()> const_ref_copies_cb =
- BindRepeating(&Identity, const_ref_n);
+ Callback<int()> const_ref_copies_cb = Bind(&Identity, const_ref_n);
EXPECT_EQ(n, const_ref_copies_cb.Run());
n++;
EXPECT_EQ(n - 1, const_ref_copies_cb.Run());
}
-TEST_F(BindTest, ReferenceArgumentBindingForOnce) {
- int n = 1;
- int& ref_n = n;
- const int& const_ref_n = n;
-
- OnceCallback<int()> ref_copies_cb = BindOnce(&Identity, ref_n);
- n++;
- EXPECT_EQ(n - 1, std::move(ref_copies_cb).Run());
-
- OnceCallback<int()> const_ref_copies_cb =
- BindOnce(&Identity, const_ref_n);
- n++;
- EXPECT_EQ(n - 1, std::move(const_ref_copies_cb).Run());
-}
-
// Check that we can pass in arrays and have them be stored as a pointer.
// - Array of values stores a pointer.
// - Array of const values stores a pointer.
-TEST_F(BindTest, ArrayArgumentBindingForRepeating) {
+TEST_F(BindTest, ArrayArgumentBinding) {
int array[4] = {1, 1, 1, 1};
const int (*const_array_ptr)[4] = &array;
- RepeatingCallback<int()> array_cb = BindRepeating(&ArrayGet, array, 1);
+ Callback<int()> array_cb = Bind(&ArrayGet, array, 1);
EXPECT_EQ(1, array_cb.Run());
- RepeatingCallback<int()> const_array_cb =
- BindRepeating(&ArrayGet, *const_array_ptr, 1);
+ Callback<int()> const_array_cb = Bind(&ArrayGet, *const_array_ptr, 1);
EXPECT_EQ(1, const_array_cb.Run());
array[1] = 3;
@@ -567,17 +656,25 @@ TEST_F(BindTest, ArrayArgumentBindingForRepeating) {
EXPECT_EQ(3, const_array_cb.Run());
}
-TEST_F(BindTest, ArrayArgumentBindingForOnce) {
- int array[4] = {1, 1, 1, 1};
- const int (*const_array_ptr)[4] = &array;
+// Unretained() wrapper support.
+// - Method bound to Unretained() non-const object.
+// - Const method bound to Unretained() non-const object.
+// - Const method bound to Unretained() const object.
+TEST_F(BindTest, Unretained) {
+ EXPECT_CALL(no_ref_, VoidMethod0());
+ EXPECT_CALL(no_ref_, VoidConstMethod0()).Times(2);
- OnceCallback<int()> array_cb = BindOnce(&ArrayGet, array, 1);
- OnceCallback<int()> const_array_cb =
- BindOnce(&ArrayGet, *const_array_ptr, 1);
+ Callback<void()> method_cb =
+ Bind(&NoRef::VoidMethod0, Unretained(&no_ref_));
+ method_cb.Run();
- array[1] = 3;
- EXPECT_EQ(3, std::move(array_cb).Run());
- EXPECT_EQ(3, std::move(const_array_cb).Run());
+ Callback<void()> const_method_cb =
+ Bind(&NoRef::VoidConstMethod0, Unretained(&no_ref_));
+ const_method_cb.Run();
+
+ Callback<void()> const_method_const_ptr_cb =
+ Bind(&NoRef::VoidConstMethod0, Unretained(const_no_ref_ptr_));
+ const_method_const_ptr_cb.Run();
}
// WeakPtr() support.
@@ -586,27 +683,27 @@ TEST_F(BindTest, ArrayArgumentBindingForOnce) {
// - Const method bound to WeakPtr<> to const object.
// - Normal Function with WeakPtr<> as P1 can have return type and is
// not canceled.
-TEST_F(BindTest, WeakPtrForRepeating) {
+TEST_F(BindTest, WeakPtr) {
EXPECT_CALL(no_ref_, VoidMethod0());
EXPECT_CALL(no_ref_, VoidConstMethod0()).Times(2);
WeakPtrFactory<NoRef> weak_factory(&no_ref_);
WeakPtrFactory<const NoRef> const_weak_factory(const_no_ref_ptr_);
- RepeatingClosure method_cb =
- BindRepeating(&NoRef::VoidMethod0, weak_factory.GetWeakPtr());
+ Closure method_cb =
+ Bind(&NoRef::VoidMethod0, weak_factory.GetWeakPtr());
method_cb.Run();
- RepeatingClosure const_method_cb =
- BindRepeating(&NoRef::VoidConstMethod0, const_weak_factory.GetWeakPtr());
+ Closure const_method_cb =
+ Bind(&NoRef::VoidConstMethod0, const_weak_factory.GetWeakPtr());
const_method_cb.Run();
- RepeatingClosure const_method_const_ptr_cb =
- BindRepeating(&NoRef::VoidConstMethod0, const_weak_factory.GetWeakPtr());
+ Closure const_method_const_ptr_cb =
+ Bind(&NoRef::VoidConstMethod0, const_weak_factory.GetWeakPtr());
const_method_const_ptr_cb.Run();
- RepeatingCallback<int(int)> normal_func_cb =
- BindRepeating(&FunctionWithWeakFirstParam, weak_factory.GetWeakPtr());
+ Callback<int(int)> normal_func_cb =
+ Bind(&FunctionWithWeakFirstParam, weak_factory.GetWeakPtr());
EXPECT_EQ(1, normal_func_cb.Run(1));
weak_factory.InvalidateWeakPtrs();
@@ -620,39 +717,15 @@ TEST_F(BindTest, WeakPtrForRepeating) {
EXPECT_EQ(2, normal_func_cb.Run(2));
}
-TEST_F(BindTest, WeakPtrForOnce) {
- WeakPtrFactory<NoRef> weak_factory(&no_ref_);
- WeakPtrFactory<const NoRef> const_weak_factory(const_no_ref_ptr_);
-
- OnceClosure method_cb =
- BindOnce(&NoRef::VoidMethod0, weak_factory.GetWeakPtr());
- OnceClosure const_method_cb =
- BindOnce(&NoRef::VoidConstMethod0, const_weak_factory.GetWeakPtr());
- OnceClosure const_method_const_ptr_cb =
- BindOnce(&NoRef::VoidConstMethod0, const_weak_factory.GetWeakPtr());
- Callback<int(int)> normal_func_cb =
- Bind(&FunctionWithWeakFirstParam, weak_factory.GetWeakPtr());
-
- weak_factory.InvalidateWeakPtrs();
- const_weak_factory.InvalidateWeakPtrs();
-
- std::move(method_cb).Run();
- std::move(const_method_cb).Run();
- std::move(const_method_const_ptr_cb).Run();
-
- // Still runs even after the pointers are invalidated.
- EXPECT_EQ(2, std::move(normal_func_cb).Run(2));
-}
-
// ConstRef() wrapper support.
// - Binding w/o ConstRef takes a copy.
// - Binding a ConstRef takes a reference.
// - Binding ConstRef to a function ConstRef does not copy on invoke.
-TEST_F(BindTest, ConstRefForRepeating) {
+TEST_F(BindTest, ConstRef) {
int n = 1;
- RepeatingCallback<int()> copy_cb = BindRepeating(&Identity, n);
- RepeatingCallback<int()> const_ref_cb = BindRepeating(&Identity, ConstRef(n));
+ Callback<int()> copy_cb = Bind(&Identity, n);
+ Callback<int()> const_ref_cb = Bind(&Identity, ConstRef(n));
EXPECT_EQ(n, copy_cb.Run());
EXPECT_EQ(n, const_ref_cb.Run());
n++;
@@ -664,8 +737,8 @@ TEST_F(BindTest, ConstRefForRepeating) {
int move_constructs = 0;
int move_assigns = 0;
CopyMoveCounter counter(&copies, &assigns, &move_constructs, &move_assigns);
- RepeatingCallback<int()> all_const_ref_cb =
- BindRepeating(&GetCopies, ConstRef(counter));
+ Callback<int()> all_const_ref_cb =
+ Bind(&GetCopies, ConstRef(counter));
EXPECT_EQ(0, all_const_ref_cb.Run());
EXPECT_EQ(0, copies);
EXPECT_EQ(0, assigns);
@@ -673,38 +746,25 @@ TEST_F(BindTest, ConstRefForRepeating) {
EXPECT_EQ(0, move_assigns);
}
-TEST_F(BindTest, ConstRefForOnce) {
- int n = 1;
-
- OnceCallback<int()> copy_cb = BindOnce(&Identity, n);
- OnceCallback<int()> const_ref_cb = BindOnce(&Identity, ConstRef(n));
- n++;
- EXPECT_EQ(n - 1, std::move(copy_cb).Run());
- EXPECT_EQ(n, std::move(const_ref_cb).Run());
+TEST_F(BindTest, ScopedRefptr) {
+ EXPECT_CALL(has_ref_, AddRef()).Times(1);
+ EXPECT_CALL(has_ref_, Release()).Times(1);
- int copies = 0;
- int assigns = 0;
- int move_constructs = 0;
- int move_assigns = 0;
- CopyMoveCounter counter(&copies, &assigns, &move_constructs, &move_assigns);
- OnceCallback<int()> all_const_ref_cb =
- BindOnce(&GetCopies, ConstRef(counter));
- EXPECT_EQ(0, std::move(all_const_ref_cb).Run());
- EXPECT_EQ(0, copies);
- EXPECT_EQ(0, assigns);
- EXPECT_EQ(0, move_constructs);
- EXPECT_EQ(0, move_assigns);
+ const scoped_refptr<HasRef> refptr(&has_ref_);
+ Callback<int()> scoped_refptr_const_ref_cb =
+ Bind(&FunctionWithScopedRefptrFirstParam, base::ConstRef(refptr), 1);
+ EXPECT_EQ(1, scoped_refptr_const_ref_cb.Run());
}
// Test Owned() support.
-TEST_F(BindTest, OwnedForRepeating) {
+TEST_F(BindTest, Owned) {
int deletes = 0;
DeleteCounter* counter = new DeleteCounter(&deletes);
// If we don't capture, delete happens on Callback destruction/reset.
// return the same value.
- RepeatingCallback<DeleteCounter*()> no_capture_cb =
- BindRepeating(&PolymorphicIdentity<DeleteCounter*>, Owned(counter));
+ Callback<DeleteCounter*()> no_capture_cb =
+ Bind(&PolymorphicIdentity<DeleteCounter*>, Owned(counter));
ASSERT_EQ(counter, no_capture_cb.Run());
ASSERT_EQ(counter, no_capture_cb.Run());
EXPECT_EQ(0, deletes);
@@ -713,272 +773,18 @@ TEST_F(BindTest, OwnedForRepeating) {
deletes = 0;
counter = new DeleteCounter(&deletes);
- RepeatingClosure own_object_cb =
- BindRepeating(&DeleteCounter::VoidMethod0, Owned(counter));
+ base::Closure own_object_cb =
+ Bind(&DeleteCounter::VoidMethod0, Owned(counter));
own_object_cb.Run();
EXPECT_EQ(0, deletes);
own_object_cb.Reset();
EXPECT_EQ(1, deletes);
}
-TEST_F(BindTest, OwnedForOnce) {
- int deletes = 0;
- DeleteCounter* counter = new DeleteCounter(&deletes);
-
- // If we don't capture, delete happens on Callback destruction/reset.
- // return the same value.
- OnceCallback<DeleteCounter*()> no_capture_cb =
- BindOnce(&PolymorphicIdentity<DeleteCounter*>, Owned(counter));
- EXPECT_EQ(0, deletes);
- no_capture_cb.Reset(); // This should trigger a delete.
- EXPECT_EQ(1, deletes);
-
- deletes = 0;
- counter = new DeleteCounter(&deletes);
- OnceClosure own_object_cb =
- BindOnce(&DeleteCounter::VoidMethod0, Owned(counter));
- EXPECT_EQ(0, deletes);
- own_object_cb.Reset();
- EXPECT_EQ(1, deletes);
-}
-
-template <typename T>
-class BindVariantsTest : public ::testing::Test {
-};
-
-struct RepeatingTestConfig {
- template <typename Signature>
- using CallbackType = RepeatingCallback<Signature>;
- using ClosureType = RepeatingClosure;
-
- template <typename F, typename... Args>
- static CallbackType<MakeUnboundRunType<F, Args...>>
- Bind(F&& f, Args&&... args) {
- return BindRepeating(std::forward<F>(f), std::forward<Args>(args)...);
- }
-};
-
-struct OnceTestConfig {
- template <typename Signature>
- using CallbackType = OnceCallback<Signature>;
- using ClosureType = OnceClosure;
-
- template <typename F, typename... Args>
- static CallbackType<MakeUnboundRunType<F, Args...>>
- Bind(F&& f, Args&&... args) {
- return BindOnce(std::forward<F>(f), std::forward<Args>(args)...);
- }
-};
-
-using BindVariantsTestConfig = ::testing::Types<
- RepeatingTestConfig, OnceTestConfig>;
-TYPED_TEST_CASE(BindVariantsTest, BindVariantsTestConfig);
-
-template <typename TypeParam, typename Signature>
-using CallbackType = typename TypeParam::template CallbackType<Signature>;
-
-// Function type support.
-// - Normal function.
-// - Normal function bound with non-refcounted first argument.
-// - Method bound to non-const object.
-// - Method bound to scoped_refptr.
-// - Const method bound to non-const object.
-// - Const method bound to const object.
-// - Derived classes can be used with pointers to non-virtual base functions.
-// - Derived classes can be used with pointers to virtual base functions (and
-// preserve virtual dispatch).
-TYPED_TEST(BindVariantsTest, FunctionTypeSupport) {
- using ClosureType = typename TypeParam::ClosureType;
-
- StrictMock<HasRef> has_ref;
- StrictMock<NoRef> no_ref;
- StrictMock<NoRef> static_func_mock;
- const HasRef* const_has_ref_ptr = &has_ref;
- g_func_mock_ptr = &static_func_mock;
-
- EXPECT_CALL(static_func_mock, VoidMethod0());
- EXPECT_CALL(has_ref, AddRef()).Times(4);
- EXPECT_CALL(has_ref, Release()).Times(4);
- EXPECT_CALL(has_ref, VoidMethod0()).Times(2);
- EXPECT_CALL(has_ref, VoidConstMethod0()).Times(2);
-
- ClosureType normal_cb = TypeParam::Bind(&VoidFunc0);
- CallbackType<TypeParam, NoRef*()> normal_non_refcounted_cb =
- TypeParam::Bind(&PolymorphicIdentity<NoRef*>, &no_ref);
- std::move(normal_cb).Run();
- EXPECT_EQ(&no_ref, std::move(normal_non_refcounted_cb).Run());
-
- ClosureType method_cb = TypeParam::Bind(&HasRef::VoidMethod0, &has_ref);
- ClosureType method_refptr_cb = TypeParam::Bind(&HasRef::VoidMethod0,
- make_scoped_refptr(&has_ref));
- ClosureType const_method_nonconst_obj_cb =
- TypeParam::Bind(&HasRef::VoidConstMethod0, &has_ref);
- ClosureType const_method_const_obj_cb =
- TypeParam::Bind(&HasRef::VoidConstMethod0, const_has_ref_ptr);
- std::move(method_cb).Run();
- std::move(method_refptr_cb).Run();
- std::move(const_method_nonconst_obj_cb).Run();
- std::move(const_method_const_obj_cb).Run();
-
- Child child;
- child.value = 0;
- ClosureType virtual_set_cb = TypeParam::Bind(&Parent::VirtualSet, &child);
- std::move(virtual_set_cb).Run();
- EXPECT_EQ(kChildValue, child.value);
-
- child.value = 0;
- ClosureType non_virtual_set_cb =
- TypeParam::Bind(&Parent::NonVirtualSet, &child);
- std::move(non_virtual_set_cb).Run();
- EXPECT_EQ(kParentValue, child.value);
-}
-
-// Return value support.
-// - Function with return value.
-// - Method with return value.
-// - Const method with return value.
-// - Move-only return value.
-TYPED_TEST(BindVariantsTest, ReturnValues) {
- StrictMock<NoRef> static_func_mock;
- StrictMock<HasRef> has_ref;
- g_func_mock_ptr = &static_func_mock;
- const HasRef* const_has_ref_ptr = &has_ref;
-
- EXPECT_CALL(static_func_mock, IntMethod0()).WillOnce(Return(1337));
- EXPECT_CALL(has_ref, AddRef()).Times(4);
- EXPECT_CALL(has_ref, Release()).Times(4);
- EXPECT_CALL(has_ref, IntMethod0()).WillOnce(Return(31337));
- EXPECT_CALL(has_ref, IntConstMethod0())
- .WillOnce(Return(41337))
- .WillOnce(Return(51337));
- EXPECT_CALL(has_ref, UniquePtrMethod0())
- .WillOnce(Return(ByMove(MakeUnique<int>(42))));
-
- CallbackType<TypeParam, int()> normal_cb = TypeParam::Bind(&IntFunc0);
- CallbackType<TypeParam, int()> method_cb =
- TypeParam::Bind(&HasRef::IntMethod0, &has_ref);
- CallbackType<TypeParam, int()> const_method_nonconst_obj_cb =
- TypeParam::Bind(&HasRef::IntConstMethod0, &has_ref);
- CallbackType<TypeParam, int()> const_method_const_obj_cb =
- TypeParam::Bind(&HasRef::IntConstMethod0, const_has_ref_ptr);
- CallbackType<TypeParam, std::unique_ptr<int>()> move_only_rv_cb =
- TypeParam::Bind(&HasRef::UniquePtrMethod0, &has_ref);
- EXPECT_EQ(1337, std::move(normal_cb).Run());
- EXPECT_EQ(31337, std::move(method_cb).Run());
- EXPECT_EQ(41337, std::move(const_method_nonconst_obj_cb).Run());
- EXPECT_EQ(51337, std::move(const_method_const_obj_cb).Run());
- EXPECT_EQ(42, *std::move(move_only_rv_cb).Run());
-}
-
-// Argument binding tests.
-// - Argument binding to primitive.
-// - Argument binding to primitive pointer.
-// - Argument binding to a literal integer.
-// - Argument binding to a literal string.
-// - Argument binding with template function.
-// - Argument binding to an object.
-// - Argument binding to pointer to incomplete type.
-// - Argument gets type converted.
-// - Pointer argument gets converted.
-// - Const Reference forces conversion.
-TYPED_TEST(BindVariantsTest, ArgumentBinding) {
- int n = 2;
-
- EXPECT_EQ(n, TypeParam::Bind(&Identity, n).Run());
- EXPECT_EQ(&n, TypeParam::Bind(&PolymorphicIdentity<int*>, &n).Run());
- EXPECT_EQ(3, TypeParam::Bind(&Identity, 3).Run());
- EXPECT_STREQ("hi", TypeParam::Bind(&CStringIdentity, "hi").Run());
- EXPECT_EQ(4, TypeParam::Bind(&PolymorphicIdentity<int>, 4).Run());
-
- NoRefParent p;
- p.value = 5;
- EXPECT_EQ(5, TypeParam::Bind(&UnwrapNoRefParent, p).Run());
-
- IncompleteType* incomplete_ptr = reinterpret_cast<IncompleteType*>(123);
- EXPECT_EQ(incomplete_ptr,
- TypeParam::Bind(&PolymorphicIdentity<IncompleteType*>,
- incomplete_ptr).Run());
-
- NoRefChild c;
- c.value = 6;
- EXPECT_EQ(6, TypeParam::Bind(&UnwrapNoRefParent, c).Run());
-
- c.value = 7;
- EXPECT_EQ(7, TypeParam::Bind(&UnwrapNoRefParentPtr, &c).Run());
-
- c.value = 8;
- EXPECT_EQ(8, TypeParam::Bind(&UnwrapNoRefParentConstRef, c).Run());
-}
-
-// Unbound argument type support tests.
-// - Unbound value.
-// - Unbound pointer.
-// - Unbound reference.
-// - Unbound const reference.
-// - Unbound unsized array.
-// - Unbound sized array.
-// - Unbound array-of-arrays.
-TYPED_TEST(BindVariantsTest, UnboundArgumentTypeSupport) {
- CallbackType<TypeParam, void(int)> unbound_value_cb =
- TypeParam::Bind(&VoidPolymorphic<int>::Run);
- CallbackType<TypeParam, void(int*)> unbound_pointer_cb =
- TypeParam::Bind(&VoidPolymorphic<int*>::Run);
- CallbackType<TypeParam, void(int&)> unbound_ref_cb =
- TypeParam::Bind(&VoidPolymorphic<int&>::Run);
- CallbackType<TypeParam, void(const int&)> unbound_const_ref_cb =
- TypeParam::Bind(&VoidPolymorphic<const int&>::Run);
- CallbackType<TypeParam, void(int[])> unbound_unsized_array_cb =
- TypeParam::Bind(&VoidPolymorphic<int[]>::Run);
- CallbackType<TypeParam, void(int[2])> unbound_sized_array_cb =
- TypeParam::Bind(&VoidPolymorphic<int[2]>::Run);
- CallbackType<TypeParam, void(int[][2])> unbound_array_of_arrays_cb =
- TypeParam::Bind(&VoidPolymorphic<int[][2]>::Run);
- CallbackType<TypeParam, void(int&)> unbound_ref_with_bound_arg =
- TypeParam::Bind(&VoidPolymorphic<int, int&>::Run, 1);
-}
-
-// Function with unbound reference parameter.
-// - Original parameter is modified by callback.
-TYPED_TEST(BindVariantsTest, UnboundReferenceSupport) {
- int n = 0;
- CallbackType<TypeParam, void(int&)> unbound_ref_cb =
- TypeParam::Bind(&RefArgSet);
- std::move(unbound_ref_cb).Run(n);
- EXPECT_EQ(2, n);
-}
-
-// Unretained() wrapper support.
-// - Method bound to Unretained() non-const object.
-// - Const method bound to Unretained() non-const object.
-// - Const method bound to Unretained() const object.
-TYPED_TEST(BindVariantsTest, Unretained) {
- StrictMock<NoRef> no_ref;
- const NoRef* const_no_ref_ptr = &no_ref;
-
- EXPECT_CALL(no_ref, VoidMethod0());
- EXPECT_CALL(no_ref, VoidConstMethod0()).Times(2);
-
- TypeParam::Bind(&NoRef::VoidMethod0, Unretained(&no_ref)).Run();
- TypeParam::Bind(&NoRef::VoidConstMethod0, Unretained(&no_ref)).Run();
- TypeParam::Bind(&NoRef::VoidConstMethod0, Unretained(const_no_ref_ptr)).Run();
-}
-
-TYPED_TEST(BindVariantsTest, ScopedRefptr) {
- StrictMock<HasRef> has_ref;
- EXPECT_CALL(has_ref, AddRef()).Times(1);
- EXPECT_CALL(has_ref, Release()).Times(1);
-
- const scoped_refptr<HasRef> refptr(&has_ref);
- CallbackType<TypeParam, int()> scoped_refptr_const_ref_cb =
- TypeParam::Bind(&FunctionWithScopedRefptrFirstParam,
- base::ConstRef(refptr), 1);
- EXPECT_EQ(1, std::move(scoped_refptr_const_ref_cb).Run());
-}
-
-TYPED_TEST(BindVariantsTest, UniquePtrReceiver) {
+TEST_F(BindTest, UniquePtrReceiver) {
std::unique_ptr<StrictMock<NoRef>> no_ref(new StrictMock<NoRef>);
EXPECT_CALL(*no_ref, VoidMethod0()).Times(1);
- TypeParam::Bind(&NoRef::VoidMethod0, std::move(no_ref)).Run();
+ Bind(&NoRef::VoidMethod0, std::move(no_ref)).Run();
}
// Tests for Passed() wrapper support:
@@ -997,6 +803,7 @@ struct CustomDeleter {
using MoveOnlyTypesToTest =
::testing::Types<std::unique_ptr<DeleteCounter>,
+ std::unique_ptr<DeleteCounter>,
std::unique_ptr<DeleteCounter, CustomDeleter>>;
TYPED_TEST_CASE(BindMoveOnlyTypeTest, MoveOnlyTypesToTest);
@@ -1247,7 +1054,7 @@ TEST_F(BindTest, CapturelessLambda) {
EXPECT_TRUE(internal::IsConvertibleToRunType<decltype(f)>::value);
int i = 0;
- auto g = [i]() { (void)i; };
+ auto g = [i]() {};
EXPECT_FALSE(internal::IsConvertibleToRunType<decltype(g)>::value);
auto h = [](int, double) { return 'k'; };
@@ -1267,126 +1074,6 @@ TEST_F(BindTest, CapturelessLambda) {
EXPECT_EQ(42, x);
}
-TEST_F(BindTest, Cancellation) {
- EXPECT_CALL(no_ref_, VoidMethodWithIntArg(_)).Times(2);
-
- WeakPtrFactory<NoRef> weak_factory(&no_ref_);
- RepeatingCallback<void(int)> cb =
- BindRepeating(&NoRef::VoidMethodWithIntArg, weak_factory.GetWeakPtr());
- RepeatingClosure cb2 = BindRepeating(cb, 8);
- OnceClosure cb3 = BindOnce(cb, 8);
-
- OnceCallback<void(int)> cb4 =
- BindOnce(&NoRef::VoidMethodWithIntArg, weak_factory.GetWeakPtr());
- EXPECT_FALSE(cb4.IsCancelled());
-
- OnceClosure cb5 = BindOnce(std::move(cb4), 8);
-
- EXPECT_FALSE(cb.IsCancelled());
- EXPECT_FALSE(cb2.IsCancelled());
- EXPECT_FALSE(cb3.IsCancelled());
- EXPECT_FALSE(cb5.IsCancelled());
-
- cb.Run(6);
- cb2.Run();
-
- weak_factory.InvalidateWeakPtrs();
-
- EXPECT_TRUE(cb.IsCancelled());
- EXPECT_TRUE(cb2.IsCancelled());
- EXPECT_TRUE(cb3.IsCancelled());
- EXPECT_TRUE(cb5.IsCancelled());
-
- cb.Run(6);
- cb2.Run();
- std::move(cb3).Run();
- std::move(cb5).Run();
-}
-
-TEST_F(BindTest, OnceCallback) {
- // Check if Callback variants have declarations of conversions as expected.
- // Copy constructor and assignment of RepeatingCallback.
- static_assert(std::is_constructible<
- RepeatingClosure, const RepeatingClosure&>::value,
- "RepeatingClosure should be copyable.");
- static_assert(is_assignable<
- RepeatingClosure, const RepeatingClosure&>::value,
- "RepeatingClosure should be copy-assignable.");
-
- // Move constructor and assignment of RepeatingCallback.
- static_assert(std::is_constructible<
- RepeatingClosure, RepeatingClosure&&>::value,
- "RepeatingClosure should be movable.");
- static_assert(is_assignable<
- RepeatingClosure, RepeatingClosure&&>::value,
- "RepeatingClosure should be move-assignable");
-
- // Conversions from OnceCallback to RepeatingCallback.
- static_assert(!std::is_constructible<
- RepeatingClosure, const OnceClosure&>::value,
- "OnceClosure should not be convertible to RepeatingClosure.");
- static_assert(!is_assignable<
- RepeatingClosure, const OnceClosure&>::value,
- "OnceClosure should not be convertible to RepeatingClosure.");
-
- // Destructive conversions from OnceCallback to RepeatingCallback.
- static_assert(!std::is_constructible<
- RepeatingClosure, OnceClosure&&>::value,
- "OnceClosure should not be convertible to RepeatingClosure.");
- static_assert(!is_assignable<
- RepeatingClosure, OnceClosure&&>::value,
- "OnceClosure should not be convertible to RepeatingClosure.");
-
- // Copy constructor and assignment of OnceCallback.
- static_assert(!std::is_constructible<
- OnceClosure, const OnceClosure&>::value,
- "OnceClosure should not be copyable.");
- static_assert(!is_assignable<
- OnceClosure, const OnceClosure&>::value,
- "OnceClosure should not be copy-assignable");
-
- // Move constructor and assignment of OnceCallback.
- static_assert(std::is_constructible<
- OnceClosure, OnceClosure&&>::value,
- "OnceClosure should be movable.");
- static_assert(is_assignable<
- OnceClosure, OnceClosure&&>::value,
- "OnceClosure should be move-assignable.");
-
- // Conversions from RepeatingCallback to OnceCallback.
- static_assert(std::is_constructible<
- OnceClosure, const RepeatingClosure&>::value,
- "RepeatingClosure should be convertible to OnceClosure.");
- static_assert(is_assignable<
- OnceClosure, const RepeatingClosure&>::value,
- "RepeatingClosure should be convertible to OnceClosure.");
-
- // Destructive conversions from RepeatingCallback to OnceCallback.
- static_assert(std::is_constructible<
- OnceClosure, RepeatingClosure&&>::value,
- "RepeatingClosure should be convertible to OnceClosure.");
- static_assert(is_assignable<
- OnceClosure, RepeatingClosure&&>::value,
- "RepeatingClosure should be covretible to OnceClosure.");
-
- OnceClosure cb = BindOnce(&VoidPolymorphic<>::Run);
- std::move(cb).Run();
-
- // RepeatingCallback should be convertible to OnceCallback.
- OnceClosure cb2 = BindRepeating(&VoidPolymorphic<>::Run);
- std::move(cb2).Run();
-
- RepeatingClosure cb3 = BindRepeating(&VoidPolymorphic<>::Run);
- cb = cb3;
- std::move(cb).Run();
-
- cb = std::move(cb2);
-
- OnceCallback<void(int)> cb4 = BindOnce(
- &VoidPolymorphic<std::unique_ptr<int>, int>::Run, MakeUnique<int>(0));
- BindOnce(std::move(cb4), 1).Run();
-}
-
// Callback construction and assignment tests.
// - Construction from an InvokerStorageHolder should not cause ref/deref.
// - Assignment from other callback should only cause one ref
@@ -1414,12 +1101,17 @@ TEST_F(BindTest, WindowsCallingConventions) {
}
#endif
+#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && GTEST_HAS_DEATH_TEST
+
// Test null callbacks cause a DCHECK.
TEST(BindDeathTest, NullCallback) {
base::Callback<void(int)> null_cb;
ASSERT_TRUE(null_cb.is_null());
- EXPECT_DCHECK_DEATH(base::Bind(null_cb, 42));
+ EXPECT_DEATH(base::Bind(null_cb, 42), "");
}
+#endif // (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) &&
+ // GTEST_HAS_DEATH_TEST
+
} // namespace
} // namespace base
diff --git a/base/bit_cast.h b/base/bit_cast.h
index 90dd925e86..c9514bceef 100644
--- a/base/bit_cast.h
+++ b/base/bit_cast.h
@@ -9,7 +9,6 @@
#include <type_traits>
#include "base/compiler_specific.h"
-#include "base/template_util.h"
#include "build/build_config.h"
// bit_cast<Dest,Source> is a template function that implements the equivalent
@@ -64,10 +63,34 @@ template <class Dest, class Source>
inline Dest bit_cast(const Source& source) {
static_assert(sizeof(Dest) == sizeof(Source),
"bit_cast requires source and destination to be the same size");
- static_assert(base::is_trivially_copyable<Dest>::value,
- "bit_cast requires the destination type to be copyable");
- static_assert(base::is_trivially_copyable<Source>::value,
- "bit_cast requires the source type to be copyable");
+
+#if (__GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1) || \
+ (defined(__clang__) && defined(_LIBCPP_VERSION)))
+ // GCC 5.1 contains the first libstdc++ with is_trivially_copyable.
+ // Assume libc++ Just Works: is_trivially_copyable added on May 13th 2011.
+ // However, with libc++ when GCC is the compiler the trait is buggy, see
+ // crbug.com/607158, so fall back to the less strict variant for non-clang.
+ static_assert(std::is_trivially_copyable<Dest>::value,
+ "non-trivially-copyable bit_cast is undefined");
+ static_assert(std::is_trivially_copyable<Source>::value,
+ "non-trivially-copyable bit_cast is undefined");
+#elif HAS_FEATURE(is_trivially_copyable)
+ // The compiler supports an equivalent intrinsic.
+ static_assert(__is_trivially_copyable(Dest),
+ "non-trivially-copyable bit_cast is undefined");
+ static_assert(__is_trivially_copyable(Source),
+ "non-trivially-copyable bit_cast is undefined");
+#elif COMPILER_GCC
+ // Fallback to compiler intrinsic on GCC and clang (which pretends to be
+ // GCC). This isn't quite the same as is_trivially_copyable but it'll do for
+ // our purpose.
+ static_assert(__has_trivial_copy(Dest),
+ "non-trivially-copyable bit_cast is undefined");
+ static_assert(__has_trivial_copy(Source),
+ "non-trivially-copyable bit_cast is undefined");
+#else
+ // Do nothing, let the bots handle it.
+#endif
Dest dest;
memcpy(&dest, &source, sizeof(dest));
diff --git a/base/bits.h b/base/bits.h
index d101cb731a..a3a59d1dfa 100644
--- a/base/bits.h
+++ b/base/bits.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -10,13 +10,8 @@
#include <stddef.h>
#include <stdint.h>
-#include "base/compiler_specific.h"
#include "base/logging.h"
-#if defined(COMPILER_MSVC)
-#include <intrin.h>
-#endif
-
namespace base {
namespace bits {
@@ -54,58 +49,6 @@ inline size_t Align(size_t size, size_t alignment) {
return (size + alignment - 1) & ~(alignment - 1);
}
-// These functions count the number of leading zeros in a binary value, starting
-// with the most significant bit. C does not have an operator to do this, but
-// fortunately the various compilers have built-ins that map to fast underlying
-// processor instructions.
-#if defined(COMPILER_MSVC)
-
-ALWAYS_INLINE uint32_t CountLeadingZeroBits32(uint32_t x) {
- unsigned long index;
- return LIKELY(_BitScanReverse(&index, x)) ? (31 - index) : 32;
-}
-
-#if defined(ARCH_CPU_64_BITS)
-
-// MSVC only supplies _BitScanForward64 when building for a 64-bit target.
-ALWAYS_INLINE uint64_t CountLeadingZeroBits64(uint64_t x) {
- unsigned long index;
- return LIKELY(_BitScanReverse64(&index, x)) ? (63 - index) : 64;
-}
-
-#endif
-
-#elif defined(COMPILER_GCC)
-
-// This is very annoying. __builtin_clz has undefined behaviour for an input of
-// 0, even though there's clearly a return value that makes sense, and even
-// though some processor clz instructions have defined behaviour for 0. We could
-// drop to raw __asm__ to do better, but we'll avoid doing that unless we see
-// proof that we need to.
-ALWAYS_INLINE uint32_t CountLeadingZeroBits32(uint32_t x) {
- return LIKELY(x) ? __builtin_clz(x) : 32;
-}
-
-ALWAYS_INLINE uint64_t CountLeadingZeroBits64(uint64_t x) {
- return LIKELY(x) ? __builtin_clzll(x) : 64;
-}
-
-#endif
-
-#if defined(ARCH_CPU_64_BITS)
-
-ALWAYS_INLINE size_t CountLeadingZeroBitsSizeT(size_t x) {
- return CountLeadingZeroBits64(x);
-}
-
-#else
-
-ALWAYS_INLINE size_t CountLeadingZeroBitsSizeT(size_t x) {
- return CountLeadingZeroBits32(x);
-}
-
-#endif
-
} // namespace bits
} // namespace base
diff --git a/base/bits_unittest.cc b/base/bits_unittest.cc
index 270b8ef7d3..4f5b6ea49e 100644
--- a/base/bits_unittest.cc
+++ b/base/bits_unittest.cc
@@ -61,25 +61,5 @@ TEST(BitsTest, Align) {
EXPECT_EQ(kSizeTMax / 2 + 1, Align(1, kSizeTMax / 2 + 1));
}
-TEST(BitsTest, CLZWorks) {
- EXPECT_EQ(32u, CountLeadingZeroBits32(0u));
- EXPECT_EQ(31u, CountLeadingZeroBits32(1u));
- EXPECT_EQ(1u, CountLeadingZeroBits32(1u << 30));
- EXPECT_EQ(0u, CountLeadingZeroBits32(1u << 31));
-
-#if defined(ARCH_CPU_64_BITS)
- EXPECT_EQ(64u, CountLeadingZeroBitsSizeT(0ull));
- EXPECT_EQ(63u, CountLeadingZeroBitsSizeT(1ull));
- EXPECT_EQ(32u, CountLeadingZeroBitsSizeT(1ull << 31));
- EXPECT_EQ(1u, CountLeadingZeroBitsSizeT(1ull << 62));
- EXPECT_EQ(0u, CountLeadingZeroBitsSizeT(1ull << 63));
-#else
- EXPECT_EQ(32u, CountLeadingZeroBitsSizeT(0u));
- EXPECT_EQ(31u, CountLeadingZeroBitsSizeT(1u));
- EXPECT_EQ(1u, CountLeadingZeroBitsSizeT(1u << 30));
- EXPECT_EQ(0u, CountLeadingZeroBitsSizeT(1u << 31));
-#endif
-}
-
} // namespace bits
} // namespace base
diff --git a/base/callback.h b/base/callback.h
index 40bd5208a8..e087c731d1 100644
--- a/base/callback.h
+++ b/base/callback.h
@@ -12,130 +12,382 @@
// Closure should #include "base/callback_forward.h" instead of this file.
// -----------------------------------------------------------------------------
-// Usage documentation
+// Introduction
// -----------------------------------------------------------------------------
//
-// See //docs/callback.md for documentation.
+// The templated Callback class is a generalized function object. Together
+// with the Bind() function in bind.h, they provide a type-safe method for
+// performing partial application of functions.
+//
+// Partial application (or "currying") is the process of binding a subset of
+// a function's arguments to produce another function that takes fewer
+// arguments. This can be used to pass around a unit of delayed execution,
+// much like lexical closures are used in other languages. For example, it
+// is used in Chromium code to schedule tasks on different MessageLoops.
+//
+// A callback with no unbound input parameters (base::Callback<void()>)
+// is called a base::Closure. Note that this is NOT the same as what other
+// languages refer to as a closure -- it does not retain a reference to its
+// enclosing environment.
+//
+// MEMORY MANAGEMENT AND PASSING
+//
+// The Callback objects themselves should be passed by const-reference, and
+// stored by copy. They internally store their state via a refcounted class
+// and thus do not need to be deleted.
+//
+// The reason to pass via a const-reference is to avoid unnecessary
+// AddRef/Release pairs to the internal state.
+//
+//
+// -----------------------------------------------------------------------------
+// Quick reference for basic stuff
+// -----------------------------------------------------------------------------
+//
+// BINDING A BARE FUNCTION
+//
+// int Return5() { return 5; }
+// base::Callback<int()> func_cb = base::Bind(&Return5);
+// LOG(INFO) << func_cb.Run(); // Prints 5.
+//
+// BINDING A CLASS METHOD
+//
+// The first argument to bind is the member function to call, the second is
+// the object on which to call it.
+//
+// class Ref : public base::RefCountedThreadSafe<Ref> {
+// public:
+// int Foo() { return 3; }
+// void PrintBye() { LOG(INFO) << "bye."; }
+// };
+// scoped_refptr<Ref> ref = new Ref();
+// base::Callback<void()> ref_cb = base::Bind(&Ref::Foo, ref);
+// LOG(INFO) << ref_cb.Run(); // Prints out 3.
+//
+// By default the object must support RefCounted or you will get a compiler
+// error. If you're passing between threads, be sure it's
+// RefCountedThreadSafe! See "Advanced binding of member functions" below if
+// you don't want to use reference counting.
+//
+// RUNNING A CALLBACK
+//
+// Callbacks can be run with their "Run" method, which has the same
+// signature as the template argument to the callback.
+//
+// void DoSomething(const base::Callback<void(int, std::string)>& callback) {
+// callback.Run(5, "hello");
+// }
+//
+// Callbacks can be run more than once (they don't get deleted or marked when
+// run). However, this precludes using base::Passed (see below).
+//
+// void DoSomething(const base::Callback<double(double)>& callback) {
+// double myresult = callback.Run(3.14159);
+// myresult += callback.Run(2.71828);
+// }
+//
+// PASSING UNBOUND INPUT PARAMETERS
+//
+// Unbound parameters are specified at the time a callback is Run(). They are
+// specified in the Callback template type:
+//
+// void MyFunc(int i, const std::string& str) {}
+// base::Callback<void(int, const std::string&)> cb = base::Bind(&MyFunc);
+// cb.Run(23, "hello, world");
+//
+// PASSING BOUND INPUT PARAMETERS
+//
+// Bound parameters are specified when you create thee callback as arguments
+// to Bind(). They will be passed to the function and the Run()ner of the
+// callback doesn't see those values or even know that the function it's
+// calling.
+//
+// void MyFunc(int i, const std::string& str) {}
+// base::Callback<void()> cb = base::Bind(&MyFunc, 23, "hello world");
+// cb.Run();
+//
+// A callback with no unbound input parameters (base::Callback<void()>)
+// is called a base::Closure. So we could have also written:
+//
+// base::Closure cb = base::Bind(&MyFunc, 23, "hello world");
+//
+// When calling member functions, bound parameters just go after the object
+// pointer.
+//
+// base::Closure cb = base::Bind(&MyClass::MyFunc, this, 23, "hello world");
+//
+// PARTIAL BINDING OF PARAMETERS
+//
+// You can specify some parameters when you create the callback, and specify
+// the rest when you execute the callback.
+//
+// void MyFunc(int i, const std::string& str) {}
+// base::Callback<void(const std::string&)> cb = base::Bind(&MyFunc, 23);
+// cb.Run("hello world");
+//
+// When calling a function bound parameters are first, followed by unbound
+// parameters.
+//
+//
+// -----------------------------------------------------------------------------
+// Quick reference for advanced binding
+// -----------------------------------------------------------------------------
+//
+// BINDING A CLASS METHOD WITH WEAK POINTERS
+//
+// base::Bind(&MyClass::Foo, GetWeakPtr());
+//
+// The callback will not be run if the object has already been destroyed.
+// DANGER: weak pointers are not threadsafe, so don't use this
+// when passing between threads!
+//
+// BINDING A CLASS METHOD WITH MANUAL LIFETIME MANAGEMENT
+//
+// base::Bind(&MyClass::Foo, base::Unretained(this));
+//
+// This disables all lifetime management on the object. You're responsible
+// for making sure the object is alive at the time of the call. You break it,
+// you own it!
+//
+// BINDING A CLASS METHOD AND HAVING THE CALLBACK OWN THE CLASS
+//
+// MyClass* myclass = new MyClass;
+// base::Bind(&MyClass::Foo, base::Owned(myclass));
+//
+// The object will be deleted when the callback is destroyed, even if it's
+// not run (like if you post a task during shutdown). Potentially useful for
+// "fire and forget" cases.
+//
+// IGNORING RETURN VALUES
+//
+// Sometimes you want to call a function that returns a value in a callback
+// that doesn't expect a return value.
+//
+// int DoSomething(int arg) { cout << arg << endl; }
+// base::Callback<void(int)> cb =
+// base::Bind(base::IgnoreResult(&DoSomething));
+//
+//
+// -----------------------------------------------------------------------------
+// Quick reference for binding parameters to Bind()
+// -----------------------------------------------------------------------------
+//
+// Bound parameters are specified as arguments to Bind() and are passed to the
+// function. A callback with no parameters or no unbound parameters is called a
+// Closure (base::Callback<void()> and base::Closure are the same thing).
+//
+// PASSING PARAMETERS OWNED BY THE CALLBACK
+//
+// void Foo(int* arg) { cout << *arg << endl; }
+// int* pn = new int(1);
+// base::Closure foo_callback = base::Bind(&foo, base::Owned(pn));
+//
+// The parameter will be deleted when the callback is destroyed, even if it's
+// not run (like if you post a task during shutdown).
+//
+// PASSING PARAMETERS AS A scoped_ptr
+//
+// void TakesOwnership(std::unique_ptr<Foo> arg) {}
+// std::unique_ptr<Foo> f(new Foo);
+// // f becomes null during the following call.
+// base::Closure cb = base::Bind(&TakesOwnership, base::Passed(&f));
+//
+// Ownership of the parameter will be with the callback until the it is run,
+// when ownership is passed to the callback function. This means the callback
+// can only be run once. If the callback is never run, it will delete the
+// object when it's destroyed.
+//
+// PASSING PARAMETERS AS A scoped_refptr
+//
+// void TakesOneRef(scoped_refptr<Foo> arg) {}
+// scoped_refptr<Foo> f(new Foo)
+// base::Closure cb = base::Bind(&TakesOneRef, f);
+//
+// This should "just work." The closure will take a reference as long as it
+// is alive, and another reference will be taken for the called function.
+//
+// PASSING PARAMETERS BY REFERENCE
+//
+// Const references are *copied* unless ConstRef is used. Example:
+//
+// void foo(const int& arg) { printf("%d %p\n", arg, &arg); }
+// int n = 1;
+// base::Closure has_copy = base::Bind(&foo, n);
+// base::Closure has_ref = base::Bind(&foo, base::ConstRef(n));
+// n = 2;
+// foo(n); // Prints "2 0xaaaaaaaaaaaa"
+// has_copy.Run(); // Prints "1 0xbbbbbbbbbbbb"
+// has_ref.Run(); // Prints "2 0xaaaaaaaaaaaa"
+//
+// Normally parameters are copied in the closure. DANGER: ConstRef stores a
+// const reference instead, referencing the original parameter. This means
+// that you must ensure the object outlives the callback!
+//
+//
+// -----------------------------------------------------------------------------
+// Implementation notes
+// -----------------------------------------------------------------------------
+//
+// WHERE IS THIS DESIGN FROM:
+//
+// The design Callback and Bind is heavily influenced by C++'s
+// tr1::function/tr1::bind, and by the "Google Callback" system used inside
+// Google.
+//
+//
+// HOW THE IMPLEMENTATION WORKS:
+//
+// There are three main components to the system:
+// 1) The Callback classes.
+// 2) The Bind() functions.
+// 3) The arguments wrappers (e.g., Unretained() and ConstRef()).
+//
+// The Callback classes represent a generic function pointer. Internally,
+// it stores a refcounted piece of state that represents the target function
+// and all its bound parameters. Each Callback specialization has a templated
+// constructor that takes an BindState<>*. In the context of the constructor,
+// the static type of this BindState<> pointer uniquely identifies the
+// function it is representing, all its bound parameters, and a Run() method
+// that is capable of invoking the target.
+//
+// Callback's constructor takes the BindState<>* that has the full static type
+// and erases the target function type as well as the types of the bound
+// parameters. It does this by storing a pointer to the specific Run()
+// function, and upcasting the state of BindState<>* to a
+// BindStateBase*. This is safe as long as this BindStateBase pointer
+// is only used with the stored Run() pointer.
+//
+// To BindState<> objects are created inside the Bind() functions.
+// These functions, along with a set of internal templates, are responsible for
+//
+// - Unwrapping the function signature into return type, and parameters
+// - Determining the number of parameters that are bound
+// - Creating the BindState storing the bound parameters
+// - Performing compile-time asserts to avoid error-prone behavior
+// - Returning an Callback<> with an arity matching the number of unbound
+// parameters and that knows the correct refcounting semantics for the
+// target object if we are binding a method.
+//
+// The Bind functions do the above using type-inference, and template
+// specializations.
+//
+// By default Bind() will store copies of all bound parameters, and attempt
+// to refcount a target object if the function being bound is a class method.
+// These copies are created even if the function takes parameters as const
+// references. (Binding to non-const references is forbidden, see bind.h.)
+//
+// To change this behavior, we introduce a set of argument wrappers
+// (e.g., Unretained(), and ConstRef()). These are simple container templates
+// that are passed by value, and wrap a pointer to argument. See the
+// file-level comment in base/bind_helpers.h for more info.
+//
+// These types are passed to the Unwrap() functions, and the MaybeRefcount()
+// functions respectively to modify the behavior of Bind(). The Unwrap()
+// and MaybeRefcount() functions change behavior by doing partial
+// specialization based on whether or not a parameter is a wrapper type.
+//
+// ConstRef() is similar to tr1::cref. Unretained() is specific to Chromium.
+//
+//
+// WHY NOT TR1 FUNCTION/BIND?
+//
+// Direct use of tr1::function and tr1::bind was considered, but ultimately
+// rejected because of the number of copy constructors invocations involved
+// in the binding of arguments during construction, and the forwarding of
+// arguments during invocation. These copies will no longer be an issue in
+// C++0x because C++0x will support rvalue reference allowing for the compiler
+// to avoid these copies. However, waiting for C++0x is not an option.
+//
+// Measured with valgrind on gcc version 4.4.3 (Ubuntu 4.4.3-4ubuntu5), the
+// tr1::bind call itself will invoke a non-trivial copy constructor three times
+// for each bound parameter. Also, each when passing a tr1::function, each
+// bound argument will be copied again.
+//
+// In addition to the copies taken at binding and invocation, copying a
+// tr1::function causes a copy to be made of all the bound parameters and
+// state.
+//
+// Furthermore, in Chromium, it is desirable for the Callback to take a
+// reference on a target object when representing a class method call. This
+// is not supported by tr1.
+//
+// Lastly, tr1::function and tr1::bind has a more general and flexible API.
+// This includes things like argument reordering by use of
+// tr1::bind::placeholder, support for non-const reference parameters, and some
+// limited amount of subtyping of the tr1::function object (e.g.,
+// tr1::function<int(int)> is convertible to tr1::function<void(int)>).
+//
+// These are not features that are required in Chromium. Some of them, such as
+// allowing for reference parameters, and subtyping of functions, may actually
+// become a source of errors. Removing support for these features actually
+// allows for a simpler implementation, and a terser Currying API.
+//
+//
+// WHY NOT GOOGLE CALLBACKS?
+//
+// The Google callback system also does not support refcounting. Furthermore,
+// its implementation has a number of strange edge cases with respect to type
+// conversion of its arguments. In particular, the argument's constness must
+// at times match exactly the function signature, or the type-inference might
+// break. Given the above, writing a custom solution was easier.
+//
+//
+// MISSING FUNCTIONALITY
+// - Invoking the return of Bind. Bind(&foo).Run() does not work;
+// - Binding arrays to functions that take a non-const pointer.
+// Example:
+// void Foo(const char* ptr);
+// void Bar(char* ptr);
+// Bind(&Foo, "test");
+// Bind(&Bar, "test"); // This fails because ptr is not const.
+//
+// If you are thinking of forward declaring Callback in your own header file,
+// please include "base/callback_forward.h" instead.
namespace base {
-namespace internal {
-
-template <typename CallbackType>
-struct IsOnceCallback : std::false_type {};
-
-template <typename Signature>
-struct IsOnceCallback<OnceCallback<Signature>> : std::true_type {};
-
-// RunMixin provides different variants of `Run()` function to `Callback<>`
-// based on the type of callback.
-template <typename CallbackType>
-class RunMixin;
-
-// Specialization for OnceCallback.
-template <typename R, typename... Args>
-class RunMixin<OnceCallback<R(Args...)>> {
- private:
- using CallbackType = OnceCallback<R(Args...)>;
-
- public:
- using PolymorphicInvoke = R(*)(internal::BindStateBase*, Args&&...);
-
- R Run(Args... /* args */) const & {
- // Note: even though this static_assert will trivially always fail, it
- // cannot be simply replaced with static_assert(false, ...) because:
- // - Per [dcl.dcl]/p4, a program is ill-formed if the constant-expression
- // argument does not evaluate to true.
- // - Per [temp.res]/p8, if no valid specialization can be generated for a
- // template definition, and that template is not instantiated, the
- // template definition is ill-formed, no diagnostic required.
- // These two clauses, taken together, would allow a conforming C++ compiler
- // to immediately reject static_assert(false, ...), even inside an
- // uninstantiated template.
- static_assert(!IsOnceCallback<CallbackType>::value,
- "OnceCallback::Run() may only be invoked on a non-const "
- "rvalue, i.e. std::move(callback).Run().");
- }
-
- R Run(Args... args) && {
- // Move the callback instance into a local variable before the invocation,
- // that ensures the internal state is cleared after the invocation.
- // It's not safe to touch |this| after the invocation, since running the
- // bound function may destroy |this|.
- CallbackType cb = static_cast<CallbackType&&>(*this);
- PolymorphicInvoke f =
- reinterpret_cast<PolymorphicInvoke>(cb.polymorphic_invoke());
- return f(cb.bind_state_.get(), std::forward<Args>(args)...);
- }
-};
-
-// Specialization for RepeatingCallback.
-template <typename R, typename... Args>
-class RunMixin<RepeatingCallback<R(Args...)>> {
+template <typename R, typename... Args, internal::CopyMode copy_mode>
+class Callback<R(Args...), copy_mode>
+ : public internal::CallbackBase<copy_mode> {
private:
- using CallbackType = RepeatingCallback<R(Args...)>;
-
- public:
- using PolymorphicInvoke = R(*)(internal::BindStateBase*, Args&&...);
-
- R Run(Args... args) const {
- const CallbackType& cb = static_cast<const CallbackType&>(*this);
- PolymorphicInvoke f =
- reinterpret_cast<PolymorphicInvoke>(cb.polymorphic_invoke());
- return f(cb.bind_state_.get(), std::forward<Args>(args)...);
- }
-};
-
-template <typename From, typename To>
-struct IsCallbackConvertible : std::false_type {};
-
-template <typename Signature>
-struct IsCallbackConvertible<RepeatingCallback<Signature>,
- OnceCallback<Signature>> : std::true_type {};
+ using PolymorphicInvoke = R (*)(internal::BindStateBase*, Args&&...);
-} // namespace internal
-
-template <typename R,
- typename... Args,
- internal::CopyMode copy_mode,
- internal::RepeatMode repeat_mode>
-class Callback<R(Args...), copy_mode, repeat_mode>
- : public internal::CallbackBase<copy_mode>,
- public internal::RunMixin<Callback<R(Args...), copy_mode, repeat_mode>> {
public:
- static_assert(repeat_mode != internal::RepeatMode::Once ||
- copy_mode == internal::CopyMode::MoveOnly,
- "OnceCallback must be MoveOnly.");
-
- using RunType = R(Args...);
+ // MSVC 2013 doesn't support Type Alias of function types.
+ // Revisit this after we update it to newer version.
+ typedef R RunType(Args...);
Callback() : internal::CallbackBase<copy_mode>(nullptr) {}
- explicit Callback(internal::BindStateBase* bind_state)
+ Callback(internal::BindStateBase* bind_state,
+ PolymorphicInvoke invoke_func)
: internal::CallbackBase<copy_mode>(bind_state) {
- }
-
- template <typename OtherCallback,
- typename = typename std::enable_if<
- internal::IsCallbackConvertible<OtherCallback, Callback>::value
- >::type>
- Callback(OtherCallback other)
- : internal::CallbackBase<copy_mode>(std::move(other)) {}
-
- template <typename OtherCallback,
- typename = typename std::enable_if<
- internal::IsCallbackConvertible<OtherCallback, Callback>::value
- >::type>
- Callback& operator=(OtherCallback other) {
- static_cast<internal::CallbackBase<copy_mode>&>(*this) = std::move(other);
- return *this;
+ using InvokeFuncStorage =
+ typename internal::CallbackBase<copy_mode>::InvokeFuncStorage;
+ this->polymorphic_invoke_ =
+ reinterpret_cast<InvokeFuncStorage>(invoke_func);
}
bool Equals(const Callback& other) const {
return this->EqualsInternal(other);
}
- friend class internal::RunMixin<Callback>;
+ // Run() makes an extra copy compared to directly calling the bound function
+ // if an argument is passed-by-value and is copyable-but-not-movable:
+ // i.e. below copies CopyableNonMovableType twice.
+ // void F(CopyableNonMovableType) {}
+ // Bind(&F).Run(CopyableNonMovableType());
+ //
+ // We can not fully apply Perfect Forwarding idiom to the callchain from
+ // Callback::Run() to the target function. Perfect Forwarding requires
+ // knowing how the caller will pass the arguments. However, the signature of
+ // InvokerType::Run() needs to be fixed in the callback constructor, so Run()
+ // cannot template its arguments based on how it's called.
+ R Run(Args... args) const {
+ PolymorphicInvoke f =
+ reinterpret_cast<PolymorphicInvoke>(this->polymorphic_invoke_);
+ return f(this->bind_state_.get(), std::forward<Args>(args)...);
+ }
};
} // namespace base
diff --git a/base/callback_forward.h b/base/callback_forward.h
index 13eed0eb0d..8b9b89cdc2 100644
--- a/base/callback_forward.h
+++ b/base/callback_forward.h
@@ -12,37 +12,19 @@ namespace internal {
// MoveOnly indicates the Callback is not copyable but movable, and Copyable
// indicates it is copyable and movable.
enum class CopyMode {
- MoveOnly,
- Copyable,
-};
-
-enum class RepeatMode {
- Once,
- Repeating,
+ MoveOnly, Copyable,
};
} // namespace internal
template <typename Signature,
- internal::CopyMode copy_mode = internal::CopyMode::Copyable,
- internal::RepeatMode repeat_mode = internal::RepeatMode::Repeating>
+ internal::CopyMode copy_mode = internal::CopyMode::Copyable>
class Callback;
// Syntactic sugar to make Callback<void()> easier to declare since it
// will be used in a lot of APIs with delayed execution.
using Closure = Callback<void()>;
-template <typename Signature>
-using OnceCallback = Callback<Signature,
- internal::CopyMode::MoveOnly,
- internal::RepeatMode::Once>;
-template <typename Signature>
-using RepeatingCallback = Callback<Signature,
- internal::CopyMode::Copyable,
- internal::RepeatMode::Repeating>;
-using OnceClosure = OnceCallback<void()>;
-using RepeatingClosure = RepeatingCallback<void()>;
-
} // namespace base
#endif // BASE_CALLBACK_FORWARD_H_
diff --git a/base/callback_helpers.h b/base/callback_helpers.h
index ec3d6cbf16..782371f6e7 100644
--- a/base/callback_helpers.h
+++ b/base/callback_helpers.h
@@ -20,13 +20,10 @@
namespace base {
-template <typename Signature,
- internal::CopyMode copy_mode,
- internal::RepeatMode repeat_mode>
-base::Callback<Signature, copy_mode, repeat_mode> ResetAndReturn(
- base::Callback<Signature, copy_mode, repeat_mode>* cb) {
- base::Callback<Signature, copy_mode, repeat_mode> ret(std::move(*cb));
- DCHECK(!*cb);
+template <typename Sig>
+base::Callback<Sig> ResetAndReturn(base::Callback<Sig>* cb) {
+ base::Callback<Sig> ret(*cb);
+ cb->Reset();
return ret;
}
diff --git a/base/callback_helpers_unittest.cc b/base/callback_helpers_unittest.cc
index 6c48d7ce4e..8283996379 100644
--- a/base/callback_helpers_unittest.cc
+++ b/base/callback_helpers_unittest.cc
@@ -14,24 +14,6 @@ void Increment(int* value) {
(*value)++;
}
-TEST(CallbackHelpersTest, TestResetAndReturn) {
- int run_count = 0;
-
- base::Closure cb = base::Bind(&Increment, &run_count);
- EXPECT_EQ(0, run_count);
- base::ResetAndReturn(&cb).Run();
- EXPECT_EQ(1, run_count);
- EXPECT_FALSE(cb);
-
- run_count = 0;
-
- base::OnceClosure cb2 = base::BindOnce(&Increment, &run_count);
- EXPECT_EQ(0, run_count);
- base::ResetAndReturn(&cb2).Run();
- EXPECT_EQ(1, run_count);
- EXPECT_FALSE(cb2);
-}
-
TEST(CallbackHelpersTest, TestScopedClosureRunnerExitScope) {
int run_count = 0;
{
diff --git a/base/callback_internal.cc b/base/callback_internal.cc
index 4330e9cce5..4c8ccae932 100644
--- a/base/callback_internal.cc
+++ b/base/callback_internal.cc
@@ -9,75 +9,40 @@
namespace base {
namespace internal {
-namespace {
-
-bool ReturnFalse(const BindStateBase*) {
- return false;
-}
-
-} // namespace
-
-BindStateBase::BindStateBase(InvokeFuncStorage polymorphic_invoke,
- void (*destructor)(const BindStateBase*))
- : BindStateBase(polymorphic_invoke, destructor, &ReturnFalse) {
-}
-
-BindStateBase::BindStateBase(InvokeFuncStorage polymorphic_invoke,
- void (*destructor)(const BindStateBase*),
- bool (*is_cancelled)(const BindStateBase*))
- : polymorphic_invoke_(polymorphic_invoke),
- ref_count_(0),
- destructor_(destructor),
- is_cancelled_(is_cancelled) {}
-
-void BindStateBase::AddRef() const {
+void BindStateBase::AddRef() {
AtomicRefCountInc(&ref_count_);
}
-void BindStateBase::Release() const {
+void BindStateBase::Release() {
if (!AtomicRefCountDec(&ref_count_))
destructor_(this);
}
-CallbackBase<CopyMode::MoveOnly>::CallbackBase(CallbackBase&& c) = default;
-
-CallbackBase<CopyMode::MoveOnly>&
-CallbackBase<CopyMode::MoveOnly>::operator=(CallbackBase&& c) = default;
-
-CallbackBase<CopyMode::MoveOnly>::CallbackBase(
- const CallbackBase<CopyMode::Copyable>& c)
- : bind_state_(c.bind_state_) {}
-
-CallbackBase<CopyMode::MoveOnly>& CallbackBase<CopyMode::MoveOnly>::operator=(
- const CallbackBase<CopyMode::Copyable>& c) {
- bind_state_ = c.bind_state_;
- return *this;
+CallbackBase<CopyMode::MoveOnly>::CallbackBase(CallbackBase&& c)
+ : bind_state_(std::move(c.bind_state_)),
+ polymorphic_invoke_(c.polymorphic_invoke_) {
+ c.polymorphic_invoke_ = nullptr;
}
-CallbackBase<CopyMode::MoveOnly>::CallbackBase(
- CallbackBase<CopyMode::Copyable>&& c)
- : bind_state_(std::move(c.bind_state_)) {}
-
-CallbackBase<CopyMode::MoveOnly>& CallbackBase<CopyMode::MoveOnly>::operator=(
- CallbackBase<CopyMode::Copyable>&& c) {
+CallbackBase<CopyMode::MoveOnly>&
+CallbackBase<CopyMode::MoveOnly>::operator=(CallbackBase&& c) {
bind_state_ = std::move(c.bind_state_);
+ polymorphic_invoke_ = c.polymorphic_invoke_;
+ c.polymorphic_invoke_ = nullptr;
return *this;
}
void CallbackBase<CopyMode::MoveOnly>::Reset() {
+ polymorphic_invoke_ = nullptr;
// NULL the bind_state_ last, since it may be holding the last ref to whatever
// object owns us, and we may be deleted after that.
bind_state_ = nullptr;
}
-bool CallbackBase<CopyMode::MoveOnly>::IsCancelled() const {
- DCHECK(bind_state_);
- return bind_state_->IsCancelled();
-}
-
bool CallbackBase<CopyMode::MoveOnly>::EqualsInternal(
const CallbackBase& other) const {
- return bind_state_ == other.bind_state_;
+ return bind_state_.get() == other.bind_state_.get() &&
+ polymorphic_invoke_ == other.polymorphic_invoke_;
}
CallbackBase<CopyMode::MoveOnly>::CallbackBase(
@@ -92,18 +57,24 @@ CallbackBase<CopyMode::Copyable>::CallbackBase(
const CallbackBase& c)
: CallbackBase<CopyMode::MoveOnly>(nullptr) {
bind_state_ = c.bind_state_;
+ polymorphic_invoke_ = c.polymorphic_invoke_;
}
-CallbackBase<CopyMode::Copyable>::CallbackBase(CallbackBase&& c) = default;
+CallbackBase<CopyMode::Copyable>::CallbackBase(CallbackBase&& c)
+ : CallbackBase<CopyMode::MoveOnly>(std::move(c)) {}
CallbackBase<CopyMode::Copyable>&
CallbackBase<CopyMode::Copyable>::operator=(const CallbackBase& c) {
bind_state_ = c.bind_state_;
+ polymorphic_invoke_ = c.polymorphic_invoke_;
return *this;
}
CallbackBase<CopyMode::Copyable>&
-CallbackBase<CopyMode::Copyable>::operator=(CallbackBase&& c) = default;
+CallbackBase<CopyMode::Copyable>::operator=(CallbackBase&& c) {
+ *static_cast<CallbackBase<CopyMode::MoveOnly>*>(this) = std::move(c);
+ return *this;
+}
template class CallbackBase<CopyMode::MoveOnly>;
template class CallbackBase<CopyMode::Copyable>;
diff --git a/base/callback_internal.h b/base/callback_internal.h
index d6dcfeb3c0..0fe0b2d9e1 100644
--- a/base/callback_internal.h
+++ b/base/callback_internal.h
@@ -30,16 +30,10 @@ class CallbackBase;
// Creating a vtable for every BindState template instantiation results in a lot
// of bloat. Its only task is to call the destructor which can be done with a
// function pointer.
-class BASE_EXPORT BindStateBase {
- public:
- using InvokeFuncStorage = void(*)();
-
+class BindStateBase {
protected:
- BindStateBase(InvokeFuncStorage polymorphic_invoke,
- void (*destructor)(const BindStateBase*));
- BindStateBase(InvokeFuncStorage polymorphic_invoke,
- void (*destructor)(const BindStateBase*),
- bool (*is_cancelled)(const BindStateBase*));
+ explicit BindStateBase(void (*destructor)(BindStateBase*))
+ : ref_count_(0), destructor_(destructor) {}
~BindStateBase() = default;
private:
@@ -47,24 +41,13 @@ class BASE_EXPORT BindStateBase {
template <CopyMode copy_mode>
friend class CallbackBase;
- bool IsCancelled() const {
- return is_cancelled_(this);
- }
-
- void AddRef() const;
- void Release() const;
-
- // In C++, it is safe to cast function pointers to function pointers of
- // another type. It is not okay to use void*. We create a InvokeFuncStorage
- // that that can store our function pointer, and then cast it back to
- // the original type on usage.
- InvokeFuncStorage polymorphic_invoke_;
+ void AddRef();
+ void Release();
- mutable AtomicRefCount ref_count_;
+ AtomicRefCount ref_count_;
// Pointer to a function that will properly destroy |this|.
- void (*destructor_)(const BindStateBase*);
- bool (*is_cancelled_)(const BindStateBase*);
+ void (*destructor_)(BindStateBase*);
DISALLOW_COPY_AND_ASSIGN(BindStateBase);
};
@@ -79,43 +62,36 @@ class BASE_EXPORT CallbackBase<CopyMode::MoveOnly> {
CallbackBase(CallbackBase&& c);
CallbackBase& operator=(CallbackBase&& c);
- explicit CallbackBase(const CallbackBase<CopyMode::Copyable>& c);
- CallbackBase& operator=(const CallbackBase<CopyMode::Copyable>& c);
-
- explicit CallbackBase(CallbackBase<CopyMode::Copyable>&& c);
- CallbackBase& operator=(CallbackBase<CopyMode::Copyable>&& c);
-
// Returns true if Callback is null (doesn't refer to anything).
bool is_null() const { return bind_state_.get() == NULL; }
explicit operator bool() const { return !is_null(); }
- // Returns true if the callback invocation will be nop due to an cancellation.
- // It's invalid to call this on uninitialized callback.
- bool IsCancelled() const;
-
// Returns the Callback into an uninitialized state.
void Reset();
protected:
- using InvokeFuncStorage = BindStateBase::InvokeFuncStorage;
+ // In C++, it is safe to cast function pointers to function pointers of
+ // another type. It is not okay to use void*. We create a InvokeFuncStorage
+ // that that can store our function pointer, and then cast it back to
+ // the original type on usage.
+ using InvokeFuncStorage = void(*)();
// Returns true if this callback equals |other|. |other| may be null.
bool EqualsInternal(const CallbackBase& other) const;
// Allow initializing of |bind_state_| via the constructor to avoid default
- // initialization of the scoped_refptr.
+ // initialization of the scoped_refptr. We do not also initialize
+ // |polymorphic_invoke_| here because doing a normal assignment in the
+ // derived Callback templates makes for much nicer compiler errors.
explicit CallbackBase(BindStateBase* bind_state);
- InvokeFuncStorage polymorphic_invoke() const {
- return bind_state_->polymorphic_invoke_;
- }
-
// Force the destructor to be instantiated inside this translation unit so
// that our subclasses will not get inlined versions. Avoids more template
// bloat.
~CallbackBase();
scoped_refptr<BindStateBase> bind_state_;
+ InvokeFuncStorage polymorphic_invoke_ = nullptr;
};
// CallbackBase<Copyable> is a direct base class of Copyable Callbacks.
diff --git a/base/callback_unittest.cc b/base/callback_unittest.cc
index a41736946a..ce453a1075 100644
--- a/base/callback_unittest.cc
+++ b/base/callback_unittest.cc
@@ -14,7 +14,7 @@
namespace base {
-void NopInvokeFunc() {}
+void NopInvokeFunc(internal::BindStateBase*) {}
// White-box testpoints to inject into a Callback<> object for checking
// comparators and emptiness APIs. Use a BindState that is specialized
@@ -22,26 +22,20 @@ void NopInvokeFunc() {}
// chance of colliding with another instantiation and breaking the
// one-definition-rule.
struct FakeBindState1 : internal::BindStateBase {
- FakeBindState1() : BindStateBase(&NopInvokeFunc, &Destroy, &IsCancelled) {}
+ FakeBindState1() : BindStateBase(&Destroy) {}
private:
~FakeBindState1() {}
- static void Destroy(const internal::BindStateBase* self) {
- delete static_cast<const FakeBindState1*>(self);
- }
- static bool IsCancelled(const internal::BindStateBase*) {
- return false;
+ static void Destroy(internal::BindStateBase* self) {
+ delete static_cast<FakeBindState1*>(self);
}
};
struct FakeBindState2 : internal::BindStateBase {
- FakeBindState2() : BindStateBase(&NopInvokeFunc, &Destroy, &IsCancelled) {}
+ FakeBindState2() : BindStateBase(&Destroy) {}
private:
~FakeBindState2() {}
- static void Destroy(const internal::BindStateBase* self) {
- delete static_cast<const FakeBindState2*>(self);
- }
- static bool IsCancelled(const internal::BindStateBase*) {
- return false;
+ static void Destroy(internal::BindStateBase* self) {
+ delete static_cast<FakeBindState2*>(self);
}
};
@@ -50,8 +44,8 @@ namespace {
class CallbackTest : public ::testing::Test {
public:
CallbackTest()
- : callback_a_(new FakeBindState1()),
- callback_b_(new FakeBindState2()) {
+ : callback_a_(new FakeBindState1(), &NopInvokeFunc),
+ callback_b_(new FakeBindState2(), &NopInvokeFunc) {
}
~CallbackTest() override {}
@@ -94,7 +88,7 @@ TEST_F(CallbackTest, Equals) {
EXPECT_FALSE(callback_b_.Equals(callback_a_));
// We should compare based on instance, not type.
- Callback<void()> callback_c(new FakeBindState1());
+ Callback<void()> callback_c(new FakeBindState1(), &NopInvokeFunc);
Callback<void()> callback_a2 = callback_a_;
EXPECT_TRUE(callback_a_.Equals(callback_a2));
EXPECT_FALSE(callback_a_.Equals(callback_c));
@@ -115,17 +109,6 @@ TEST_F(CallbackTest, Reset) {
EXPECT_TRUE(callback_a_.Equals(null_callback_));
}
-TEST_F(CallbackTest, Move) {
- // Moving should reset the callback.
- ASSERT_FALSE(callback_a_.is_null());
- ASSERT_FALSE(callback_a_.Equals(null_callback_));
-
- auto tmp = std::move(callback_a_);
-
- EXPECT_TRUE(callback_a_.is_null());
- EXPECT_TRUE(callback_a_.Equals(null_callback_));
-}
-
struct TestForReentrancy {
TestForReentrancy()
: cb_already_run(false),
diff --git a/base/cancelable_callback.h b/base/cancelable_callback.h
index 13cbd0c213..0034fddccd 100644
--- a/base/cancelable_callback.h
+++ b/base/cancelable_callback.h
@@ -26,18 +26,16 @@
// to the message loop, the intensive test runs, the message loop is run,
// then the callback is cancelled.
//
-// RunLoop run_loop;
-//
// void TimeoutCallback(const std::string& timeout_message) {
// FAIL() << timeout_message;
-// run_loop.QuitWhenIdle();
+// MessageLoop::current()->QuitWhenIdle();
// }
//
// CancelableClosure timeout(base::Bind(&TimeoutCallback, "Test timed out."));
-// ThreadTaskRunnerHandle::Get()->PostDelayedTask(FROM_HERE, timeout.callback(),
-// TimeDelta::FromSeconds(4));
+// MessageLoop::current()->PostDelayedTask(FROM_HERE, timeout.callback(),
+// 4000) // 4 seconds to run.
// RunIntensiveTest();
-// run_loop.Run();
+// MessageLoop::current()->Run();
// timeout.Cancel(); // Hopefully this is hit before the timeout callback runs.
//
diff --git a/base/command_line.cc b/base/command_line.cc
index 3033fcfc6e..099bb185a4 100644
--- a/base/command_line.cc
+++ b/base/command_line.cc
@@ -149,10 +149,7 @@ string16 QuoteForCommandLineToArgvW(const string16& arg,
} // namespace
-CommandLine::CommandLine(NoProgram /* no_program */)
- : argv_(1),
- begin_args_(1) {
-}
+CommandLine::CommandLine(NoProgram) : argv_(1), begin_args_(1) {}
CommandLine::CommandLine(const FilePath& program)
: argv_(1),
@@ -455,7 +452,9 @@ CommandLine::StringType CommandLine::GetCommandLineStringInternal(
CommandLine::StringType CommandLine::GetArgumentsStringInternal(
bool quote_placeholders) const {
- ALLOW_UNUSED_PARAM(quote_placeholders);
+#if !defined(OS_WIN)
+ (void)quote_placeholders; // Avoid an unused warning.
+#endif
StringType params;
// Append switches and arguments.
bool parse_switches = true;
diff --git a/base/compiler_specific.h b/base/compiler_specific.h
index 358a5c9ca3..c2a02dee01 100644
--- a/base/compiler_specific.h
+++ b/base/compiler_specific.h
@@ -85,9 +85,6 @@
// ALLOW_UNUSED_LOCAL(x);
#define ALLOW_UNUSED_LOCAL(x) false ? (void)x : (void)0
-// Used for Arc++ where -Wno-unused-parameter is used.
-#define ALLOW_UNUSED_PARAM(x) false ? (void)x : (void)0
-
// Annotate a typedef or function indicating it's ok if it's not used.
// Use like:
// typedef Foo Bar ALLOW_UNUSED_TYPE;
@@ -108,14 +105,6 @@
#define NOINLINE
#endif
-#if COMPILER_GCC && defined(NDEBUG)
-#define ALWAYS_INLINE inline __attribute__((__always_inline__))
-#elif COMPILER_MSVC && defined(NDEBUG)
-#define ALWAYS_INLINE __forceinline
-#else
-#define ALWAYS_INLINE inline
-#endif
-
// Specify memory alignment for structs, classes, etc.
// Use like:
// class ALIGNAS(16) MyClass { ... }
@@ -165,16 +154,6 @@
// If available, it would look like:
// __attribute__((format(wprintf, format_param, dots_param)))
-// Sanitizers annotations.
-#if defined(__has_attribute)
-#if __has_attribute(no_sanitize)
-#define NO_SANITIZE(what) __attribute__((no_sanitize(what)))
-#endif
-#endif
-#if !defined(NO_SANITIZE)
-#define NO_SANITIZE(what)
-#endif
-
// MemorySanitizer annotations.
#if defined(MEMORY_SANITIZER) && !defined(OS_NACL)
#include <sanitizer/msan_interface.h>
@@ -195,15 +174,6 @@
#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size)
#endif // MEMORY_SANITIZER
-// DISABLE_CFI_PERF -- Disable Control Flow Integrity for perf reasons.
-#if !defined(DISABLE_CFI_PERF)
-#if defined(__clang__) && defined(OFFICIAL_BUILD)
-#define DISABLE_CFI_PERF __attribute__((no_sanitize("cfi")))
-#else
-#define DISABLE_CFI_PERF
-#endif
-#endif
-
// Macro useful for writing cross-platform function pointers.
#if !defined(CDECL)
#if defined(OS_WIN)
@@ -222,14 +192,6 @@
#endif // defined(COMPILER_GCC)
#endif // !defined(UNLIKELY)
-#if !defined(LIKELY)
-#if defined(COMPILER_GCC)
-#define LIKELY(x) __builtin_expect(!!(x), 1)
-#else
-#define LIKELY(x) (x)
-#endif // defined(COMPILER_GCC)
-#endif // !defined(LIKELY)
-
// Compiler feature-detection.
// clang.llvm.org/docs/LanguageExtensions.html#has-feature-and-has-extension
#if defined(__has_feature)
diff --git a/base/containers/mru_cache.h b/base/containers/mru_cache.h
index 4005489d4b..6c1d6260f5 100644
--- a/base/containers/mru_cache.h
+++ b/base/containers/mru_cache.h
@@ -209,12 +209,10 @@ class MRUCacheBase {
// A container that does not do anything to free its data. Use this when storing
// value types (as opposed to pointers) in the list.
-template <class KeyType,
- class PayloadType,
- class CompareType = std::less<KeyType>>
-class MRUCache : public MRUCacheBase<KeyType, PayloadType, CompareType> {
+template <class KeyType, class PayloadType>
+class MRUCache : public MRUCacheBase<KeyType, PayloadType, std::less<KeyType>> {
private:
- using ParentType = MRUCacheBase<KeyType, PayloadType, CompareType>;
+ using ParentType = MRUCacheBase<KeyType, PayloadType, std::less<KeyType>>;
public:
// See MRUCacheBase, noting the possibility of using NO_AUTO_EVICT.
diff --git a/base/containers/scoped_ptr_hash_map.h b/base/containers/scoped_ptr_hash_map.h
new file mode 100644
index 0000000000..f513f06ac2
--- /dev/null
+++ b/base/containers/scoped_ptr_hash_map.h
@@ -0,0 +1,176 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_CONTAINERS_SCOPED_PTR_HASH_MAP_H_
+#define BASE_CONTAINERS_SCOPED_PTR_HASH_MAP_H_
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "base/containers/hash_tables.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/stl_util.h"
+
+namespace base {
+
+// Deprecated. Use std::unordered_map instead. https://crbug.com/579229
+//
+// This type acts like a hash_map<K, std::unique_ptr<V, D> >, based on top of
+// base::hash_map. The ScopedPtrHashMap has ownership of all values in the data
+// structure.
+template <typename Key, typename ScopedPtr>
+class ScopedPtrHashMap {
+ typedef base::hash_map<Key, typename ScopedPtr::element_type*> Container;
+
+ public:
+ typedef typename Container::key_type key_type;
+ typedef typename Container::mapped_type mapped_type;
+ typedef typename Container::value_type value_type;
+ typedef typename Container::iterator iterator;
+ typedef typename Container::const_iterator const_iterator;
+
+ ScopedPtrHashMap() {}
+
+ ~ScopedPtrHashMap() { clear(); }
+
+ void swap(ScopedPtrHashMap<Key, ScopedPtr>& other) {
+ data_.swap(other.data_);
+ }
+
+ // Replaces value but not key if key is already present.
+ iterator set(const Key& key, ScopedPtr data) {
+ iterator it = find(key);
+ if (it != end()) {
+ // Let ScopedPtr decide how to delete. For example, it may use custom
+ // deleter.
+ ScopedPtr(it->second).reset();
+ it->second = data.release();
+ return it;
+ }
+
+ return data_.insert(std::make_pair(key, data.release())).first;
+ }
+
+ // Does nothing if key is already present
+ std::pair<iterator, bool> add(const Key& key, ScopedPtr data) {
+ std::pair<iterator, bool> result =
+ data_.insert(std::make_pair(key, data.get()));
+ if (result.second)
+ ::ignore_result(data.release());
+ return result;
+ }
+
+ void erase(iterator it) {
+ // Let ScopedPtr decide how to delete.
+ ScopedPtr(it->second).reset();
+ data_.erase(it);
+ }
+
+ size_t erase(const Key& k) {
+ iterator it = data_.find(k);
+ if (it == data_.end())
+ return 0;
+ erase(it);
+ return 1;
+ }
+
+ ScopedPtr take(iterator it) {
+ DCHECK(it != data_.end());
+ if (it == data_.end())
+ return ScopedPtr();
+
+ ScopedPtr ret(it->second);
+ it->second = NULL;
+ return ret;
+ }
+
+ ScopedPtr take(const Key& k) {
+ iterator it = find(k);
+ if (it == data_.end())
+ return ScopedPtr();
+
+ return take(it);
+ }
+
+ ScopedPtr take_and_erase(iterator it) {
+ DCHECK(it != data_.end());
+ if (it == data_.end())
+ return ScopedPtr();
+
+ ScopedPtr ret(it->second);
+ data_.erase(it);
+ return ret;
+ }
+
+ ScopedPtr take_and_erase(const Key& k) {
+ iterator it = find(k);
+ if (it == data_.end())
+ return ScopedPtr();
+
+ return take_and_erase(it);
+ }
+
+ // Returns the element in the hash_map that matches the given key.
+ // If no such element exists it returns NULL.
+ typename ScopedPtr::element_type* get(const Key& k) const {
+ const_iterator it = find(k);
+ if (it == end())
+ return NULL;
+ return it->second;
+ }
+
+ inline bool contains(const Key& k) const { return data_.count(k) > 0; }
+
+ inline void clear() {
+ auto it = data_.begin();
+ while (it != data_.end()) {
+ // NOTE: Like STLDeleteContainerPointers, deleting behind the iterator.
+ // Deleting the value does not always invalidate the iterator, but it may
+ // do so if the key is a pointer into the value object.
+ auto temp = it;
+ ++it;
+ // Let ScopedPtr decide how to delete.
+ ScopedPtr(temp->second).reset();
+ }
+ data_.clear();
+ }
+
+ inline const_iterator find(const Key& k) const { return data_.find(k); }
+ inline iterator find(const Key& k) { return data_.find(k); }
+
+ inline size_t count(const Key& k) const { return data_.count(k); }
+ inline std::pair<const_iterator, const_iterator> equal_range(
+ const Key& k) const {
+ return data_.equal_range(k);
+ }
+ inline std::pair<iterator, iterator> equal_range(const Key& k) {
+ return data_.equal_range(k);
+ }
+
+ inline size_t size() const { return data_.size(); }
+ inline size_t max_size() const { return data_.max_size(); }
+
+ inline bool empty() const { return data_.empty(); }
+
+ inline size_t bucket_count() const { return data_.bucket_count(); }
+ inline void resize(size_t size) { return data_.resize(size); }
+
+ inline iterator begin() { return data_.begin(); }
+ inline const_iterator begin() const { return data_.begin(); }
+ inline iterator end() { return data_.end(); }
+ inline const_iterator end() const { return data_.end(); }
+
+ private:
+ Container data_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedPtrHashMap);
+};
+
+} // namespace base
+
+#endif // BASE_CONTAINERS_SCOPED_PTR_HASH_MAP_H_
diff --git a/base/containers/small_map.h b/base/containers/small_map.h
index 2945d58769..82ed6c5473 100644
--- a/base/containers/small_map.h
+++ b/base/containers/small_map.h
@@ -32,7 +32,7 @@ namespace base {
//
// - If you only ever keep a couple of items and have very simple usage,
// consider whether a using a vector and brute-force searching it will be
-// the most efficient. It's not a lot of generated code (less than a
+// the most efficient. It's not a lot of generated code (less then a
// red-black tree if your key is "weird" and not eliminated as duplicate of
// something else) and will probably be faster and do fewer heap allocations
// than std::map if you have just a couple of items.
@@ -510,8 +510,8 @@ class SmallMap {
size_ = 0;
}
- // Invalidates iterators. Returns iterator following the last removed element.
- iterator erase(const iterator& position) {
+ // Invalidates iterators.
+ void erase(const iterator& position) {
if (size_ >= 0) {
int i = position.array_iter_ - array_;
array_[i].Destroy();
@@ -519,11 +519,10 @@ class SmallMap {
if (i != size_) {
array_[i].InitFromMove(std::move(array_[size_]));
array_[size_].Destroy();
- return iterator(array_ + i);
}
- return end();
+ } else {
+ map_->erase(position.hash_iter_);
}
- return iterator(map_->erase(position.hash_iter_));
}
size_t erase(const key_type& key) {
@@ -575,13 +574,17 @@ class SmallMap {
// We want to call constructors and destructors manually, but we don't
// want to allocate and deallocate the memory used for them separately.
- // So, we use this crazy ManualConstructor class. Since C++11 it's possible
- // to use objects in unions like this, but the ManualDestructor syntax is
- // a bit better and doesn't have limitations on object type.
+ // So, we use this crazy ManualConstructor class.
//
// Since array_ and map_ are mutually exclusive, we'll put them in a
- // union.
+ // union, too. We add in a dummy_ value which quiets MSVC from otherwise
+ // giving an erroneous "union member has copy constructor" error message
+ // (C2621). This dummy member has to come before array_ to quiet the
+ // compiler.
+ //
+ // TODO(brettw) remove this and use C++11 unions when we require C++11.
union {
+ ManualConstructor<value_type> dummy_;
ManualConstructor<value_type> array_[kArraySize];
ManualConstructor<NormalMap> map_;
};
diff --git a/base/cpu.cc b/base/cpu.cc
index 848208f7c1..de4a001f7f 100644
--- a/base/cpu.cc
+++ b/base/cpu.cc
@@ -16,6 +16,7 @@
#if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX))
#include "base/files/file_util.h"
+#include "base/lazy_instance.h"
#endif
#if defined(ARCH_CPU_X86_FAMILY)
@@ -42,7 +43,6 @@ CPU::CPU()
has_ssse3_(false),
has_sse41_(false),
has_sse42_(false),
- has_popcnt_(false),
has_avx_(false),
has_avx2_(false),
has_aesni_(false),
@@ -59,22 +59,23 @@ namespace {
#if defined(__pic__) && defined(__i386__)
void __cpuid(int cpu_info[4], int info_type) {
- __asm__ volatile(
- "mov %%ebx, %%edi\n"
- "cpuid\n"
- "xchg %%edi, %%ebx\n"
- : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]),
- "=d"(cpu_info[3])
- : "a"(info_type), "c"(0));
+ __asm__ volatile (
+ "mov %%ebx, %%edi\n"
+ "cpuid\n"
+ "xchg %%edi, %%ebx\n"
+ : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
+ : "a"(info_type)
+ );
}
#else
void __cpuid(int cpu_info[4], int info_type) {
- __asm__ volatile("cpuid\n"
- : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]),
- "=d"(cpu_info[3])
- : "a"(info_type), "c"(0));
+ __asm__ volatile (
+ "cpuid\n"
+ : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
+ : "a"(info_type)
+ );
}
#endif
@@ -93,8 +94,9 @@ uint64_t _xgetbv(uint32_t xcr) {
#endif // ARCH_CPU_X86_FAMILY
#if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX))
-std::string* CpuInfoBrand() {
- static std::string* brand = []() {
+class LazyCpuInfoValue {
+ public:
+ LazyCpuInfoValue() {
// This function finds the value from /proc/cpuinfo under the key "model
// name" or "Processor". "model name" is used in Linux 3.8 and later (3.7
// and later for arm64) and is shown once per CPU. "Processor" is used in
@@ -107,23 +109,30 @@ std::string* CpuInfoBrand() {
ReadFileToString(FilePath("/proc/cpuinfo"), &contents);
DCHECK(!contents.empty());
if (contents.empty()) {
- return new std::string();
+ return;
}
std::istringstream iss(contents);
std::string line;
while (std::getline(iss, line)) {
- if ((line.compare(0, strlen(kModelNamePrefix), kModelNamePrefix) == 0 ||
+ if (brand_.empty() &&
+ (line.compare(0, strlen(kModelNamePrefix), kModelNamePrefix) == 0 ||
line.compare(0, strlen(kProcessorPrefix), kProcessorPrefix) == 0)) {
- return new std::string(line.substr(strlen(kModelNamePrefix)));
+ brand_.assign(line.substr(strlen(kModelNamePrefix)));
}
}
+ }
- return new std::string();
- }();
+ const std::string& brand() const { return brand_; }
+
+ private:
+ std::string brand_;
+ DISALLOW_COPY_AND_ASSIGN(LazyCpuInfoValue);
+};
+
+base::LazyInstance<LazyCpuInfoValue>::Leaky g_lazy_cpuinfo =
+ LAZY_INSTANCE_INITIALIZER;
- return brand;
-}
#endif // defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) ||
// defined(OS_LINUX))
@@ -168,8 +177,6 @@ void CPU::Initialize() {
has_ssse3_ = (cpu_info[2] & 0x00000200) != 0;
has_sse41_ = (cpu_info[2] & 0x00080000) != 0;
has_sse42_ = (cpu_info[2] & 0x00100000) != 0;
- has_popcnt_ = (cpu_info[2] & 0x00800000) != 0;
-
// AVX instructions will generate an illegal instruction exception unless
// a) they are supported by the CPU,
// b) XSAVE is supported by the CPU and
@@ -212,7 +219,7 @@ void CPU::Initialize() {
has_non_stop_time_stamp_counter_ = (cpu_info[3] & (1 << 8)) != 0;
}
#elif defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX))
- cpu_brand_.assign(*CpuInfoBrand());
+ cpu_brand_.assign(g_lazy_cpuinfo.Get().brand());
#endif
}
diff --git a/base/cpu.h b/base/cpu.h
index 0e24df61dd..0e4303bfa0 100644
--- a/base/cpu.h
+++ b/base/cpu.h
@@ -46,7 +46,6 @@ class BASE_EXPORT CPU {
bool has_ssse3() const { return has_ssse3_; }
bool has_sse41() const { return has_sse41_; }
bool has_sse42() const { return has_sse42_; }
- bool has_popcnt() const { return has_popcnt_; }
bool has_avx() const { return has_avx_; }
bool has_avx2() const { return has_avx2_; }
bool has_aesni() const { return has_aesni_; }
@@ -75,7 +74,6 @@ class BASE_EXPORT CPU {
bool has_ssse3_;
bool has_sse41_;
bool has_sse42_;
- bool has_popcnt_;
bool has_avx_;
bool has_avx2_;
bool has_aesni_;
diff --git a/base/cpu_unittest.cc b/base/cpu_unittest.cc
index 9cabfd6998..ec14620f98 100644
--- a/base/cpu_unittest.cc
+++ b/base/cpu_unittest.cc
@@ -57,11 +57,6 @@ TEST(CPU, RunExtendedInstructions) {
__asm__ __volatile__("crc32 %%eax, %%eax\n" : : : "eax");
}
- if (cpu.has_popcnt()) {
- // Execute a POPCNT instruction.
- __asm__ __volatile__("popcnt %%eax, %%eax\n" : : : "eax");
- }
-
if (cpu.has_avx()) {
// Execute an AVX instruction.
__asm__ __volatile__("vzeroupper\n" : : : "xmm0");
@@ -105,11 +100,6 @@ TEST(CPU, RunExtendedInstructions) {
__asm crc32 eax, eax;
}
- if (cpu.has_popcnt()) {
- // Execute a POPCNT instruction.
- __asm popcnt eax, eax;
- }
-
// Visual C 2012 required for AVX.
#if _MSC_VER >= 1700
if (cpu.has_avx()) {
diff --git a/base/critical_closure.h b/base/critical_closure.h
index 1b10cde7ce..6ebd7afa50 100644
--- a/base/critical_closure.h
+++ b/base/critical_closure.h
@@ -25,15 +25,21 @@ bool IsMultiTaskingSupported();
// This class wraps a closure so it can continue to run for a period of time
// when the application goes to the background by using
// |ios::ScopedCriticalAction|.
+template <typename R>
class CriticalClosure {
public:
- explicit CriticalClosure(const Closure& closure);
- ~CriticalClosure();
- void Run();
+ explicit CriticalClosure(const Callback<R(void)>& closure)
+ : closure_(closure) {}
+
+ ~CriticalClosure() {}
+
+ R Run() {
+ return closure_.Run();
+ }
private:
ios::ScopedCriticalAction critical_action_;
- Closure closure_;
+ Callback<R(void)> closure_;
DISALLOW_COPY_AND_ASSIGN(CriticalClosure);
};
@@ -41,7 +47,8 @@ class CriticalClosure {
} // namespace internal
-// Returns a closure that will continue to run for a period of time when the
+// Returns a closure (which may return a result, but must not require any extra
+// arguments) that will continue to run for a period of time when the
// application goes to the background if possible on platforms where
// applications don't execute while backgrounded, otherwise the original task is
// returned.
@@ -55,13 +62,15 @@ class CriticalClosure {
// background running time, |MakeCriticalClosure| should be applied on them
// before posting.
#if defined(OS_IOS)
-inline Closure MakeCriticalClosure(const Closure& closure) {
+template <typename R>
+Callback<R(void)> MakeCriticalClosure(const Callback<R(void)>& closure) {
DCHECK(internal::IsMultiTaskingSupported());
- return base::Bind(&internal::CriticalClosure::Run,
- Owned(new internal::CriticalClosure(closure)));
+ return base::Bind(&internal::CriticalClosure<R>::Run,
+ Owned(new internal::CriticalClosure<R>(closure)));
}
#else // defined(OS_IOS)
-inline Closure MakeCriticalClosure(const Closure& closure) {
+template <typename R>
+inline Callback<R(void)> MakeCriticalClosure(const Callback<R(void)>& closure) {
// No-op for platforms where the application does not need to acquire
// background time for closures to finish when it goes into the background.
return closure;
diff --git a/base/debug/activity_tracker.cc b/base/debug/activity_tracker.cc
deleted file mode 100644
index 40e9b9537c..0000000000
--- a/base/debug/activity_tracker.cc
+++ /dev/null
@@ -1,1389 +0,0 @@
-// Copyright 2016 The Chromium 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/debug/activity_tracker.h"
-
-#include <algorithm>
-#include <limits>
-#include <utility>
-
-#include "base/atomic_sequence_num.h"
-#include "base/debug/stack_trace.h"
-#include "base/files/file.h"
-#include "base/files/file_path.h"
-#include "base/files/memory_mapped_file.h"
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/field_trial.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/pending_task.h"
-#include "base/pickle.h"
-#include "base/process/process.h"
-#include "base/process/process_handle.h"
-#include "base/stl_util.h"
-#include "base/strings/string_util.h"
-#include "base/threading/platform_thread.h"
-
-namespace base {
-namespace debug {
-
-namespace {
-
-// A number that identifies the memory as having been initialized. It's
-// arbitrary but happens to be the first 4 bytes of SHA1(ThreadActivityTracker).
-// A version number is added on so that major structure changes won't try to
-// read an older version (since the cookie won't match).
-const uint32_t kHeaderCookie = 0xC0029B24UL + 2; // v2
-
-// The minimum depth a stack should support.
-const int kMinStackDepth = 2;
-
-// The amount of memory set aside for holding arbitrary user data (key/value
-// pairs) globally or associated with ActivityData entries.
-const size_t kUserDataSize = 1 << 10; // 1 KiB
-const size_t kGlobalDataSize = 16 << 10; // 16 KiB
-const size_t kMaxUserDataNameLength =
- static_cast<size_t>(std::numeric_limits<uint8_t>::max());
-
-// A constant used to indicate that module information is changing.
-const uint32_t kModuleInformationChanging = 0x80000000;
-
-union ThreadRef {
- int64_t as_id;
-#if defined(OS_WIN)
- // On Windows, the handle itself is often a pseudo-handle with a common
- // value meaning "this thread" and so the thread-id is used. The former
- // can be converted to a thread-id with a system call.
- PlatformThreadId as_tid;
-#elif defined(OS_POSIX)
- // On Posix, the handle is always a unique identifier so no conversion
- // needs to be done. However, it's value is officially opaque so there
- // is no one correct way to convert it to a numerical identifier.
- PlatformThreadHandle::Handle as_handle;
-#endif
-};
-
-// Determines the previous aligned index.
-size_t RoundDownToAlignment(size_t index, size_t alignment) {
- return index & (0 - alignment);
-}
-
-// Determines the next aligned index.
-size_t RoundUpToAlignment(size_t index, size_t alignment) {
- return (index + (alignment - 1)) & (0 - alignment);
-}
-
-} // namespace
-
-
-// It doesn't matter what is contained in this (though it will be all zeros)
-// as only the address of it is important.
-const ActivityData kNullActivityData = {};
-
-ActivityData ActivityData::ForThread(const PlatformThreadHandle& handle) {
- ThreadRef thread_ref;
- thread_ref.as_id = 0; // Zero the union in case other is smaller.
-#if defined(OS_WIN)
- thread_ref.as_tid = ::GetThreadId(handle.platform_handle());
-#elif defined(OS_POSIX)
- thread_ref.as_handle = handle.platform_handle();
-#endif
- return ForThread(thread_ref.as_id);
-}
-
-ActivityTrackerMemoryAllocator::ActivityTrackerMemoryAllocator(
- PersistentMemoryAllocator* allocator,
- uint32_t object_type,
- uint32_t object_free_type,
- size_t object_size,
- size_t cache_size,
- bool make_iterable)
- : allocator_(allocator),
- object_type_(object_type),
- object_free_type_(object_free_type),
- object_size_(object_size),
- cache_size_(cache_size),
- make_iterable_(make_iterable),
- iterator_(allocator),
- cache_values_(new Reference[cache_size]),
- cache_used_(0) {
- DCHECK(allocator);
-}
-
-ActivityTrackerMemoryAllocator::~ActivityTrackerMemoryAllocator() {}
-
-ActivityTrackerMemoryAllocator::Reference
-ActivityTrackerMemoryAllocator::GetObjectReference() {
- // First see if there is a cached value that can be returned. This is much
- // faster than searching the memory system for free blocks.
- while (cache_used_ > 0) {
- Reference cached = cache_values_[--cache_used_];
- // Change the type of the cached object to the proper type and return it.
- // If the type-change fails that means another thread has taken this from
- // under us (via the search below) so ignore it and keep trying. Don't
- // clear the memory because that was done when the type was made "free".
- if (allocator_->ChangeType(cached, object_type_, object_free_type_, false))
- return cached;
- }
-
- // Fetch the next "free" object from persistent memory. Rather than restart
- // the iterator at the head each time and likely waste time going again
- // through objects that aren't relevant, the iterator continues from where
- // it last left off and is only reset when the end is reached. If the
- // returned reference matches |last|, then it has wrapped without finding
- // anything.
- const Reference last = iterator_.GetLast();
- while (true) {
- uint32_t type;
- Reference found = iterator_.GetNext(&type);
- if (found && type == object_free_type_) {
- // Found a free object. Change it to the proper type and return it. If
- // the type-change fails that means another thread has taken this from
- // under us so ignore it and keep trying.
- if (allocator_->ChangeType(found, object_type_, object_free_type_, false))
- return found;
- }
- if (found == last) {
- // Wrapped. No desired object was found.
- break;
- }
- if (!found) {
- // Reached end; start over at the beginning.
- iterator_.Reset();
- }
- }
-
- // No free block was found so instead allocate a new one.
- Reference allocated = allocator_->Allocate(object_size_, object_type_);
- if (allocated && make_iterable_)
- allocator_->MakeIterable(allocated);
- return allocated;
-}
-
-void ActivityTrackerMemoryAllocator::ReleaseObjectReference(Reference ref) {
- // Mark object as free.
- bool success = allocator_->ChangeType(ref, object_free_type_, object_type_,
- /*clear=*/true);
- DCHECK(success);
-
- // Add this reference to our "free" cache if there is space. If not, the type
- // has still been changed to indicate that it is free so this (or another)
- // thread can find it, albeit more slowly, using the iteration method above.
- if (cache_used_ < cache_size_)
- cache_values_[cache_used_++] = ref;
-}
-
-// static
-void Activity::FillFrom(Activity* activity,
- const void* program_counter,
- const void* origin,
- Type type,
- const ActivityData& data) {
- activity->time_internal = base::TimeTicks::Now().ToInternalValue();
- activity->calling_address = reinterpret_cast<uintptr_t>(program_counter);
- activity->origin_address = reinterpret_cast<uintptr_t>(origin);
- activity->activity_type = type;
- activity->data = data;
-
-#if defined(SYZYASAN)
- // Create a stacktrace from the current location and get the addresses.
- StackTrace stack_trace;
- size_t stack_depth;
- const void* const* stack_addrs = stack_trace.Addresses(&stack_depth);
- // Copy the stack addresses, ignoring the first one (here).
- size_t i;
- for (i = 1; i < stack_depth && i < kActivityCallStackSize; ++i) {
- activity->call_stack[i - 1] = reinterpret_cast<uintptr_t>(stack_addrs[i]);
- }
- activity->call_stack[i - 1] = 0;
-#else
- activity->call_stack[0] = 0;
-#endif
-}
-
-ActivityUserData::TypedValue::TypedValue() {}
-ActivityUserData::TypedValue::TypedValue(const TypedValue& other) = default;
-ActivityUserData::TypedValue::~TypedValue() {}
-
-StringPiece ActivityUserData::TypedValue::Get() const {
- DCHECK_EQ(RAW_VALUE, type_);
- return long_value_;
-}
-
-StringPiece ActivityUserData::TypedValue::GetString() const {
- DCHECK_EQ(STRING_VALUE, type_);
- return long_value_;
-}
-
-bool ActivityUserData::TypedValue::GetBool() const {
- DCHECK_EQ(BOOL_VALUE, type_);
- return short_value_ != 0;
-}
-
-char ActivityUserData::TypedValue::GetChar() const {
- DCHECK_EQ(CHAR_VALUE, type_);
- return static_cast<char>(short_value_);
-}
-
-int64_t ActivityUserData::TypedValue::GetInt() const {
- DCHECK_EQ(SIGNED_VALUE, type_);
- return static_cast<int64_t>(short_value_);
-}
-
-uint64_t ActivityUserData::TypedValue::GetUint() const {
- DCHECK_EQ(UNSIGNED_VALUE, type_);
- return static_cast<uint64_t>(short_value_);
-}
-
-StringPiece ActivityUserData::TypedValue::GetReference() const {
- DCHECK_EQ(RAW_VALUE_REFERENCE, type_);
- return ref_value_;
-}
-
-StringPiece ActivityUserData::TypedValue::GetStringReference() const {
- DCHECK_EQ(STRING_VALUE_REFERENCE, type_);
- return ref_value_;
-}
-
-ActivityUserData::ValueInfo::ValueInfo() {}
-ActivityUserData::ValueInfo::ValueInfo(ValueInfo&&) = default;
-ActivityUserData::ValueInfo::~ValueInfo() {}
-
-StaticAtomicSequenceNumber ActivityUserData::next_id_;
-
-ActivityUserData::ActivityUserData(void* memory, size_t size)
- : memory_(reinterpret_cast<char*>(memory)),
- available_(RoundDownToAlignment(size, kMemoryAlignment)),
- id_(reinterpret_cast<std::atomic<uint32_t>*>(memory)) {
- // It's possible that no user data is being stored.
- if (!memory_)
- return;
-
- DCHECK_LT(kMemoryAlignment, available_);
- if (id_->load(std::memory_order_relaxed) == 0) {
- // Generate a new ID and store it in the first 32-bit word of memory_.
- // |id_| must be non-zero for non-sink instances.
- uint32_t id;
- while ((id = next_id_.GetNext()) == 0)
- ;
- id_->store(id, std::memory_order_relaxed);
- DCHECK_NE(0U, id_->load(std::memory_order_relaxed));
- }
- memory_ += kMemoryAlignment;
- available_ -= kMemoryAlignment;
-
- // If there is already data present, load that. This allows the same class
- // to be used for analysis through snapshots.
- ImportExistingData();
-}
-
-ActivityUserData::~ActivityUserData() {}
-
-void ActivityUserData::Set(StringPiece name,
- ValueType type,
- const void* memory,
- size_t size) {
- DCHECK_GE(std::numeric_limits<uint8_t>::max(), name.length());
- size = std::min(std::numeric_limits<uint16_t>::max() - (kMemoryAlignment - 1),
- size);
-
- // It's possible that no user data is being stored.
- if (!memory_)
- return;
-
- // The storage of a name is limited so use that limit during lookup.
- if (name.length() > kMaxUserDataNameLength)
- name.set(name.data(), kMaxUserDataNameLength);
-
- ValueInfo* info;
- auto existing = values_.find(name);
- if (existing != values_.end()) {
- info = &existing->second;
- } else {
- // The name size is limited to what can be held in a single byte but
- // because there are not alignment constraints on strings, it's set tight
- // against the header. Its extent (the reserved space, even if it's not
- // all used) is calculated so that, when pressed against the header, the
- // following field will be aligned properly.
- size_t name_size = name.length();
- size_t name_extent =
- RoundUpToAlignment(sizeof(Header) + name_size, kMemoryAlignment) -
- sizeof(Header);
- size_t value_extent = RoundUpToAlignment(size, kMemoryAlignment);
-
- // The "base size" is the size of the header and (padded) string key. Stop
- // now if there's not room enough for even this.
- size_t base_size = sizeof(Header) + name_extent;
- if (base_size > available_)
- return;
-
- // The "full size" is the size for storing the entire value.
- size_t full_size = std::min(base_size + value_extent, available_);
-
- // If the value is actually a single byte, see if it can be stuffed at the
- // end of the name extent rather than wasting kMemoryAlignment bytes.
- if (size == 1 && name_extent > name_size) {
- full_size = base_size;
- --name_extent;
- --base_size;
- }
-
- // Truncate the stored size to the amount of available memory. Stop now if
- // there's not any room for even part of the value.
- if (size != 0) {
- size = std::min(full_size - base_size, size);
- if (size == 0)
- return;
- }
-
- // Allocate a chunk of memory.
- Header* header = reinterpret_cast<Header*>(memory_);
- memory_ += full_size;
- available_ -= full_size;
-
- // Datafill the header and name records. Memory must be zeroed. The |type|
- // is written last, atomically, to release all the other values.
- DCHECK_EQ(END_OF_VALUES, header->type.load(std::memory_order_relaxed));
- DCHECK_EQ(0, header->value_size.load(std::memory_order_relaxed));
- header->name_size = static_cast<uint8_t>(name_size);
- header->record_size = full_size;
- char* name_memory = reinterpret_cast<char*>(header) + sizeof(Header);
- void* value_memory =
- reinterpret_cast<char*>(header) + sizeof(Header) + name_extent;
- memcpy(name_memory, name.data(), name_size);
- header->type.store(type, std::memory_order_release);
-
- // Create an entry in |values_| so that this field can be found and changed
- // later on without having to allocate new entries.
- StringPiece persistent_name(name_memory, name_size);
- auto inserted =
- values_.insert(std::make_pair(persistent_name, ValueInfo()));
- DCHECK(inserted.second); // True if inserted, false if existed.
- info = &inserted.first->second;
- info->name = persistent_name;
- info->memory = value_memory;
- info->size_ptr = &header->value_size;
- info->extent = full_size - sizeof(Header) - name_extent;
- info->type = type;
- }
-
- // Copy the value data to storage. The |size| is written last, atomically, to
- // release the copied data. Until then, a parallel reader will just ignore
- // records with a zero size.
- DCHECK_EQ(type, info->type);
- size = std::min(size, info->extent);
- info->size_ptr->store(0, std::memory_order_seq_cst);
- memcpy(info->memory, memory, size);
- info->size_ptr->store(size, std::memory_order_release);
-}
-
-void ActivityUserData::SetReference(StringPiece name,
- ValueType type,
- const void* memory,
- size_t size) {
- ReferenceRecord rec;
- rec.address = reinterpret_cast<uintptr_t>(memory);
- rec.size = size;
- Set(name, type, &rec, sizeof(rec));
-}
-
-void ActivityUserData::ImportExistingData() const {
- while (available_ > sizeof(Header)) {
- Header* header = reinterpret_cast<Header*>(memory_);
- ValueType type =
- static_cast<ValueType>(header->type.load(std::memory_order_acquire));
- if (type == END_OF_VALUES)
- return;
- if (header->record_size > available_)
- return;
-
- size_t value_offset = RoundUpToAlignment(sizeof(Header) + header->name_size,
- kMemoryAlignment);
- if (header->record_size == value_offset &&
- header->value_size.load(std::memory_order_relaxed) == 1) {
- value_offset -= 1;
- }
- if (value_offset + header->value_size > header->record_size)
- return;
-
- ValueInfo info;
- info.name = StringPiece(memory_ + sizeof(Header), header->name_size);
- info.type = type;
- info.memory = memory_ + value_offset;
- info.size_ptr = &header->value_size;
- info.extent = header->record_size - value_offset;
-
- StringPiece key(info.name);
- values_.insert(std::make_pair(key, std::move(info)));
-
- memory_ += header->record_size;
- available_ -= header->record_size;
- }
-}
-
-bool ActivityUserData::CreateSnapshot(Snapshot* output_snapshot) const {
- DCHECK(output_snapshot);
- DCHECK(output_snapshot->empty());
-
- // Find any new data that may have been added by an active instance of this
- // class that is adding records.
- ImportExistingData();
-
- for (const auto& entry : values_) {
- TypedValue value;
- value.type_ = entry.second.type;
- DCHECK_GE(entry.second.extent,
- entry.second.size_ptr->load(std::memory_order_relaxed));
-
- switch (entry.second.type) {
- case RAW_VALUE:
- case STRING_VALUE:
- value.long_value_ =
- std::string(reinterpret_cast<char*>(entry.second.memory),
- entry.second.size_ptr->load(std::memory_order_relaxed));
- break;
- case RAW_VALUE_REFERENCE:
- case STRING_VALUE_REFERENCE: {
- ReferenceRecord* ref =
- reinterpret_cast<ReferenceRecord*>(entry.second.memory);
- value.ref_value_ = StringPiece(
- reinterpret_cast<char*>(static_cast<uintptr_t>(ref->address)),
- static_cast<size_t>(ref->size));
- } break;
- case BOOL_VALUE:
- case CHAR_VALUE:
- value.short_value_ = *reinterpret_cast<char*>(entry.second.memory);
- break;
- case SIGNED_VALUE:
- case UNSIGNED_VALUE:
- value.short_value_ = *reinterpret_cast<uint64_t*>(entry.second.memory);
- break;
- case END_OF_VALUES: // Included for completeness purposes.
- NOTREACHED();
- }
- auto inserted = output_snapshot->insert(
- std::make_pair(entry.second.name.as_string(), std::move(value)));
- DCHECK(inserted.second); // True if inserted, false if existed.
- }
-
- return true;
-}
-
-const void* ActivityUserData::GetBaseAddress() {
- // The |memory_| pointer advances as elements are written but the |id_|
- // value is always at the start of the block so just return that.
- return id_;
-}
-
-// This information is kept for every thread that is tracked. It is filled
-// the very first time the thread is seen. All fields must be of exact sizes
-// so there is no issue moving between 32 and 64-bit builds.
-struct ThreadActivityTracker::Header {
- // Defined in .h for analyzer access. Increment this if structure changes!
- static constexpr uint32_t kPersistentTypeId =
- GlobalActivityTracker::kTypeIdActivityTracker;
-
- // Expected size for 32/64-bit check.
- static constexpr size_t kExpectedInstanceSize = 80;
-
- // This unique number indicates a valid initialization of the memory.
- std::atomic<uint32_t> cookie;
-
- // The number of Activity slots (spaces that can hold an Activity) that
- // immediately follow this structure in memory.
- uint32_t stack_slots;
-
- // The process-id and thread-id (thread_ref.as_id) to which this data belongs.
- // These identifiers are not guaranteed to mean anything but are unique, in
- // combination, among all active trackers. It would be nice to always have
- // the process_id be a 64-bit value but the necessity of having it atomic
- // (for the memory barriers it provides) limits it to the natural word size
- // of the machine.
-#ifdef ARCH_CPU_64_BITS
- std::atomic<int64_t> process_id;
-#else
- std::atomic<int32_t> process_id;
- int32_t process_id_padding;
-#endif
- ThreadRef thread_ref;
-
- // The start-time and start-ticks when the data was created. Each activity
- // record has a |time_internal| value that can be converted to a "wall time"
- // with these two values.
- int64_t start_time;
- int64_t start_ticks;
-
- // The current depth of the stack. This may be greater than the number of
- // slots. If the depth exceeds the number of slots, the newest entries
- // won't be recorded.
- std::atomic<uint32_t> current_depth;
-
- // A memory location used to indicate if changes have been made to the stack
- // that would invalidate an in-progress read of its contents. The active
- // tracker will zero the value whenever something gets popped from the
- // stack. A monitoring tracker can write a non-zero value here, copy the
- // stack contents, and read the value to know, if it is still non-zero, that
- // the contents didn't change while being copied. This can handle concurrent
- // snapshot operations only if each snapshot writes a different bit (which
- // is not the current implementation so no parallel snapshots allowed).
- std::atomic<uint32_t> stack_unchanged;
-
- // The name of the thread (up to a maximum length). Dynamic-length names
- // are not practical since the memory has to come from the same persistent
- // allocator that holds this structure and to which this object has no
- // reference.
- char thread_name[32];
-};
-
-ThreadActivityTracker::Snapshot::Snapshot() {}
-ThreadActivityTracker::Snapshot::~Snapshot() {}
-
-ThreadActivityTracker::ScopedActivity::ScopedActivity(
- ThreadActivityTracker* tracker,
- const void* program_counter,
- const void* origin,
- Activity::Type type,
- const ActivityData& data)
- : tracker_(tracker) {
- if (tracker_)
- activity_id_ = tracker_->PushActivity(program_counter, origin, type, data);
-}
-
-ThreadActivityTracker::ScopedActivity::~ScopedActivity() {
- if (tracker_)
- tracker_->PopActivity(activity_id_);
-}
-
-void ThreadActivityTracker::ScopedActivity::ChangeTypeAndData(
- Activity::Type type,
- const ActivityData& data) {
- if (tracker_)
- tracker_->ChangeActivity(activity_id_, type, data);
-}
-
-ThreadActivityTracker::ThreadActivityTracker(void* base, size_t size)
- : header_(static_cast<Header*>(base)),
- stack_(reinterpret_cast<Activity*>(reinterpret_cast<char*>(base) +
- sizeof(Header))),
- stack_slots_(
- static_cast<uint32_t>((size - sizeof(Header)) / sizeof(Activity))) {
- DCHECK(thread_checker_.CalledOnValidThread());
-
- // Verify the parameters but fail gracefully if they're not valid so that
- // production code based on external inputs will not crash. IsValid() will
- // return false in this case.
- if (!base ||
- // Ensure there is enough space for the header and at least a few records.
- size < sizeof(Header) + kMinStackDepth * sizeof(Activity) ||
- // Ensure that the |stack_slots_| calculation didn't overflow.
- (size - sizeof(Header)) / sizeof(Activity) >
- std::numeric_limits<uint32_t>::max()) {
- NOTREACHED();
- return;
- }
-
- // Ensure that the thread reference doesn't exceed the size of the ID number.
- // This won't compile at the global scope because Header is a private struct.
- static_assert(
- sizeof(header_->thread_ref) == sizeof(header_->thread_ref.as_id),
- "PlatformThreadHandle::Handle is too big to hold in 64-bit ID");
-
- // Ensure that the alignment of Activity.data is properly aligned to a
- // 64-bit boundary so there are no interoperability-issues across cpu
- // architectures.
- static_assert(offsetof(Activity, data) % sizeof(uint64_t) == 0,
- "ActivityData.data is not 64-bit aligned");
-
- // Provided memory should either be completely initialized or all zeros.
- if (header_->cookie.load(std::memory_order_relaxed) == 0) {
- // This is a new file. Double-check other fields and then initialize.
- DCHECK_EQ(0, header_->process_id.load(std::memory_order_relaxed));
- DCHECK_EQ(0, header_->thread_ref.as_id);
- DCHECK_EQ(0, header_->start_time);
- DCHECK_EQ(0, header_->start_ticks);
- DCHECK_EQ(0U, header_->stack_slots);
- DCHECK_EQ(0U, header_->current_depth.load(std::memory_order_relaxed));
- DCHECK_EQ(0U, header_->stack_unchanged.load(std::memory_order_relaxed));
- DCHECK_EQ(0, stack_[0].time_internal);
- DCHECK_EQ(0U, stack_[0].origin_address);
- DCHECK_EQ(0U, stack_[0].call_stack[0]);
- DCHECK_EQ(0U, stack_[0].data.task.sequence_id);
-
-#if defined(OS_WIN)
- header_->thread_ref.as_tid = PlatformThread::CurrentId();
-#elif defined(OS_POSIX)
- header_->thread_ref.as_handle =
- PlatformThread::CurrentHandle().platform_handle();
-#endif
- header_->process_id.store(GetCurrentProcId(), std::memory_order_relaxed);
-
- header_->start_time = base::Time::Now().ToInternalValue();
- header_->start_ticks = base::TimeTicks::Now().ToInternalValue();
- header_->stack_slots = stack_slots_;
- strlcpy(header_->thread_name, PlatformThread::GetName(),
- sizeof(header_->thread_name));
-
- // This is done last so as to guarantee that everything above is "released"
- // by the time this value gets written.
- header_->cookie.store(kHeaderCookie, std::memory_order_release);
-
- valid_ = true;
- DCHECK(IsValid());
- } else {
- // This is a file with existing data. Perform basic consistency checks.
- valid_ = true;
- valid_ = IsValid();
- }
-}
-
-ThreadActivityTracker::~ThreadActivityTracker() {}
-
-ThreadActivityTracker::ActivityId ThreadActivityTracker::PushActivity(
- const void* program_counter,
- const void* origin,
- Activity::Type type,
- const ActivityData& data) {
- // A thread-checker creates a lock to check the thread-id which means
- // re-entry into this code if lock acquisitions are being tracked.
- DCHECK(type == Activity::ACT_LOCK_ACQUIRE ||
- thread_checker_.CalledOnValidThread());
-
- // Get the current depth of the stack. No access to other memory guarded
- // by this variable is done here so a "relaxed" load is acceptable.
- uint32_t depth = header_->current_depth.load(std::memory_order_relaxed);
-
- // Handle the case where the stack depth has exceeded the storage capacity.
- // Extra entries will be lost leaving only the base of the stack.
- if (depth >= stack_slots_) {
- // Since no other threads modify the data, no compare/exchange is needed.
- // Since no other memory is being modified, a "relaxed" store is acceptable.
- header_->current_depth.store(depth + 1, std::memory_order_relaxed);
- return depth;
- }
-
- // Get a pointer to the next activity and load it. No atomicity is required
- // here because the memory is known only to this thread. It will be made
- // known to other threads once the depth is incremented.
- Activity::FillFrom(&stack_[depth], program_counter, origin, type, data);
-
- // Save the incremented depth. Because this guards |activity| memory filled
- // above that may be read by another thread once the recorded depth changes,
- // a "release" store is required.
- header_->current_depth.store(depth + 1, std::memory_order_release);
-
- // The current depth is used as the activity ID because it simply identifies
- // an entry. Once an entry is pop'd, it's okay to reuse the ID.
- return depth;
-}
-
-void ThreadActivityTracker::ChangeActivity(ActivityId id,
- Activity::Type type,
- const ActivityData& data) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(type != Activity::ACT_NULL || &data != &kNullActivityData);
- DCHECK_LT(id, header_->current_depth.load(std::memory_order_acquire));
-
- // Update the information if it is being recorded (i.e. within slot limit).
- if (id < stack_slots_) {
- Activity* activity = &stack_[id];
-
- if (type != Activity::ACT_NULL) {
- DCHECK_EQ(activity->activity_type & Activity::ACT_CATEGORY_MASK,
- type & Activity::ACT_CATEGORY_MASK);
- activity->activity_type = type;
- }
-
- if (&data != &kNullActivityData)
- activity->data = data;
- }
-}
-
-void ThreadActivityTracker::PopActivity(ActivityId id) {
- // Do an atomic decrement of the depth. No changes to stack entries guarded
- // by this variable are done here so a "relaxed" operation is acceptable.
- // |depth| will receive the value BEFORE it was modified which means the
- // return value must also be decremented. The slot will be "free" after
- // this call but since only a single thread can access this object, the
- // data will remain valid until this method returns or calls outside.
- uint32_t depth =
- header_->current_depth.fetch_sub(1, std::memory_order_relaxed) - 1;
-
- // Validate that everything is running correctly.
- DCHECK_EQ(id, depth);
-
- // A thread-checker creates a lock to check the thread-id which means
- // re-entry into this code if lock acquisitions are being tracked.
- DCHECK(stack_[depth].activity_type == Activity::ACT_LOCK_ACQUIRE ||
- thread_checker_.CalledOnValidThread());
-
- // The stack has shrunk meaning that some other thread trying to copy the
- // contents for reporting purposes could get bad data. That thread would
- // have written a non-zero value into |stack_unchanged|; clearing it here
- // will let that thread detect that something did change. This needs to
- // happen after the atomic |depth| operation above so a "release" store
- // is required.
- header_->stack_unchanged.store(0, std::memory_order_release);
-}
-
-std::unique_ptr<ActivityUserData> ThreadActivityTracker::GetUserData(
- ActivityId id,
- ActivityTrackerMemoryAllocator* allocator) {
- // User-data is only stored for activities actually held in the stack.
- if (id < stack_slots_) {
- // Don't allow user data for lock acquisition as recursion may occur.
- if (stack_[id].activity_type == Activity::ACT_LOCK_ACQUIRE) {
- NOTREACHED();
- return MakeUnique<ActivityUserData>(nullptr, 0);
- }
-
- // Get (or reuse) a block of memory and create a real UserData object
- // on it.
- PersistentMemoryAllocator::Reference ref = allocator->GetObjectReference();
- void* memory =
- allocator->GetAsArray<char>(ref, PersistentMemoryAllocator::kSizeAny);
- if (memory) {
- std::unique_ptr<ActivityUserData> user_data =
- MakeUnique<ActivityUserData>(memory, kUserDataSize);
- stack_[id].user_data_ref = ref;
- stack_[id].user_data_id = user_data->id();
- return user_data;
- }
- }
-
- // Return a dummy object that will still accept (but ignore) Set() calls.
- return MakeUnique<ActivityUserData>(nullptr, 0);
-}
-
-bool ThreadActivityTracker::HasUserData(ActivityId id) {
- // User-data is only stored for activities actually held in the stack.
- return (id < stack_slots_ && stack_[id].user_data_ref);
-}
-
-void ThreadActivityTracker::ReleaseUserData(
- ActivityId id,
- ActivityTrackerMemoryAllocator* allocator) {
- // User-data is only stored for activities actually held in the stack.
- if (id < stack_slots_ && stack_[id].user_data_ref) {
- allocator->ReleaseObjectReference(stack_[id].user_data_ref);
- stack_[id].user_data_ref = 0;
- }
-}
-
-bool ThreadActivityTracker::IsValid() const {
- if (header_->cookie.load(std::memory_order_acquire) != kHeaderCookie ||
- header_->process_id.load(std::memory_order_relaxed) == 0 ||
- header_->thread_ref.as_id == 0 ||
- header_->start_time == 0 ||
- header_->start_ticks == 0 ||
- header_->stack_slots != stack_slots_ ||
- header_->thread_name[sizeof(header_->thread_name) - 1] != '\0') {
- return false;
- }
-
- return valid_;
-}
-
-bool ThreadActivityTracker::CreateSnapshot(Snapshot* output_snapshot) const {
- DCHECK(output_snapshot);
-
- // There is no "called on valid thread" check for this method as it can be
- // called from other threads or even other processes. It is also the reason
- // why atomic operations must be used in certain places above.
-
- // It's possible for the data to change while reading it in such a way that it
- // invalidates the read. Make several attempts but don't try forever.
- const int kMaxAttempts = 10;
- uint32_t depth;
-
- // Stop here if the data isn't valid.
- if (!IsValid())
- return false;
-
- // Allocate the maximum size for the stack so it doesn't have to be done
- // during the time-sensitive snapshot operation. It is shrunk once the
- // actual size is known.
- output_snapshot->activity_stack.reserve(stack_slots_);
-
- for (int attempt = 0; attempt < kMaxAttempts; ++attempt) {
- // Remember the process and thread IDs to ensure they aren't replaced
- // during the snapshot operation. Use "acquire" to ensure that all the
- // non-atomic fields of the structure are valid (at least at the current
- // moment in time).
- const int64_t starting_process_id =
- header_->process_id.load(std::memory_order_acquire);
- const int64_t starting_thread_id = header_->thread_ref.as_id;
-
- // Write a non-zero value to |stack_unchanged| so it's possible to detect
- // at the end that nothing has changed since copying the data began. A
- // "cst" operation is required to ensure it occurs before everything else.
- // Using "cst" memory ordering is relatively expensive but this is only
- // done during analysis so doesn't directly affect the worker threads.
- header_->stack_unchanged.store(1, std::memory_order_seq_cst);
-
- // Fetching the current depth also "acquires" the contents of the stack.
- depth = header_->current_depth.load(std::memory_order_acquire);
- uint32_t count = std::min(depth, stack_slots_);
- output_snapshot->activity_stack.resize(count);
- if (count > 0) {
- // Copy the existing contents. Memcpy is used for speed.
- memcpy(&output_snapshot->activity_stack[0], stack_,
- count * sizeof(Activity));
- }
-
- // Retry if something changed during the copy. A "cst" operation ensures
- // it must happen after all the above operations.
- if (!header_->stack_unchanged.load(std::memory_order_seq_cst))
- continue;
-
- // Stack copied. Record it's full depth.
- output_snapshot->activity_stack_depth = depth;
-
- // TODO(bcwhite): Snapshot other things here.
-
- // Get the general thread information. Loading of "process_id" is guaranteed
- // to be last so that it's possible to detect below if any content has
- // changed while reading it. It's technically possible for a thread to end,
- // have its data cleared, a new thread get created with the same IDs, and
- // it perform an action which starts tracking all in the time since the
- // ID reads above but the chance is so unlikely that it's not worth the
- // effort and complexity of protecting against it (perhaps with an
- // "unchanged" field like is done for the stack).
- output_snapshot->thread_name =
- std::string(header_->thread_name, sizeof(header_->thread_name) - 1);
- output_snapshot->thread_id = header_->thread_ref.as_id;
- output_snapshot->process_id =
- header_->process_id.load(std::memory_order_seq_cst);
-
- // All characters of the thread-name buffer were copied so as to not break
- // if the trailing NUL were missing. Now limit the length if the actual
- // name is shorter.
- output_snapshot->thread_name.resize(
- strlen(output_snapshot->thread_name.c_str()));
-
- // If the process or thread ID has changed then the tracker has exited and
- // the memory reused by a new one. Try again.
- if (output_snapshot->process_id != starting_process_id ||
- output_snapshot->thread_id != starting_thread_id) {
- continue;
- }
-
- // Only successful if the data is still valid once everything is done since
- // it's possible for the thread to end somewhere in the middle and all its
- // values become garbage.
- if (!IsValid())
- return false;
-
- // Change all the timestamps in the activities from "ticks" to "wall" time.
- const Time start_time = Time::FromInternalValue(header_->start_time);
- const int64_t start_ticks = header_->start_ticks;
- for (Activity& activity : output_snapshot->activity_stack) {
- activity.time_internal =
- (start_time +
- TimeDelta::FromInternalValue(activity.time_internal - start_ticks))
- .ToInternalValue();
- }
-
- // Success!
- return true;
- }
-
- // Too many attempts.
- return false;
-}
-
-// static
-size_t ThreadActivityTracker::SizeForStackDepth(int stack_depth) {
- return static_cast<size_t>(stack_depth) * sizeof(Activity) + sizeof(Header);
-}
-
-// The instantiation of the GlobalActivityTracker object.
-// The object held here will obviously not be destructed at process exit
-// but that's best since PersistentMemoryAllocator objects (that underlie
-// GlobalActivityTracker objects) are explicitly forbidden from doing anything
-// essential at exit anyway due to the fact that they depend on data managed
-// elsewhere and which could be destructed first. An AtomicWord is used instead
-// of std::atomic because the latter can create global ctors and dtors.
-subtle::AtomicWord GlobalActivityTracker::g_tracker_ = 0;
-
-GlobalActivityTracker::ModuleInfo::ModuleInfo() {}
-GlobalActivityTracker::ModuleInfo::ModuleInfo(ModuleInfo&& rhs) = default;
-GlobalActivityTracker::ModuleInfo::ModuleInfo(const ModuleInfo& rhs) = default;
-GlobalActivityTracker::ModuleInfo::~ModuleInfo() {}
-
-GlobalActivityTracker::ModuleInfo& GlobalActivityTracker::ModuleInfo::operator=(
- ModuleInfo&& rhs) = default;
-GlobalActivityTracker::ModuleInfo& GlobalActivityTracker::ModuleInfo::operator=(
- const ModuleInfo& rhs) = default;
-
-GlobalActivityTracker::ModuleInfoRecord::ModuleInfoRecord() {}
-GlobalActivityTracker::ModuleInfoRecord::~ModuleInfoRecord() {}
-
-bool GlobalActivityTracker::ModuleInfoRecord::DecodeTo(
- GlobalActivityTracker::ModuleInfo* info,
- size_t record_size) const {
- // Get the current "changes" indicator, acquiring all the other values.
- uint32_t current_changes = changes.load(std::memory_order_acquire);
-
- // Copy out the dynamic information.
- info->is_loaded = loaded != 0;
- info->address = static_cast<uintptr_t>(address);
- info->load_time = load_time;
-
- // Check to make sure no information changed while being read. A "seq-cst"
- // operation is expensive but is only done during analysis and it's the only
- // way to ensure this occurs after all the accesses above. If changes did
- // occur then return a "not loaded" result so that |size| and |address|
- // aren't expected to be accurate.
- if ((current_changes & kModuleInformationChanging) != 0 ||
- changes.load(std::memory_order_seq_cst) != current_changes) {
- info->is_loaded = false;
- }
-
- // Copy out the static information. These never change so don't have to be
- // protected by the atomic |current_changes| operations.
- info->size = static_cast<size_t>(size);
- info->timestamp = timestamp;
- info->age = age;
- memcpy(info->identifier, identifier, sizeof(info->identifier));
-
- if (offsetof(ModuleInfoRecord, pickle) + pickle_size > record_size)
- return false;
- Pickle pickler(pickle, pickle_size);
- PickleIterator iter(pickler);
- return iter.ReadString(&info->file) && iter.ReadString(&info->debug_file);
-}
-
-bool GlobalActivityTracker::ModuleInfoRecord::EncodeFrom(
- const GlobalActivityTracker::ModuleInfo& info,
- size_t record_size) {
- Pickle pickler;
- bool okay =
- pickler.WriteString(info.file) && pickler.WriteString(info.debug_file);
- if (!okay) {
- NOTREACHED();
- return false;
- }
- if (offsetof(ModuleInfoRecord, pickle) + pickler.size() > record_size) {
- NOTREACHED();
- return false;
- }
-
- // These fields never changes and are done before the record is made
- // iterable so no thread protection is necessary.
- size = info.size;
- timestamp = info.timestamp;
- age = info.age;
- memcpy(identifier, info.identifier, sizeof(identifier));
- memcpy(pickle, pickler.data(), pickler.size());
- pickle_size = pickler.size();
- changes.store(0, std::memory_order_relaxed);
-
- // Now set those fields that can change.
- return UpdateFrom(info);
-}
-
-bool GlobalActivityTracker::ModuleInfoRecord::UpdateFrom(
- const GlobalActivityTracker::ModuleInfo& info) {
- // Updates can occur after the record is made visible so make changes atomic.
- // A "strong" exchange ensures no false failures.
- uint32_t old_changes = changes.load(std::memory_order_relaxed);
- uint32_t new_changes = old_changes | kModuleInformationChanging;
- if ((old_changes & kModuleInformationChanging) != 0 ||
- !changes.compare_exchange_strong(old_changes, new_changes,
- std::memory_order_acquire,
- std::memory_order_acquire)) {
- NOTREACHED() << "Multiple sources are updating module information.";
- return false;
- }
-
- loaded = info.is_loaded ? 1 : 0;
- address = info.address;
- load_time = Time::Now().ToInternalValue();
-
- bool success = changes.compare_exchange_strong(new_changes, old_changes + 1,
- std::memory_order_release,
- std::memory_order_relaxed);
- DCHECK(success);
- return true;
-}
-
-// static
-size_t GlobalActivityTracker::ModuleInfoRecord::EncodedSize(
- const GlobalActivityTracker::ModuleInfo& info) {
- PickleSizer sizer;
- sizer.AddString(info.file);
- sizer.AddString(info.debug_file);
-
- return offsetof(ModuleInfoRecord, pickle) + sizeof(Pickle::Header) +
- sizer.payload_size();
-}
-
-GlobalActivityTracker::ScopedThreadActivity::ScopedThreadActivity(
- const void* program_counter,
- const void* origin,
- Activity::Type type,
- const ActivityData& data,
- bool lock_allowed)
- : ThreadActivityTracker::ScopedActivity(GetOrCreateTracker(lock_allowed),
- program_counter,
- origin,
- type,
- data) {}
-
-GlobalActivityTracker::ScopedThreadActivity::~ScopedThreadActivity() {
- if (tracker_ && tracker_->HasUserData(activity_id_)) {
- GlobalActivityTracker* global = GlobalActivityTracker::Get();
- AutoLock lock(global->user_data_allocator_lock_);
- tracker_->ReleaseUserData(activity_id_, &global->user_data_allocator_);
- }
-}
-
-ActivityUserData& GlobalActivityTracker::ScopedThreadActivity::user_data() {
- if (!user_data_) {
- if (tracker_) {
- GlobalActivityTracker* global = GlobalActivityTracker::Get();
- AutoLock lock(global->user_data_allocator_lock_);
- user_data_ =
- tracker_->GetUserData(activity_id_, &global->user_data_allocator_);
- } else {
- user_data_ = MakeUnique<ActivityUserData>(nullptr, 0);
- }
- }
- return *user_data_;
-}
-
-GlobalActivityTracker::GlobalUserData::GlobalUserData(void* memory, size_t size)
- : ActivityUserData(memory, size) {}
-
-GlobalActivityTracker::GlobalUserData::~GlobalUserData() {}
-
-void GlobalActivityTracker::GlobalUserData::Set(StringPiece name,
- ValueType type,
- const void* memory,
- size_t size) {
- AutoLock lock(data_lock_);
- ActivityUserData::Set(name, type, memory, size);
-}
-
-GlobalActivityTracker::ManagedActivityTracker::ManagedActivityTracker(
- PersistentMemoryAllocator::Reference mem_reference,
- void* base,
- size_t size)
- : ThreadActivityTracker(base, size),
- mem_reference_(mem_reference),
- mem_base_(base) {}
-
-GlobalActivityTracker::ManagedActivityTracker::~ManagedActivityTracker() {
- // The global |g_tracker_| must point to the owner of this class since all
- // objects of this type must be destructed before |g_tracker_| can be changed
- // (something that only occurs in tests).
- DCHECK(g_tracker_);
- GlobalActivityTracker::Get()->ReturnTrackerMemory(this);
-}
-
-void GlobalActivityTracker::CreateWithAllocator(
- std::unique_ptr<PersistentMemoryAllocator> allocator,
- int stack_depth) {
- // There's no need to do anything with the result. It is self-managing.
- GlobalActivityTracker* global_tracker =
- new GlobalActivityTracker(std::move(allocator), stack_depth);
- // Create a tracker for this thread since it is known.
- global_tracker->CreateTrackerForCurrentThread();
-}
-
-#if !defined(OS_NACL)
-// static
-void GlobalActivityTracker::CreateWithFile(const FilePath& file_path,
- size_t size,
- uint64_t id,
- StringPiece name,
- int stack_depth) {
- DCHECK(!file_path.empty());
- DCHECK_GE(static_cast<uint64_t>(std::numeric_limits<int64_t>::max()), size);
-
- // Create and map the file into memory and make it globally available.
- std::unique_ptr<MemoryMappedFile> mapped_file(new MemoryMappedFile());
- bool success =
- mapped_file->Initialize(File(file_path,
- File::FLAG_CREATE_ALWAYS | File::FLAG_READ |
- File::FLAG_WRITE | File::FLAG_SHARE_DELETE),
- {0, static_cast<int64_t>(size)},
- MemoryMappedFile::READ_WRITE_EXTEND);
- DCHECK(success);
- CreateWithAllocator(MakeUnique<FilePersistentMemoryAllocator>(
- std::move(mapped_file), size, id, name, false),
- stack_depth);
-}
-#endif // !defined(OS_NACL)
-
-// static
-void GlobalActivityTracker::CreateWithLocalMemory(size_t size,
- uint64_t id,
- StringPiece name,
- int stack_depth) {
- CreateWithAllocator(
- MakeUnique<LocalPersistentMemoryAllocator>(size, id, name), stack_depth);
-}
-
-ThreadActivityTracker* GlobalActivityTracker::CreateTrackerForCurrentThread() {
- DCHECK(!this_thread_tracker_.Get());
-
- PersistentMemoryAllocator::Reference mem_reference;
-
- {
- base::AutoLock autolock(thread_tracker_allocator_lock_);
- mem_reference = thread_tracker_allocator_.GetObjectReference();
- }
-
- if (!mem_reference) {
- // Failure. This shouldn't happen. But be graceful if it does, probably
- // because the underlying allocator wasn't given enough memory to satisfy
- // to all possible requests.
- NOTREACHED();
- // Report the thread-count at which the allocator was full so that the
- // failure can be seen and underlying memory resized appropriately.
- UMA_HISTOGRAM_COUNTS_1000(
- "ActivityTracker.ThreadTrackers.MemLimitTrackerCount",
- thread_tracker_count_.load(std::memory_order_relaxed));
- // Return null, just as if tracking wasn't enabled.
- return nullptr;
- }
-
- // Convert the memory block found above into an actual memory address.
- // Doing the conversion as a Header object enacts the 32/64-bit size
- // consistency checks which would not otherwise be done. Unfortunately,
- // some older compilers and MSVC don't have standard-conforming definitions
- // of std::atomic which cause it not to be plain-old-data. Don't check on
- // those platforms assuming that the checks on other platforms will be
- // sufficient.
- // TODO(bcwhite): Review this after major compiler releases.
- DCHECK(mem_reference);
- void* mem_base;
- mem_base =
- allocator_->GetAsObject<ThreadActivityTracker::Header>(mem_reference);
-
- DCHECK(mem_base);
- DCHECK_LE(stack_memory_size_, allocator_->GetAllocSize(mem_reference));
-
- // Create a tracker with the acquired memory and set it as the tracker
- // for this particular thread in thread-local-storage.
- ManagedActivityTracker* tracker =
- new ManagedActivityTracker(mem_reference, mem_base, stack_memory_size_);
- DCHECK(tracker->IsValid());
- this_thread_tracker_.Set(tracker);
- int old_count = thread_tracker_count_.fetch_add(1, std::memory_order_relaxed);
-
- UMA_HISTOGRAM_ENUMERATION("ActivityTracker.ThreadTrackers.Count",
- old_count + 1, kMaxThreadCount);
- return tracker;
-}
-
-void GlobalActivityTracker::ReleaseTrackerForCurrentThreadForTesting() {
- ThreadActivityTracker* tracker =
- reinterpret_cast<ThreadActivityTracker*>(this_thread_tracker_.Get());
- if (tracker)
- delete tracker;
-}
-
-void GlobalActivityTracker::RecordLogMessage(StringPiece message) {
- // Allocate at least one extra byte so the string is NUL terminated. All
- // memory returned by the allocator is guaranteed to be zeroed.
- PersistentMemoryAllocator::Reference ref =
- allocator_->Allocate(message.size() + 1, kTypeIdGlobalLogMessage);
- char* memory = allocator_->GetAsArray<char>(ref, kTypeIdGlobalLogMessage,
- message.size() + 1);
- if (memory) {
- memcpy(memory, message.data(), message.size());
- allocator_->MakeIterable(ref);
- }
-}
-
-void GlobalActivityTracker::RecordModuleInfo(const ModuleInfo& info) {
- AutoLock lock(modules_lock_);
- auto found = modules_.find(info.file);
- if (found != modules_.end()) {
- ModuleInfoRecord* record = found->second;
- DCHECK(record);
-
- // Update the basic state of module information that has been already
- // recorded. It is assumed that the string information (identifier,
- // version, etc.) remain unchanged which means that there's no need
- // to create a new record to accommodate a possibly longer length.
- record->UpdateFrom(info);
- return;
- }
-
- size_t required_size = ModuleInfoRecord::EncodedSize(info);
- ModuleInfoRecord* record = allocator_->New<ModuleInfoRecord>(required_size);
- if (!record)
- return;
-
- bool success = record->EncodeFrom(info, required_size);
- DCHECK(success);
- allocator_->MakeIterable(record);
- modules_.insert(std::make_pair(info.file, record));
-}
-
-void GlobalActivityTracker::RecordFieldTrial(const std::string& trial_name,
- StringPiece group_name) {
- const std::string key = std::string("FieldTrial.") + trial_name;
- global_data_.SetString(key, group_name);
-}
-
-GlobalActivityTracker::GlobalActivityTracker(
- std::unique_ptr<PersistentMemoryAllocator> allocator,
- int stack_depth)
- : allocator_(std::move(allocator)),
- stack_memory_size_(ThreadActivityTracker::SizeForStackDepth(stack_depth)),
- this_thread_tracker_(&OnTLSDestroy),
- thread_tracker_count_(0),
- thread_tracker_allocator_(allocator_.get(),
- kTypeIdActivityTracker,
- kTypeIdActivityTrackerFree,
- stack_memory_size_,
- kCachedThreadMemories,
- /*make_iterable=*/true),
- user_data_allocator_(allocator_.get(),
- kTypeIdUserDataRecord,
- kTypeIdUserDataRecordFree,
- kUserDataSize,
- kCachedUserDataMemories,
- /*make_iterable=*/false),
- global_data_(
- allocator_->GetAsArray<char>(
- allocator_->Allocate(kGlobalDataSize, kTypeIdGlobalDataRecord),
- kTypeIdGlobalDataRecord,
- PersistentMemoryAllocator::kSizeAny),
- kGlobalDataSize) {
- // Ensure the passed memory is valid and empty (iterator finds nothing).
- uint32_t type;
- DCHECK(!PersistentMemoryAllocator::Iterator(allocator_.get()).GetNext(&type));
-
- // Ensure that there is no other global object and then make this one such.
- DCHECK(!g_tracker_);
- subtle::Release_Store(&g_tracker_, reinterpret_cast<uintptr_t>(this));
-
- // The global records must be iterable in order to be found by an analyzer.
- allocator_->MakeIterable(allocator_->GetAsReference(
- global_data_.GetBaseAddress(), kTypeIdGlobalDataRecord));
-
- // Fetch and record all activated field trials.
- FieldTrial::ActiveGroups active_groups;
- FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
- for (auto& group : active_groups)
- RecordFieldTrial(group.trial_name, group.group_name);
-}
-
-GlobalActivityTracker::~GlobalActivityTracker() {
- DCHECK_EQ(Get(), this);
- DCHECK_EQ(0, thread_tracker_count_.load(std::memory_order_relaxed));
- subtle::Release_Store(&g_tracker_, 0);
-}
-
-void GlobalActivityTracker::ReturnTrackerMemory(
- ManagedActivityTracker* tracker) {
- PersistentMemoryAllocator::Reference mem_reference = tracker->mem_reference_;
- void* mem_base = tracker->mem_base_;
- DCHECK(mem_reference);
- DCHECK(mem_base);
-
- // Remove the destructed tracker from the set of known ones.
- DCHECK_LE(1, thread_tracker_count_.load(std::memory_order_relaxed));
- thread_tracker_count_.fetch_sub(1, std::memory_order_relaxed);
-
- // Release this memory for re-use at a later time.
- base::AutoLock autolock(thread_tracker_allocator_lock_);
- thread_tracker_allocator_.ReleaseObjectReference(mem_reference);
-}
-
-// static
-void GlobalActivityTracker::OnTLSDestroy(void* value) {
- delete reinterpret_cast<ManagedActivityTracker*>(value);
-}
-
-ScopedActivity::ScopedActivity(const void* program_counter,
- uint8_t action,
- uint32_t id,
- int32_t info)
- : GlobalActivityTracker::ScopedThreadActivity(
- program_counter,
- nullptr,
- static_cast<Activity::Type>(Activity::ACT_GENERIC | action),
- ActivityData::ForGeneric(id, info),
- /*lock_allowed=*/true),
- id_(id) {
- // The action must not affect the category bits of the activity type.
- DCHECK_EQ(0, action & Activity::ACT_CATEGORY_MASK);
-}
-
-void ScopedActivity::ChangeAction(uint8_t action) {
- DCHECK_EQ(0, action & Activity::ACT_CATEGORY_MASK);
- ChangeTypeAndData(static_cast<Activity::Type>(Activity::ACT_GENERIC | action),
- kNullActivityData);
-}
-
-void ScopedActivity::ChangeInfo(int32_t info) {
- ChangeTypeAndData(Activity::ACT_NULL, ActivityData::ForGeneric(id_, info));
-}
-
-void ScopedActivity::ChangeActionAndInfo(uint8_t action, int32_t info) {
- DCHECK_EQ(0, action & Activity::ACT_CATEGORY_MASK);
- ChangeTypeAndData(static_cast<Activity::Type>(Activity::ACT_GENERIC | action),
- ActivityData::ForGeneric(id_, info));
-}
-
-ScopedTaskRunActivity::ScopedTaskRunActivity(
- const void* program_counter,
- const base::PendingTask& task)
- : GlobalActivityTracker::ScopedThreadActivity(
- program_counter,
- task.posted_from.program_counter(),
- Activity::ACT_TASK_RUN,
- ActivityData::ForTask(task.sequence_num),
- /*lock_allowed=*/true) {}
-
-ScopedLockAcquireActivity::ScopedLockAcquireActivity(
- const void* program_counter,
- const base::internal::LockImpl* lock)
- : GlobalActivityTracker::ScopedThreadActivity(
- program_counter,
- nullptr,
- Activity::ACT_LOCK_ACQUIRE,
- ActivityData::ForLock(lock),
- /*lock_allowed=*/false) {}
-
-ScopedEventWaitActivity::ScopedEventWaitActivity(
- const void* program_counter,
- const base::WaitableEvent* event)
- : GlobalActivityTracker::ScopedThreadActivity(
- program_counter,
- nullptr,
- Activity::ACT_EVENT_WAIT,
- ActivityData::ForEvent(event),
- /*lock_allowed=*/true) {}
-
-ScopedThreadJoinActivity::ScopedThreadJoinActivity(
- const void* program_counter,
- const base::PlatformThreadHandle* thread)
- : GlobalActivityTracker::ScopedThreadActivity(
- program_counter,
- nullptr,
- Activity::ACT_THREAD_JOIN,
- ActivityData::ForThread(*thread),
- /*lock_allowed=*/true) {}
-
-#if !defined(OS_NACL) && !defined(OS_IOS)
-ScopedProcessWaitActivity::ScopedProcessWaitActivity(
- const void* program_counter,
- const base::Process* process)
- : GlobalActivityTracker::ScopedThreadActivity(
- program_counter,
- nullptr,
- Activity::ACT_PROCESS_WAIT,
- ActivityData::ForProcess(process->Pid()),
- /*lock_allowed=*/true) {}
-#endif
-
-} // namespace debug
-} // namespace base
diff --git a/base/debug/activity_tracker.h b/base/debug/activity_tracker.h
deleted file mode 100644
index 719a31865c..0000000000
--- a/base/debug/activity_tracker.h
+++ /dev/null
@@ -1,1102 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Activity tracking provides a low-overhead method of collecting information
-// about the state of the application for analysis both while it is running
-// and after it has terminated unexpectedly. Its primary purpose is to help
-// locate reasons the browser becomes unresponsive by providing insight into
-// what all the various threads and processes are (or were) doing.
-
-#ifndef BASE_DEBUG_ACTIVITY_TRACKER_H_
-#define BASE_DEBUG_ACTIVITY_TRACKER_H_
-
-// std::atomic is undesired due to performance issues when used as global
-// variables. There are no such instances here. This module uses the
-// PersistentMemoryAllocator which also uses std::atomic and is written
-// by the same author.
-#include <atomic>
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/atomicops.h"
-#include "base/base_export.h"
-#include "base/compiler_specific.h"
-#include "base/gtest_prod_util.h"
-#include "base/location.h"
-#include "base/metrics/persistent_memory_allocator.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/threading/platform_thread.h"
-#include "base/threading/thread_checker.h"
-#include "base/threading/thread_local_storage.h"
-
-namespace base {
-
-struct PendingTask;
-
-class FilePath;
-class Lock;
-class PlatformThreadHandle;
-class Process;
-class StaticAtomicSequenceNumber;
-class WaitableEvent;
-
-namespace debug {
-
-class ThreadActivityTracker;
-
-
-enum : int {
- // The maximum number of call-stack addresses stored per activity. This
- // cannot be changed without also changing the version number of the
- // structure. See kTypeIdActivityTracker in GlobalActivityTracker.
- kActivityCallStackSize = 10,
-};
-
-// The data associated with an activity is dependent upon the activity type.
-// This union defines all of the various fields. All fields must be explicitly
-// sized types to ensure no interoperability problems between 32-bit and
-// 64-bit systems.
-union ActivityData {
- // Generic activities don't have any defined structure.
- struct {
- uint32_t id; // An arbitrary identifier used for association.
- int32_t info; // An arbitrary value used for information purposes.
- } generic;
- struct {
- uint64_t sequence_id; // The sequence identifier of the posted task.
- } task;
- struct {
- uint64_t lock_address; // The memory address of the lock object.
- } lock;
- struct {
- uint64_t event_address; // The memory address of the event object.
- } event;
- struct {
- int64_t thread_id; // A unique identifier for a thread within a process.
- } thread;
- struct {
- int64_t process_id; // A unique identifier for a process.
- } process;
-
- // These methods create an ActivityData object from the appropriate
- // parameters. Objects of this type should always be created this way to
- // ensure that no fields remain unpopulated should the set of recorded
- // fields change. They're defined inline where practical because they
- // reduce to loading a small local structure with a few values, roughly
- // the same as loading all those values into parameters.
-
- static ActivityData ForGeneric(uint32_t id, int32_t info) {
- ActivityData data;
- data.generic.id = id;
- data.generic.info = info;
- return data;
- }
-
- static ActivityData ForTask(uint64_t sequence) {
- ActivityData data;
- data.task.sequence_id = sequence;
- return data;
- }
-
- static ActivityData ForLock(const void* lock) {
- ActivityData data;
- data.lock.lock_address = reinterpret_cast<uintptr_t>(lock);
- return data;
- }
-
- static ActivityData ForEvent(const void* event) {
- ActivityData data;
- data.event.event_address = reinterpret_cast<uintptr_t>(event);
- return data;
- }
-
- static ActivityData ForThread(const PlatformThreadHandle& handle);
- static ActivityData ForThread(const int64_t id) {
- ActivityData data;
- data.thread.thread_id = id;
- return data;
- }
-
- static ActivityData ForProcess(const int64_t id) {
- ActivityData data;
- data.process.process_id = id;
- return data;
- }
-};
-
-// A "null" activity-data that can be passed to indicate "do not change".
-extern const ActivityData kNullActivityData;
-
-
-// A helper class that is used for managing memory allocations within a
-// persistent memory allocator. Instances of this class are NOT thread-safe.
-// Use from a single thread or protect access with a lock.
-class BASE_EXPORT ActivityTrackerMemoryAllocator {
- public:
- using Reference = PersistentMemoryAllocator::Reference;
-
- // Creates a instance for allocating objects of a fixed |object_type|, a
- // corresponding |object_free| type, and the |object_size|. An internal
- // cache of the last |cache_size| released references will be kept for
- // quick future fetches. If |make_iterable| then allocated objects will
- // be marked "iterable" in the allocator.
- ActivityTrackerMemoryAllocator(PersistentMemoryAllocator* allocator,
- uint32_t object_type,
- uint32_t object_free_type,
- size_t object_size,
- size_t cache_size,
- bool make_iterable);
- ~ActivityTrackerMemoryAllocator();
-
- // Gets a reference to an object of the configured type. This can return
- // a null reference if it was not possible to allocate the memory.
- Reference GetObjectReference();
-
- // Returns an object to the "free" pool.
- void ReleaseObjectReference(Reference ref);
-
- // Helper function to access an object allocated using this instance.
- template <typename T>
- T* GetAsObject(Reference ref) {
- return allocator_->GetAsObject<T>(ref);
- }
-
- // Similar to GetAsObject() but converts references to arrays of objects.
- template <typename T>
- T* GetAsArray(Reference ref, size_t count) {
- return allocator_->GetAsArray<T>(ref, object_type_, count);
- }
-
- // The current "used size" of the internal cache, visible for testing.
- size_t cache_used() const { return cache_used_; }
-
- private:
- PersistentMemoryAllocator* const allocator_;
- const uint32_t object_type_;
- const uint32_t object_free_type_;
- const size_t object_size_;
- const size_t cache_size_;
- const bool make_iterable_;
-
- // An iterator for going through persistent memory looking for free'd objects.
- PersistentMemoryAllocator::Iterator iterator_;
-
- // The cache of released object memories.
- std::unique_ptr<Reference[]> cache_values_;
- size_t cache_used_;
-
- DISALLOW_COPY_AND_ASSIGN(ActivityTrackerMemoryAllocator);
-};
-
-
-// This structure is the full contents recorded for every activity pushed
-// onto the stack. The |activity_type| indicates what is actually stored in
-// the |data| field. All fields must be explicitly sized types to ensure no
-// interoperability problems between 32-bit and 64-bit systems.
-struct Activity {
- // SHA1(base::debug::Activity): Increment this if structure changes!
- static constexpr uint32_t kPersistentTypeId = 0x99425159 + 1;
- // Expected size for 32/64-bit check. Update this if structure changes!
- static constexpr size_t kExpectedInstanceSize =
- 48 + 8 * kActivityCallStackSize;
-
- // The type of an activity on the stack. Activities are broken into
- // categories with the category ID taking the top 4 bits and the lower
- // bits representing an action within that category. This combination
- // makes it easy to "switch" based on the type during analysis.
- enum Type : uint8_t {
- // This "null" constant is used to indicate "do not change" in calls.
- ACT_NULL = 0,
-
- // Task activities involve callbacks posted to a thread or thread-pool
- // using the PostTask() method or any of its friends.
- ACT_TASK = 1 << 4,
- ACT_TASK_RUN = ACT_TASK,
-
- // Lock activities involve the acquisition of "mutex" locks.
- ACT_LOCK = 2 << 4,
- ACT_LOCK_ACQUIRE = ACT_LOCK,
- ACT_LOCK_RELEASE,
-
- // Event activities involve operations on a WaitableEvent.
- ACT_EVENT = 3 << 4,
- ACT_EVENT_WAIT = ACT_EVENT,
- ACT_EVENT_SIGNAL,
-
- // Thread activities involve the life management of threads.
- ACT_THREAD = 4 << 4,
- ACT_THREAD_START = ACT_THREAD,
- ACT_THREAD_JOIN,
-
- // Process activities involve the life management of processes.
- ACT_PROCESS = 5 << 4,
- ACT_PROCESS_START = ACT_PROCESS,
- ACT_PROCESS_WAIT,
-
- // Generic activities are user defined and can be anything.
- ACT_GENERIC = 15 << 4,
-
- // These constants can be used to separate the category and action from
- // a combined activity type.
- ACT_CATEGORY_MASK = 0xF << 4,
- ACT_ACTION_MASK = 0xF
- };
-
- // Internal representation of time. During collection, this is in "ticks"
- // but when returned in a snapshot, it is "wall time".
- int64_t time_internal;
-
- // The address that pushed the activity onto the stack as a raw number.
- uint64_t calling_address;
-
- // The address that is the origin of the activity if it not obvious from
- // the call stack. This is useful for things like tasks that are posted
- // from a completely different thread though most activities will leave
- // it null.
- uint64_t origin_address;
-
- // Array of program-counters that make up the top of the call stack.
- // Despite the fixed size, this list is always null-terminated. Entries
- // after the terminator have no meaning and may or may not also be null.
- // The list will be completely empty if call-stack collection is not
- // enabled.
- uint64_t call_stack[kActivityCallStackSize];
-
- // Reference to arbitrary user data within the persistent memory segment
- // and a unique identifier for it.
- uint32_t user_data_ref;
- uint32_t user_data_id;
-
- // The (enumerated) type of the activity. This defines what fields of the
- // |data| record are valid.
- uint8_t activity_type;
-
- // Padding to ensure that the next member begins on a 64-bit boundary
- // even on 32-bit builds which ensures inter-operability between CPU
- // architectures. New fields can be taken from this space.
- uint8_t padding[7];
-
- // Information specific to the |activity_type|.
- ActivityData data;
-
- static void FillFrom(Activity* activity,
- const void* program_counter,
- const void* origin,
- Type type,
- const ActivityData& data);
-};
-
-// This class manages arbitrary user data that can be associated with activities
-// done by a thread by supporting key/value pairs of any type. This can provide
-// additional information during debugging. It is also used to store arbitrary
-// global data. All updates must be done from the same thread.
-class BASE_EXPORT ActivityUserData {
- public:
- // List of known value type. REFERENCE types must immediately follow the non-
- // external types.
- enum ValueType : uint8_t {
- END_OF_VALUES = 0,
- RAW_VALUE,
- RAW_VALUE_REFERENCE,
- STRING_VALUE,
- STRING_VALUE_REFERENCE,
- CHAR_VALUE,
- BOOL_VALUE,
- SIGNED_VALUE,
- UNSIGNED_VALUE,
- };
-
- class BASE_EXPORT TypedValue {
- public:
- TypedValue();
- TypedValue(const TypedValue& other);
- ~TypedValue();
-
- ValueType type() const { return type_; }
-
- // These methods return the extracted value in the correct format.
- StringPiece Get() const;
- StringPiece GetString() const;
- bool GetBool() const;
- char GetChar() const;
- int64_t GetInt() const;
- uint64_t GetUint() const;
-
- // These methods return references to process memory as originally provided
- // to corresponding Set calls. USE WITH CAUTION! There is no guarantee that
- // the referenced memory is assessible or useful. It's possible that:
- // - the memory was free'd and reallocated for a different purpose
- // - the memory has been released back to the OS
- // - the memory belongs to a different process's address space
- // Dereferencing the returned StringPiece when the memory is not accessible
- // will cause the program to SEGV!
- StringPiece GetReference() const;
- StringPiece GetStringReference() const;
-
- private:
- friend class ActivityUserData;
-
- ValueType type_;
- uint64_t short_value_; // Used to hold copy of numbers, etc.
- std::string long_value_; // Used to hold copy of raw/string data.
- StringPiece ref_value_; // Used to hold reference to external data.
- };
-
- using Snapshot = std::map<std::string, TypedValue>;
-
- ActivityUserData(void* memory, size_t size);
- virtual ~ActivityUserData();
-
- // Gets the unique ID number for this user data. If this changes then the
- // contents have been overwritten by another thread. The return value is
- // always non-zero unless it's actually just a data "sink".
- uint32_t id() const {
- return memory_ ? id_->load(std::memory_order_relaxed) : 0;
- }
-
- // Writes a |value| (as part of a key/value pair) that will be included with
- // the activity in any reports. The same |name| can be written multiple times
- // with each successive call overwriting the previously stored |value|. For
- // raw and string values, the maximum size of successive writes is limited by
- // the first call. The length of "name" is limited to 255 characters.
- //
- // This information is stored on a "best effort" basis. It may be dropped if
- // the memory buffer is full or the associated activity is beyond the maximum
- // recording depth.
- void Set(StringPiece name, const void* memory, size_t size) {
- Set(name, RAW_VALUE, memory, size);
- }
- void SetString(StringPiece name, StringPiece value) {
- Set(name, STRING_VALUE, value.data(), value.length());
- }
- void SetString(StringPiece name, StringPiece16 value) {
- SetString(name, UTF16ToUTF8(value));
- }
- void SetBool(StringPiece name, bool value) {
- char cvalue = value ? 1 : 0;
- Set(name, BOOL_VALUE, &cvalue, sizeof(cvalue));
- }
- void SetChar(StringPiece name, char value) {
- Set(name, CHAR_VALUE, &value, sizeof(value));
- }
- void SetInt(StringPiece name, int64_t value) {
- Set(name, SIGNED_VALUE, &value, sizeof(value));
- }
- void SetUint(StringPiece name, uint64_t value) {
- Set(name, UNSIGNED_VALUE, &value, sizeof(value));
- }
-
- // These function as above but don't actually copy the data into the
- // persistent memory. They store unaltered pointers along with a size. These
- // can be used in conjuction with a memory dump to find certain large pieces
- // of information.
- void SetReference(StringPiece name, const void* memory, size_t size) {
- SetReference(name, RAW_VALUE_REFERENCE, memory, size);
- }
- void SetStringReference(StringPiece name, StringPiece value) {
- SetReference(name, STRING_VALUE_REFERENCE, value.data(), value.length());
- }
-
- // Creates a snapshot of the key/value pairs contained within. The returned
- // data will be fixed, independent of whatever changes afterward. There is
- // protection against concurrent modification of the values but no protection
- // against a complete overwrite of the contents; the caller must ensure that
- // the memory segment is not going to be re-initialized while this runs.
- bool CreateSnapshot(Snapshot* output_snapshot) const;
-
- // Gets the base memory address used for storing data.
- const void* GetBaseAddress();
-
- protected:
- virtual void Set(StringPiece name,
- ValueType type,
- const void* memory,
- size_t size);
-
- private:
- FRIEND_TEST_ALL_PREFIXES(ActivityTrackerTest, UserDataTest);
-
- enum : size_t { kMemoryAlignment = sizeof(uint64_t) };
-
- // A structure used to reference data held outside of persistent memory.
- struct ReferenceRecord {
- uint64_t address;
- uint64_t size;
- };
-
- // Header to a key/value record held in persistent memory.
- struct Header {
- std::atomic<uint8_t> type; // Encoded ValueType
- uint8_t name_size; // Length of "name" key.
- std::atomic<uint16_t> value_size; // Actual size of of the stored value.
- uint16_t record_size; // Total storage of name, value, header.
- };
-
- // This record is used to hold known value is a map so that they can be
- // found and overwritten later.
- struct ValueInfo {
- ValueInfo();
- ValueInfo(ValueInfo&&);
- ~ValueInfo();
-
- StringPiece name; // The "key" of the record.
- ValueType type; // The type of the value.
- void* memory; // Where the "value" is held.
- std::atomic<uint16_t>* size_ptr; // Address of the actual size of value.
- size_t extent; // The total storage of the value,
- }; // typically rounded up for alignment.
-
- void SetReference(StringPiece name,
- ValueType type,
- const void* memory,
- size_t size);
-
- // Loads any data already in the memory segment. This allows for accessing
- // records created previously.
- void ImportExistingData() const;
-
- // A map of all the values within the memory block, keyed by name for quick
- // updates of the values. This is "mutable" because it changes on "const"
- // objects even when the actual data values can't change.
- mutable std::map<StringPiece, ValueInfo> values_;
-
- // Information about the memory block in which new data can be stored. These
- // are "mutable" because they change even on "const" objects that are just
- // skipping already set values.
- mutable char* memory_;
- mutable size_t available_;
-
- // A pointer to the unique ID for this instance.
- std::atomic<uint32_t>* const id_;
-
- // This ID is used to create unique indentifiers for user data so that it's
- // possible to tell if the information has been overwritten.
- static StaticAtomicSequenceNumber next_id_;
-
- DISALLOW_COPY_AND_ASSIGN(ActivityUserData);
-};
-
-// This class manages tracking a stack of activities for a single thread in
-// a persistent manner, implementing a bounded-size stack in a fixed-size
-// memory allocation. In order to support an operational mode where another
-// thread is analyzing this data in real-time, atomic operations are used
-// where necessary to guarantee a consistent view from the outside.
-//
-// This class is not generally used directly but instead managed by the
-// GlobalActivityTracker instance and updated using Scoped*Activity local
-// objects.
-class BASE_EXPORT ThreadActivityTracker {
- public:
- using ActivityId = uint32_t;
-
- // This structure contains all the common information about the thread so
- // it doesn't have to be repeated in every entry on the stack. It is defined
- // and used completely within the .cc file.
- struct Header;
-
- // This structure holds a copy of all the internal data at the moment the
- // "snapshot" operation is done. It is disconnected from the live tracker
- // so that continued operation of the thread will not cause changes here.
- struct BASE_EXPORT Snapshot {
- // Explicit constructor/destructor are needed because of complex types
- // with non-trivial default constructors and destructors.
- Snapshot();
- ~Snapshot();
-
- // The name of the thread as set when it was created. The name may be
- // truncated due to internal length limitations.
- std::string thread_name;
-
- // The process and thread IDs. These values have no meaning other than
- // they uniquely identify a running process and a running thread within
- // that process. Thread-IDs can be re-used across different processes
- // and both can be re-used after the process/thread exits.
- int64_t process_id = 0;
- int64_t thread_id = 0;
-
- // The current stack of activities that are underway for this thread. It
- // is limited in its maximum size with later entries being left off.
- std::vector<Activity> activity_stack;
-
- // The current total depth of the activity stack, including those later
- // entries not recorded in the |activity_stack| vector.
- uint32_t activity_stack_depth = 0;
- };
-
- // This is the base class for having the compiler manage an activity on the
- // tracker's stack. It does nothing but call methods on the passed |tracker|
- // if it is not null, making it safe (and cheap) to create these objects
- // even if activity tracking is not enabled.
- class BASE_EXPORT ScopedActivity {
- public:
- ScopedActivity(ThreadActivityTracker* tracker,
- const void* program_counter,
- const void* origin,
- Activity::Type type,
- const ActivityData& data);
- ~ScopedActivity();
-
- // Changes some basic metadata about the activity.
- void ChangeTypeAndData(Activity::Type type, const ActivityData& data);
-
- protected:
- // The thread tracker to which this object reports. It can be null if
- // activity tracking is not (yet) enabled.
- ThreadActivityTracker* const tracker_;
-
- // An identifier that indicates a specific activity on the stack.
- ActivityId activity_id_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ScopedActivity);
- };
-
- // A ThreadActivityTracker runs on top of memory that is managed externally.
- // It must be large enough for the internal header and a few Activity
- // blocks. See SizeForStackDepth().
- ThreadActivityTracker(void* base, size_t size);
- virtual ~ThreadActivityTracker();
-
- // Indicates that an activity has started from a given |origin| address in
- // the code, though it can be null if the creator's address is not known.
- // The |type| and |data| describe the activity. |program_counter| should be
- // the result of GetProgramCounter() where push is called. Returned is an
- // ID that can be used to adjust the pushed activity.
- ActivityId PushActivity(const void* program_counter,
- const void* origin,
- Activity::Type type,
- const ActivityData& data);
-
- // An inlined version of the above that gets the program counter where it
- // is called.
- ALWAYS_INLINE
- ActivityId PushActivity(const void* origin,
- Activity::Type type,
- const ActivityData& data) {
- return PushActivity(::tracked_objects::GetProgramCounter(), origin, type,
- data);
- }
-
- // Changes the activity |type| and |data| of the top-most entry on the stack.
- // This is useful if the information has changed and it is desireable to
- // track that change without creating a new stack entry. If the type is
- // ACT_NULL or the data is kNullActivityData then that value will remain
- // unchanged. The type, if changed, must remain in the same category.
- // Changing both is not atomic so a snapshot operation could occur between
- // the update of |type| and |data| or between update of |data| fields.
- void ChangeActivity(ActivityId id,
- Activity::Type type,
- const ActivityData& data);
-
- // Indicates that an activity has completed.
- void PopActivity(ActivityId id);
-
- // Sets the user-data information for an activity.
- std::unique_ptr<ActivityUserData> GetUserData(
- ActivityId id,
- ActivityTrackerMemoryAllocator* allocator);
-
- // Returns if there is true use-data associated with a given ActivityId since
- // it's possible than any returned object is just a sink.
- bool HasUserData(ActivityId id);
-
- // Release the user-data information for an activity.
- void ReleaseUserData(ActivityId id,
- ActivityTrackerMemoryAllocator* allocator);
-
- // Returns whether the current data is valid or not. It is not valid if
- // corruption has been detected in the header or other data structures.
- bool IsValid() const;
-
- // Gets a copy of the tracker contents for analysis. Returns false if a
- // snapshot was not possible, perhaps because the data is not valid; the
- // contents of |output_snapshot| are undefined in that case. The current
- // implementation does not support concurrent snapshot operations.
- bool CreateSnapshot(Snapshot* output_snapshot) const;
-
- // Calculates the memory size required for a given stack depth, including
- // the internal header structure for the stack.
- static size_t SizeForStackDepth(int stack_depth);
-
- private:
- friend class ActivityTrackerTest;
-
- Header* const header_; // Pointer to the Header structure.
- Activity* const stack_; // The stack of activities.
- const uint32_t stack_slots_; // The total number of stack slots.
-
- bool valid_ = false; // Tracks whether the data is valid or not.
-
- base::ThreadChecker thread_checker_;
-
- DISALLOW_COPY_AND_ASSIGN(ThreadActivityTracker);
-};
-
-
-// The global tracker manages all the individual thread trackers. Memory for
-// the thread trackers is taken from a PersistentMemoryAllocator which allows
-// for the data to be analyzed by a parallel process or even post-mortem.
-class BASE_EXPORT GlobalActivityTracker {
- public:
- // Type identifiers used when storing in persistent memory so they can be
- // identified during extraction; the first 4 bytes of the SHA1 of the name
- // is used as a unique integer. A "version number" is added to the base
- // so that, if the structure of that object changes, stored older versions
- // will be safely ignored. These are public so that an external process
- // can recognize records of this type within an allocator.
- enum : uint32_t {
- kTypeIdActivityTracker = 0x5D7381AF + 3, // SHA1(ActivityTracker) v3
- kTypeIdUserDataRecord = 0x615EDDD7 + 2, // SHA1(UserDataRecord) v2
- kTypeIdGlobalLogMessage = 0x4CF434F9 + 1, // SHA1(GlobalLogMessage) v1
- kTypeIdGlobalDataRecord = kTypeIdUserDataRecord + 1000,
-
- kTypeIdActivityTrackerFree = ~kTypeIdActivityTracker,
- kTypeIdUserDataRecordFree = ~kTypeIdUserDataRecord,
- };
-
- // This structure contains information about a loaded module, as shown to
- // users of the tracker.
- struct BASE_EXPORT ModuleInfo {
- ModuleInfo();
- ModuleInfo(ModuleInfo&& rhs);
- ModuleInfo(const ModuleInfo& rhs);
- ~ModuleInfo();
-
- ModuleInfo& operator=(ModuleInfo&& rhs);
- ModuleInfo& operator=(const ModuleInfo& rhs);
-
- // Information about where and when the module was loaded/unloaded.
- bool is_loaded = false; // Was the last operation a load or unload?
- uintptr_t address = 0; // Address of the last load operation.
- int64_t load_time = 0; // Time of last change; set automatically.
-
- // Information about the module itself. These never change no matter how
- // many times a module may be loaded and unloaded.
- size_t size = 0; // The size of the loaded module.
- uint32_t timestamp = 0; // Opaque "timestamp" for the module.
- uint32_t age = 0; // Opaque "age" for the module.
- uint8_t identifier[16]; // Opaque identifier (GUID, etc.) for the module.
- std::string file; // The full path to the file. (UTF-8)
- std::string debug_file; // The full path to the debug file.
- };
-
- // This is a thin wrapper around the thread-tracker's ScopedActivity that
- // accesses the global tracker to provide some of the information, notably
- // which thread-tracker to use. It is safe to create even if activity
- // tracking is not enabled.
- class BASE_EXPORT ScopedThreadActivity
- : public ThreadActivityTracker::ScopedActivity {
- public:
- ScopedThreadActivity(const void* program_counter,
- const void* origin,
- Activity::Type type,
- const ActivityData& data,
- bool lock_allowed);
- ~ScopedThreadActivity();
-
- // Returns an object for manipulating user data.
- ActivityUserData& user_data();
-
- private:
- // Gets (or creates) a tracker for the current thread. If locking is not
- // allowed (because a lock is being tracked which would cause recursion)
- // then the attempt to create one if none found will be skipped. Once
- // the tracker for this thread has been created for other reasons, locks
- // will be tracked. The thread-tracker uses locks.
- static ThreadActivityTracker* GetOrCreateTracker(bool lock_allowed) {
- GlobalActivityTracker* global_tracker = Get();
- if (!global_tracker)
- return nullptr;
- if (lock_allowed)
- return global_tracker->GetOrCreateTrackerForCurrentThread();
- else
- return global_tracker->GetTrackerForCurrentThread();
- }
-
- // An object that manages additional user data, created only upon request.
- std::unique_ptr<ActivityUserData> user_data_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedThreadActivity);
- };
-
- ~GlobalActivityTracker();
-
- // Creates a global tracker using a given persistent-memory |allocator| and
- // providing the given |stack_depth| to each thread tracker it manages. The
- // created object is activated so tracking will begin immediately upon return.
- static void CreateWithAllocator(
- std::unique_ptr<PersistentMemoryAllocator> allocator,
- int stack_depth);
-
-#if !defined(OS_NACL)
- // Like above but internally creates an allocator around a disk file with
- // the specified |size| at the given |file_path|. Any existing file will be
- // overwritten. The |id| and |name| are arbitrary and stored in the allocator
- // for reference by whatever process reads it.
- static void CreateWithFile(const FilePath& file_path,
- size_t size,
- uint64_t id,
- StringPiece name,
- int stack_depth);
-#endif // !defined(OS_NACL)
-
- // Like above but internally creates an allocator using local heap memory of
- // the specified size. This is used primarily for unit tests.
- static void CreateWithLocalMemory(size_t size,
- uint64_t id,
- StringPiece name,
- int stack_depth);
-
- // Gets the global activity-tracker or null if none exists.
- static GlobalActivityTracker* Get() {
- return reinterpret_cast<GlobalActivityTracker*>(
- subtle::Acquire_Load(&g_tracker_));
- }
-
- // Convenience method for determining if a global tracker is active.
- static bool IsEnabled() { return Get() != nullptr; }
-
- // Gets the persistent-memory-allocator in which data is stored. Callers
- // can store additional records here to pass more information to the
- // analysis process.
- PersistentMemoryAllocator* allocator() { return allocator_.get(); }
-
- // Gets the thread's activity-tracker if it exists. This is inline for
- // performance reasons and it uses thread-local-storage (TLS) so that there
- // is no significant lookup time required to find the one for the calling
- // thread. Ownership remains with the global tracker.
- ThreadActivityTracker* GetTrackerForCurrentThread() {
- return reinterpret_cast<ThreadActivityTracker*>(this_thread_tracker_.Get());
- }
-
- // Gets the thread's activity-tracker or creates one if none exists. This
- // is inline for performance reasons. Ownership remains with the global
- // tracker.
- ThreadActivityTracker* GetOrCreateTrackerForCurrentThread() {
- ThreadActivityTracker* tracker = GetTrackerForCurrentThread();
- if (tracker)
- return tracker;
- return CreateTrackerForCurrentThread();
- }
-
- // Creates an activity-tracker for the current thread.
- ThreadActivityTracker* CreateTrackerForCurrentThread();
-
- // Releases the activity-tracker for the current thread (for testing only).
- void ReleaseTrackerForCurrentThreadForTesting();
-
- // Records a log message. The current implementation does NOT recycle these
- // only store critical messages such as FATAL ones.
- void RecordLogMessage(StringPiece message);
- static void RecordLogMessageIfEnabled(StringPiece message) {
- GlobalActivityTracker* tracker = Get();
- if (tracker)
- tracker->RecordLogMessage(message);
- }
-
- // Records a module load/unload event. This is safe to call multiple times
- // even with the same information.
- void RecordModuleInfo(const ModuleInfo& info);
- static void RecordModuleInfoIfEnabled(const ModuleInfo& info) {
- GlobalActivityTracker* tracker = Get();
- if (tracker)
- tracker->RecordModuleInfo(info);
- }
-
- // Record field trial information. This call is thread-safe. In addition to
- // this, construction of a GlobalActivityTracker will cause all existing
- // active field trials to be fetched and recorded.
- void RecordFieldTrial(const std::string& trial_name, StringPiece group_name);
- static void RecordFieldTrialIfEnabled(const std::string& trial_name,
- StringPiece group_name) {
- GlobalActivityTracker* tracker = Get();
- if (tracker)
- tracker->RecordFieldTrial(trial_name, group_name);
- }
-
- // Accesses the global data record for storing arbitrary key/value pairs.
- ActivityUserData& global_data() { return global_data_; }
-
- private:
- friend class GlobalActivityAnalyzer;
- friend class ScopedThreadActivity;
- friend class ActivityTrackerTest;
-
- enum : int {
- // The maximum number of threads that can be tracked within a process. If
- // more than this number run concurrently, tracking of new ones may cease.
- kMaxThreadCount = 100,
- kCachedThreadMemories = 10,
- kCachedUserDataMemories = 10,
- };
-
- // A wrapper around ActivityUserData that is thread-safe and thus can be used
- // in the global scope without the requirement of being called from only one
- // thread.
- class GlobalUserData : public ActivityUserData {
- public:
- GlobalUserData(void* memory, size_t size);
- ~GlobalUserData() override;
-
- private:
- void Set(StringPiece name,
- ValueType type,
- const void* memory,
- size_t size) override;
-
- Lock data_lock_;
-
- DISALLOW_COPY_AND_ASSIGN(GlobalUserData);
- };
-
- // State of a module as stored in persistent memory. This supports a single
- // loading of a module only. If modules are loaded multiple times at
- // different addresses, only the last will be recorded and an unload will
- // not revert to the information of any other addresses.
- struct BASE_EXPORT ModuleInfoRecord {
- // SHA1(ModuleInfoRecord): Increment this if structure changes!
- static constexpr uint32_t kPersistentTypeId = 0x05DB5F41 + 1;
-
- // Expected size for 32/64-bit check by PersistentMemoryAllocator.
- static constexpr size_t kExpectedInstanceSize = 56;
-
- // The atomic unfortunately makes this a "complex" class on some compilers
- // and thus requires an out-of-line constructor & destructor even though
- // they do nothing.
- ModuleInfoRecord();
- ~ModuleInfoRecord();
-
- uint64_t address; // The base address of the module.
- uint64_t load_time; // Time of last load/unload.
- uint64_t size; // The size of the module in bytes.
- uint32_t timestamp; // Opaque timestamp of the module.
- uint32_t age; // Opaque "age" associated with the module.
- uint8_t identifier[16]; // Opaque identifier for the module.
- std::atomic<uint32_t> changes; // Number load/unload actions.
- uint16_t pickle_size; // The size of the following pickle.
- uint8_t loaded; // Flag if module is loaded or not.
- char pickle[1]; // Other strings; may allocate larger.
-
- // Decodes/encodes storage structure from more generic info structure.
- bool DecodeTo(GlobalActivityTracker::ModuleInfo* info,
- size_t record_size) const;
- bool EncodeFrom(const GlobalActivityTracker::ModuleInfo& info,
- size_t record_size);
-
- // Updates the core information without changing the encoded strings. This
- // is useful when a known module changes state (i.e. new load or unload).
- bool UpdateFrom(const GlobalActivityTracker::ModuleInfo& info);
-
- // Determines the required memory size for the encoded storage.
- static size_t EncodedSize(const GlobalActivityTracker::ModuleInfo& info);
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ModuleInfoRecord);
- };
-
- // A thin wrapper around the main thread-tracker that keeps additional
- // information that the global tracker needs to handle joined threads.
- class ManagedActivityTracker : public ThreadActivityTracker {
- public:
- ManagedActivityTracker(PersistentMemoryAllocator::Reference mem_reference,
- void* base,
- size_t size);
- ~ManagedActivityTracker() override;
-
- // The reference into persistent memory from which the thread-tracker's
- // memory was created.
- const PersistentMemoryAllocator::Reference mem_reference_;
-
- // The physical address used for the thread-tracker's memory.
- void* const mem_base_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ManagedActivityTracker);
- };
-
- // Creates a global tracker using a given persistent-memory |allocator| and
- // providing the given |stack_depth| to each thread tracker it manages. The
- // created object is activated so tracking has already started upon return.
- GlobalActivityTracker(std::unique_ptr<PersistentMemoryAllocator> allocator,
- int stack_depth);
-
- // Returns the memory used by an activity-tracker managed by this class.
- // It is called during the destruction of a ManagedActivityTracker object.
- void ReturnTrackerMemory(ManagedActivityTracker* tracker);
-
- // Releases the activity-tracker associcated with thread. It is called
- // automatically when a thread is joined and thus there is nothing more to
- // be tracked. |value| is a pointer to a ManagedActivityTracker.
- static void OnTLSDestroy(void* value);
-
- // The persistent-memory allocator from which the memory for all trackers
- // is taken.
- std::unique_ptr<PersistentMemoryAllocator> allocator_;
-
- // The size (in bytes) of memory required by a ThreadActivityTracker to
- // provide the stack-depth requested during construction.
- const size_t stack_memory_size_;
-
- // The activity tracker for the currently executing thread.
- base::ThreadLocalStorage::Slot this_thread_tracker_;
-
- // The number of thread trackers currently active.
- std::atomic<int> thread_tracker_count_;
-
- // A caching memory allocator for thread-tracker objects.
- ActivityTrackerMemoryAllocator thread_tracker_allocator_;
- base::Lock thread_tracker_allocator_lock_;
-
- // A caching memory allocator for user data attached to activity data.
- ActivityTrackerMemoryAllocator user_data_allocator_;
- base::Lock user_data_allocator_lock_;
-
- // An object for holding global arbitrary key value pairs. Values must always
- // be written from the main UI thread.
- GlobalUserData global_data_;
-
- // A map of global module information, keyed by module path.
- std::map<const std::string, ModuleInfoRecord*> modules_;
- base::Lock modules_lock_;
-
- // The active global activity tracker.
- static subtle::AtomicWord g_tracker_;
-
- DISALLOW_COPY_AND_ASSIGN(GlobalActivityTracker);
-};
-
-
-// Record entry in to and out of an arbitrary block of code.
-class BASE_EXPORT ScopedActivity
- : public GlobalActivityTracker::ScopedThreadActivity {
- public:
- // Track activity at the specified FROM_HERE location for an arbitrary
- // 4-bit |action|, an arbitrary 32-bit |id|, and 32-bits of arbitrary
- // |info|. None of these values affect operation; they're all purely
- // for association and analysis. To have unique identifiers across a
- // diverse code-base, create the number by taking the first 8 characters
- // of the hash of the activity being tracked.
- //
- // For example:
- // Tracking method: void MayNeverExit(uint32_t foo) {...}
- // echo -n "MayNeverExit" | sha1sum => e44873ccab21e2b71270da24aa1...
- //
- // void MayNeverExit(int32_t foo) {
- // base::debug::ScopedActivity track_me(0, 0xE44873CC, foo);
- // ...
- // }
- ALWAYS_INLINE
- ScopedActivity(uint8_t action, uint32_t id, int32_t info)
- : ScopedActivity(::tracked_objects::GetProgramCounter(),
- action,
- id,
- info) {}
- ScopedActivity() : ScopedActivity(0, 0, 0) {}
-
- // Changes the |action| and/or |info| of this activity on the stack. This
- // is useful for tracking progress through a function, updating the action
- // to indicate "milestones" in the block (max 16 milestones: 0-15) or the
- // info to reflect other changes. Changing both is not atomic so a snapshot
- // operation could occur between the update of |action| and |info|.
- void ChangeAction(uint8_t action);
- void ChangeInfo(int32_t info);
- void ChangeActionAndInfo(uint8_t action, int32_t info);
-
- private:
- // Constructs the object using a passed-in program-counter.
- ScopedActivity(const void* program_counter,
- uint8_t action,
- uint32_t id,
- int32_t info);
-
- // A copy of the ID code so it doesn't have to be passed by the caller when
- // changing the |info| field.
- uint32_t id_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedActivity);
-};
-
-
-// These "scoped" classes provide easy tracking of various blocking actions.
-
-class BASE_EXPORT ScopedTaskRunActivity
- : public GlobalActivityTracker::ScopedThreadActivity {
- public:
- ALWAYS_INLINE
- explicit ScopedTaskRunActivity(const base::PendingTask& task)
- : ScopedTaskRunActivity(::tracked_objects::GetProgramCounter(),
- task) {}
-
- private:
- ScopedTaskRunActivity(const void* program_counter,
- const base::PendingTask& task);
- DISALLOW_COPY_AND_ASSIGN(ScopedTaskRunActivity);
-};
-
-class BASE_EXPORT ScopedLockAcquireActivity
- : public GlobalActivityTracker::ScopedThreadActivity {
- public:
- ALWAYS_INLINE
- explicit ScopedLockAcquireActivity(const base::internal::LockImpl* lock)
- : ScopedLockAcquireActivity(::tracked_objects::GetProgramCounter(),
- lock) {}
-
- private:
- ScopedLockAcquireActivity(const void* program_counter,
- const base::internal::LockImpl* lock);
- DISALLOW_COPY_AND_ASSIGN(ScopedLockAcquireActivity);
-};
-
-class BASE_EXPORT ScopedEventWaitActivity
- : public GlobalActivityTracker::ScopedThreadActivity {
- public:
- ALWAYS_INLINE
- explicit ScopedEventWaitActivity(const base::WaitableEvent* event)
- : ScopedEventWaitActivity(::tracked_objects::GetProgramCounter(),
- event) {}
-
- private:
- ScopedEventWaitActivity(const void* program_counter,
- const base::WaitableEvent* event);
- DISALLOW_COPY_AND_ASSIGN(ScopedEventWaitActivity);
-};
-
-class BASE_EXPORT ScopedThreadJoinActivity
- : public GlobalActivityTracker::ScopedThreadActivity {
- public:
- ALWAYS_INLINE
- explicit ScopedThreadJoinActivity(const base::PlatformThreadHandle* thread)
- : ScopedThreadJoinActivity(::tracked_objects::GetProgramCounter(),
- thread) {}
-
- private:
- ScopedThreadJoinActivity(const void* program_counter,
- const base::PlatformThreadHandle* thread);
- DISALLOW_COPY_AND_ASSIGN(ScopedThreadJoinActivity);
-};
-
-// Some systems don't have base::Process
-#if !defined(OS_NACL) && !defined(OS_IOS)
-class BASE_EXPORT ScopedProcessWaitActivity
- : public GlobalActivityTracker::ScopedThreadActivity {
- public:
- ALWAYS_INLINE
- explicit ScopedProcessWaitActivity(const base::Process* process)
- : ScopedProcessWaitActivity(::tracked_objects::GetProgramCounter(),
- process) {}
-
- private:
- ScopedProcessWaitActivity(const void* program_counter,
- const base::Process* process);
- DISALLOW_COPY_AND_ASSIGN(ScopedProcessWaitActivity);
-};
-#endif
-
-} // namespace debug
-} // namespace base
-
-#endif // BASE_DEBUG_ACTIVITY_TRACKER_H_
diff --git a/base/debug/activity_tracker_unittest.cc b/base/debug/activity_tracker_unittest.cc
deleted file mode 100644
index aced4fb36a..0000000000
--- a/base/debug/activity_tracker_unittest.cc
+++ /dev/null
@@ -1,340 +0,0 @@
-// Copyright 2016 The Chromium 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/debug/activity_tracker.h"
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/files/file.h"
-#include "base/files/file_util.h"
-#include "base/files/memory_mapped_file.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/memory/ptr_util.h"
-#include "base/pending_task.h"
-#include "base/rand_util.h"
-#include "base/synchronization/condition_variable.h"
-#include "base/synchronization/lock.h"
-#include "base/synchronization/spin_wait.h"
-#include "base/threading/platform_thread.h"
-#include "base/threading/simple_thread.h"
-#include "base/time/time.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace base {
-namespace debug {
-
-namespace {
-
-class TestActivityTracker : public ThreadActivityTracker {
- public:
- TestActivityTracker(std::unique_ptr<char[]> memory, size_t mem_size)
- : ThreadActivityTracker(memset(memory.get(), 0, mem_size), mem_size),
- mem_segment_(std::move(memory)) {}
-
- ~TestActivityTracker() override {}
-
- private:
- std::unique_ptr<char[]> mem_segment_;
-};
-
-} // namespace
-
-
-class ActivityTrackerTest : public testing::Test {
- public:
- const int kMemorySize = 1 << 20; // 1MiB
- const int kStackSize = 1 << 10; // 1KiB
-
- using ActivityId = ThreadActivityTracker::ActivityId;
-
- ActivityTrackerTest() {}
-
- ~ActivityTrackerTest() override {
- GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get();
- if (global_tracker) {
- global_tracker->ReleaseTrackerForCurrentThreadForTesting();
- delete global_tracker;
- }
- }
-
- std::unique_ptr<ThreadActivityTracker> CreateActivityTracker() {
- std::unique_ptr<char[]> memory(new char[kStackSize]);
- return MakeUnique<TestActivityTracker>(std::move(memory), kStackSize);
- }
-
- size_t GetGlobalActiveTrackerCount() {
- GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get();
- if (!global_tracker)
- return 0;
- return global_tracker->thread_tracker_count_.load(
- std::memory_order_relaxed);
- }
-
- size_t GetGlobalInactiveTrackerCount() {
- GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get();
- if (!global_tracker)
- return 0;
- base::AutoLock autolock(global_tracker->thread_tracker_allocator_lock_);
- return global_tracker->thread_tracker_allocator_.cache_used();
- }
-
- size_t GetGlobalUserDataMemoryCacheUsed() {
- return GlobalActivityTracker::Get()->user_data_allocator_.cache_used();
- }
-
- static void DoNothing() {}
-};
-
-TEST_F(ActivityTrackerTest, UserDataTest) {
- char buffer[256];
- memset(buffer, 0, sizeof(buffer));
- ActivityUserData data(buffer, sizeof(buffer));
- const size_t space = sizeof(buffer) - 8;
- ASSERT_EQ(space, data.available_);
-
- data.SetInt("foo", 1);
- ASSERT_EQ(space - 24, data.available_);
-
- data.SetUint("b", 1U); // Small names fit beside header in a word.
- ASSERT_EQ(space - 24 - 16, data.available_);
-
- data.Set("c", buffer, 10);
- ASSERT_EQ(space - 24 - 16 - 24, data.available_);
-
- data.SetString("dear john", "it's been fun");
- ASSERT_EQ(space - 24 - 16 - 24 - 32, data.available_);
-
- data.Set("c", buffer, 20);
- ASSERT_EQ(space - 24 - 16 - 24 - 32, data.available_);
-
- data.SetString("dear john", "but we're done together");
- ASSERT_EQ(space - 24 - 16 - 24 - 32, data.available_);
-
- data.SetString("dear john", "bye");
- ASSERT_EQ(space - 24 - 16 - 24 - 32, data.available_);
-
- data.SetChar("d", 'x');
- ASSERT_EQ(space - 24 - 16 - 24 - 32 - 8, data.available_);
-
- data.SetBool("ee", true);
- ASSERT_EQ(space - 24 - 16 - 24 - 32 - 8 - 16, data.available_);
-
- data.SetString("f", "");
- ASSERT_EQ(space - 24 - 16 - 24 - 32 - 8 - 16 - 8, data.available_);
-}
-
-TEST_F(ActivityTrackerTest, PushPopTest) {
- std::unique_ptr<ThreadActivityTracker> tracker = CreateActivityTracker();
- ThreadActivityTracker::Snapshot snapshot;
-
- ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
- ASSERT_EQ(0U, snapshot.activity_stack_depth);
- ASSERT_EQ(0U, snapshot.activity_stack.size());
-
- char origin1;
- ActivityId id1 = tracker->PushActivity(&origin1, Activity::ACT_TASK,
- ActivityData::ForTask(11));
- ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
- ASSERT_EQ(1U, snapshot.activity_stack_depth);
- ASSERT_EQ(1U, snapshot.activity_stack.size());
- EXPECT_NE(0, snapshot.activity_stack[0].time_internal);
- EXPECT_EQ(Activity::ACT_TASK, snapshot.activity_stack[0].activity_type);
- EXPECT_EQ(reinterpret_cast<uintptr_t>(&origin1),
- snapshot.activity_stack[0].origin_address);
- EXPECT_EQ(11U, snapshot.activity_stack[0].data.task.sequence_id);
-
- char origin2;
- char lock2;
- ActivityId id2 = tracker->PushActivity(&origin2, Activity::ACT_LOCK,
- ActivityData::ForLock(&lock2));
- ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
- ASSERT_EQ(2U, snapshot.activity_stack_depth);
- ASSERT_EQ(2U, snapshot.activity_stack.size());
- EXPECT_LE(snapshot.activity_stack[0].time_internal,
- snapshot.activity_stack[1].time_internal);
- EXPECT_EQ(Activity::ACT_LOCK, snapshot.activity_stack[1].activity_type);
- EXPECT_EQ(reinterpret_cast<uintptr_t>(&origin2),
- snapshot.activity_stack[1].origin_address);
- EXPECT_EQ(reinterpret_cast<uintptr_t>(&lock2),
- snapshot.activity_stack[1].data.lock.lock_address);
-
- tracker->PopActivity(id2);
- ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
- ASSERT_EQ(1U, snapshot.activity_stack_depth);
- ASSERT_EQ(1U, snapshot.activity_stack.size());
- EXPECT_EQ(Activity::ACT_TASK, snapshot.activity_stack[0].activity_type);
- EXPECT_EQ(reinterpret_cast<uintptr_t>(&origin1),
- snapshot.activity_stack[0].origin_address);
- EXPECT_EQ(11U, snapshot.activity_stack[0].data.task.sequence_id);
-
- tracker->PopActivity(id1);
- ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
- ASSERT_EQ(0U, snapshot.activity_stack_depth);
- ASSERT_EQ(0U, snapshot.activity_stack.size());
-}
-
-TEST_F(ActivityTrackerTest, ScopedTaskTest) {
- GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3);
-
- ThreadActivityTracker* tracker =
- GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread();
- ThreadActivityTracker::Snapshot snapshot;
- ASSERT_EQ(0U, GetGlobalUserDataMemoryCacheUsed());
-
- ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
- ASSERT_EQ(0U, snapshot.activity_stack_depth);
- ASSERT_EQ(0U, snapshot.activity_stack.size());
-
- {
- PendingTask task1(FROM_HERE, base::Bind(&DoNothing));
- ScopedTaskRunActivity activity1(task1);
- ActivityUserData& user_data1 = activity1.user_data();
- (void)user_data1; // Tell compiler it's been used.
-
- ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
- ASSERT_EQ(1U, snapshot.activity_stack_depth);
- ASSERT_EQ(1U, snapshot.activity_stack.size());
- EXPECT_EQ(Activity::ACT_TASK, snapshot.activity_stack[0].activity_type);
-
- {
- PendingTask task2(FROM_HERE, base::Bind(&DoNothing));
- ScopedTaskRunActivity activity2(task2);
- ActivityUserData& user_data2 = activity2.user_data();
- (void)user_data2; // Tell compiler it's been used.
-
- ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
- ASSERT_EQ(2U, snapshot.activity_stack_depth);
- ASSERT_EQ(2U, snapshot.activity_stack.size());
- EXPECT_EQ(Activity::ACT_TASK, snapshot.activity_stack[1].activity_type);
- }
-
- ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
- ASSERT_EQ(1U, snapshot.activity_stack_depth);
- ASSERT_EQ(1U, snapshot.activity_stack.size());
- EXPECT_EQ(Activity::ACT_TASK, snapshot.activity_stack[0].activity_type);
- }
-
- ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
- ASSERT_EQ(0U, snapshot.activity_stack_depth);
- ASSERT_EQ(0U, snapshot.activity_stack.size());
- ASSERT_EQ(2U, GetGlobalUserDataMemoryCacheUsed());
-}
-
-TEST_F(ActivityTrackerTest, CreateWithFileTest) {
- const char temp_name[] = "CreateWithFileTest";
- ScopedTempDir temp_dir;
- ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath temp_file = temp_dir.GetPath().AppendASCII(temp_name);
- const size_t temp_size = 64 << 10; // 64 KiB
-
- // Create a global tracker on a new file.
- ASSERT_FALSE(PathExists(temp_file));
- GlobalActivityTracker::CreateWithFile(temp_file, temp_size, 0, "foo", 3);
- GlobalActivityTracker* global = GlobalActivityTracker::Get();
- EXPECT_EQ(std::string("foo"), global->allocator()->Name());
- global->ReleaseTrackerForCurrentThreadForTesting();
- delete global;
-
- // Create a global tracker over an existing file, replacing it. If the
- // replacement doesn't work, the name will remain as it was first created.
- ASSERT_TRUE(PathExists(temp_file));
- GlobalActivityTracker::CreateWithFile(temp_file, temp_size, 0, "bar", 3);
- global = GlobalActivityTracker::Get();
- EXPECT_EQ(std::string("bar"), global->allocator()->Name());
- global->ReleaseTrackerForCurrentThreadForTesting();
- delete global;
-}
-
-
-// GlobalActivityTracker tests below.
-
-class SimpleActivityThread : public SimpleThread {
- public:
- SimpleActivityThread(const std::string& name,
- const void* origin,
- Activity::Type activity,
- const ActivityData& data)
- : SimpleThread(name, Options()),
- origin_(origin),
- activity_(activity),
- data_(data),
- exit_condition_(&lock_) {}
-
- ~SimpleActivityThread() override {}
-
- void Run() override {
- ThreadActivityTracker::ActivityId id =
- GlobalActivityTracker::Get()
- ->GetOrCreateTrackerForCurrentThread()
- ->PushActivity(origin_, activity_, data_);
-
- {
- AutoLock auto_lock(lock_);
- ready_ = true;
- while (!exit_)
- exit_condition_.Wait();
- }
-
- GlobalActivityTracker::Get()->GetTrackerForCurrentThread()->PopActivity(id);
- }
-
- void Exit() {
- AutoLock auto_lock(lock_);
- exit_ = true;
- exit_condition_.Signal();
- }
-
- void WaitReady() {
- SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(ready_);
- }
-
- private:
- const void* origin_;
- Activity::Type activity_;
- ActivityData data_;
-
- bool ready_ = false;
- bool exit_ = false;
- Lock lock_;
- ConditionVariable exit_condition_;
-
- DISALLOW_COPY_AND_ASSIGN(SimpleActivityThread);
-};
-
-TEST_F(ActivityTrackerTest, ThreadDeathTest) {
- GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3);
- GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread();
- const size_t starting_active = GetGlobalActiveTrackerCount();
- const size_t starting_inactive = GetGlobalInactiveTrackerCount();
-
- SimpleActivityThread t1("t1", nullptr, Activity::ACT_TASK,
- ActivityData::ForTask(11));
- t1.Start();
- t1.WaitReady();
- EXPECT_EQ(starting_active + 1, GetGlobalActiveTrackerCount());
- EXPECT_EQ(starting_inactive, GetGlobalInactiveTrackerCount());
-
- t1.Exit();
- t1.Join();
- EXPECT_EQ(starting_active, GetGlobalActiveTrackerCount());
- EXPECT_EQ(starting_inactive + 1, GetGlobalInactiveTrackerCount());
-
- // Start another thread and ensure it re-uses the existing memory.
-
- SimpleActivityThread t2("t2", nullptr, Activity::ACT_TASK,
- ActivityData::ForTask(22));
- t2.Start();
- t2.WaitReady();
- EXPECT_EQ(starting_active + 1, GetGlobalActiveTrackerCount());
- EXPECT_EQ(starting_inactive, GetGlobalInactiveTrackerCount());
-
- t2.Exit();
- t2.Join();
- EXPECT_EQ(starting_active, GetGlobalActiveTrackerCount());
- EXPECT_EQ(starting_inactive + 1, GetGlobalInactiveTrackerCount());
-}
-
-} // namespace debug
-} // namespace base
diff --git a/base/debug/alias.cc b/base/debug/alias.cc
index d49808491b..ff3557450f 100644
--- a/base/debug/alias.cc
+++ b/base/debug/alias.cc
@@ -12,8 +12,7 @@ namespace debug {
#pragma optimize("", off)
#endif
-void Alias(const void* /* var */) {
-}
+void Alias(const void*) {}
#if defined(COMPILER_MSVC)
#pragma optimize("", on)
diff --git a/base/debug/debugger_posix.cc b/base/debug/debugger_posix.cc
index ebe9d611f7..a157d9ad3f 100644
--- a/base/debug/debugger_posix.cc
+++ b/base/debug/debugger_posix.cc
@@ -18,8 +18,6 @@
#include <vector>
#include "base/macros.h"
-#include "base/threading/platform_thread.h"
-#include "base/time/time.h"
#include "build/build_config.h"
#if defined(__GLIBCXX__)
diff --git a/base/debug/debugging_flags.h b/base/debug/debugging_flags.h
index e6ae1ee192..1ea435fd71 100644
--- a/base/debug/debugging_flags.h
+++ b/base/debug/debugging_flags.h
@@ -1,8 +1,11 @@
// Generated by build/write_buildflag_header.py
// From "base_debugging_flags"
+
#ifndef BASE_DEBUG_DEBUGGING_FLAGS_H_
#define BASE_DEBUG_DEBUGGING_FLAGS_H_
+
#include "build/buildflag.h"
+
#define BUILDFLAG_INTERNAL_ENABLE_PROFILING() (0)
-#define BUILDFLAG_INTERNAL_ENABLE_MEMORY_TASK_PROFILER() (0)
+
#endif // BASE_DEBUG_DEBUGGING_FLAGS_H_
diff --git a/base/debug/dump_without_crashing.cc b/base/debug/dump_without_crashing.cc
deleted file mode 100644
index 4b338ca293..0000000000
--- a/base/debug/dump_without_crashing.cc
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/debug/dump_without_crashing.h"
-
-#include "base/logging.h"
-
-namespace {
-
-// Pointer to the function that's called by DumpWithoutCrashing() to dump the
-// process's memory.
-void (CDECL *dump_without_crashing_function_)() = NULL;
-
-} // namespace
-
-namespace base {
-
-namespace debug {
-
-bool DumpWithoutCrashing() {
- if (dump_without_crashing_function_) {
- (*dump_without_crashing_function_)();
- return true;
- }
- return false;
-}
-
-void SetDumpWithoutCrashingFunction(void (CDECL *function)()) {
- dump_without_crashing_function_ = function;
-}
-
-} // namespace debug
-
-} // namespace base
diff --git a/base/debug/dump_without_crashing.h b/base/debug/dump_without_crashing.h
deleted file mode 100644
index a5c85d5ebe..0000000000
--- a/base/debug/dump_without_crashing.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_DEBUG_DUMP_WITHOUT_CRASHING_H_
-#define BASE_DEBUG_DUMP_WITHOUT_CRASHING_H_
-
-#include "base/base_export.h"
-#include "base/compiler_specific.h"
-#include "build/build_config.h"
-
-namespace base {
-
-namespace debug {
-
-// Handler to silently dump the current process without crashing.
-// Before calling this function, call SetDumpWithoutCrashingFunction to pass a
-// function pointer, typically chrome!DumpProcessWithoutCrash. See example code
-// in chrome_main.cc that does this for chrome.dll.
-// Returns false if called before SetDumpWithoutCrashingFunction.
-BASE_EXPORT bool DumpWithoutCrashing();
-
-// Sets a function that'll be invoked to dump the current process when
-// DumpWithoutCrashing() is called.
-BASE_EXPORT void SetDumpWithoutCrashingFunction(void (CDECL *function)());
-
-} // namespace debug
-
-} // namespace base
-
-#endif // BASE_DEBUG_DUMP_WITHOUT_CRASHING_H_
diff --git a/base/debug/leak_tracker_unittest.cc b/base/debug/leak_tracker_unittest.cc
index b9ecdcf3c9..8b4c5681e0 100644
--- a/base/debug/leak_tracker_unittest.cc
+++ b/base/debug/leak_tracker_unittest.cc
@@ -30,7 +30,7 @@ TEST(LeakTrackerTest, NotEnabled) {
EXPECT_EQ(-1, LeakTracker<ClassA>::NumLiveInstances());
EXPECT_EQ(-1, LeakTracker<ClassB>::NumLiveInstances());
- // Use unique_ptr so compiler doesn't complain about unused variables.
+ // Use scoped_ptr so compiler doesn't complain about unused variables.
std::unique_ptr<ClassA> a1(new ClassA);
std::unique_ptr<ClassB> b1(new ClassB);
std::unique_ptr<ClassB> b2(new ClassB);
diff --git a/base/debug/profiler.cc b/base/debug/profiler.cc
deleted file mode 100644
index e303c2891a..0000000000
--- a/base/debug/profiler.cc
+++ /dev/null
@@ -1,225 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/debug/profiler.h"
-
-#include <string>
-
-#include "base/debug/debugging_flags.h"
-#include "base/process/process_handle.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "build/build_config.h"
-
-#if defined(OS_WIN)
-#include "base/win/current_module.h"
-#include "base/win/pe_image.h"
-#endif // defined(OS_WIN)
-
-// TODO(peria): Enable profiling on Windows.
-#if BUILDFLAG(ENABLE_PROFILING) && !defined(NO_TCMALLOC) && !defined(OS_WIN)
-#include "third_party/tcmalloc/chromium/src/gperftools/profiler.h"
-#endif
-
-namespace base {
-namespace debug {
-
-// TODO(peria): Enable profiling on Windows.
-#if BUILDFLAG(ENABLE_PROFILING) && !defined(NO_TCMALLOC) && !defined(OS_WIN)
-
-static int profile_count = 0;
-
-void StartProfiling(const std::string& name) {
- ++profile_count;
- std::string full_name(name);
- std::string pid = IntToString(GetCurrentProcId());
- std::string count = IntToString(profile_count);
- ReplaceSubstringsAfterOffset(&full_name, 0, "{pid}", pid);
- ReplaceSubstringsAfterOffset(&full_name, 0, "{count}", count);
- ProfilerStart(full_name.c_str());
-}
-
-void StopProfiling() {
- ProfilerFlush();
- ProfilerStop();
-}
-
-void FlushProfiling() {
- ProfilerFlush();
-}
-
-bool BeingProfiled() {
- return ProfilingIsEnabledForAllThreads();
-}
-
-void RestartProfilingAfterFork() {
- ProfilerRegisterThread();
-}
-
-bool IsProfilingSupported() {
- return true;
-}
-
-#else
-
-void StartProfiling(const std::string&) {
-}
-
-void StopProfiling() {
-}
-
-void FlushProfiling() {
-}
-
-bool BeingProfiled() {
- return false;
-}
-
-void RestartProfilingAfterFork() {
-}
-
-bool IsProfilingSupported() {
- return false;
-}
-
-#endif
-
-#if !defined(OS_WIN)
-
-bool IsBinaryInstrumented() {
- return false;
-}
-
-ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() {
- return NULL;
-}
-
-DynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc() {
- return NULL;
-}
-
-AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() {
- return NULL;
-}
-
-MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() {
- return NULL;
-}
-
-#else // defined(OS_WIN)
-
-bool IsBinaryInstrumented() {
- enum InstrumentationCheckState {
- UNINITIALIZED,
- INSTRUMENTED_IMAGE,
- NON_INSTRUMENTED_IMAGE,
- };
-
- static InstrumentationCheckState state = UNINITIALIZED;
-
- if (state == UNINITIALIZED) {
- base::win::PEImage image(CURRENT_MODULE());
-
- // Check to be sure our image is structured as we'd expect.
- DCHECK(image.VerifyMagic());
-
- // Syzygy-instrumented binaries contain a PE image section named ".thunks",
- // and all Syzygy-modified binaries contain the ".syzygy" image section.
- // This is a very fast check, as it only looks at the image header.
- if ((image.GetImageSectionHeaderByName(".thunks") != NULL) &&
- (image.GetImageSectionHeaderByName(".syzygy") != NULL)) {
- state = INSTRUMENTED_IMAGE;
- } else {
- state = NON_INSTRUMENTED_IMAGE;
- }
- }
- DCHECK(state != UNINITIALIZED);
-
- return state == INSTRUMENTED_IMAGE;
-}
-
-namespace {
-
-struct FunctionSearchContext {
- const char* name;
- FARPROC function;
-};
-
-// Callback function to PEImage::EnumImportChunks.
-bool FindResolutionFunctionInImports(
- const base::win::PEImage &image, const char* module_name,
- PIMAGE_THUNK_DATA unused_name_table, PIMAGE_THUNK_DATA import_address_table,
- PVOID cookie) {
- FunctionSearchContext* context =
- reinterpret_cast<FunctionSearchContext*>(cookie);
-
- DCHECK(context);
- DCHECK(!context->function);
-
- // Our import address table contains pointers to the functions we import
- // at this point. Let's retrieve the first such function and use it to
- // find the module this import was resolved to by the loader.
- const wchar_t* function_in_module =
- reinterpret_cast<const wchar_t*>(import_address_table->u1.Function);
-
- // Retrieve the module by a function in the module.
- const DWORD kFlags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
- GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT;
- HMODULE module = NULL;
- if (!::GetModuleHandleEx(kFlags, function_in_module, &module)) {
- // This can happen if someone IAT patches us to a thunk.
- return true;
- }
-
- // See whether this module exports the function we're looking for.
- FARPROC exported_func = ::GetProcAddress(module, context->name);
- if (exported_func != NULL) {
- // We found it, return the function and terminate the enumeration.
- context->function = exported_func;
- return false;
- }
-
- // Keep going.
- return true;
-}
-
-template <typename FunctionType>
-FunctionType FindFunctionInImports(const char* function_name) {
- if (!IsBinaryInstrumented())
- return NULL;
-
- base::win::PEImage image(CURRENT_MODULE());
-
- FunctionSearchContext ctx = { function_name, NULL };
- image.EnumImportChunks(FindResolutionFunctionInImports, &ctx);
-
- return reinterpret_cast<FunctionType>(ctx.function);
-}
-
-} // namespace
-
-ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() {
- return FindFunctionInImports<ReturnAddressLocationResolver>(
- "ResolveReturnAddressLocation");
-}
-
-DynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc() {
- return FindFunctionInImports<DynamicFunctionEntryHook>(
- "OnDynamicFunctionEntry");
-}
-
-AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() {
- return FindFunctionInImports<AddDynamicSymbol>(
- "AddDynamicSymbol");
-}
-
-MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() {
- return FindFunctionInImports<MoveDynamicSymbol>(
- "MoveDynamicSymbol");
-}
-
-#endif // defined(OS_WIN)
-
-} // namespace debug
-} // namespace base
diff --git a/base/debug/profiler.h b/base/debug/profiler.h
deleted file mode 100644
index ea81b13c6a..0000000000
--- a/base/debug/profiler.h
+++ /dev/null
@@ -1,94 +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_DEBUG_PROFILER_H_
-#define BASE_DEBUG_PROFILER_H_
-
-#include <stddef.h>
-
-#include <string>
-
-#include "base/base_export.h"
-
-// The Profiler functions allow usage of the underlying sampling based
-// profiler. If the application has not been built with the necessary
-// flags (-DENABLE_PROFILING and not -DNO_TCMALLOC) then these functions
-// are noops.
-namespace base {
-namespace debug {
-
-// Start profiling with the supplied name.
-// {pid} will be replaced by the process' pid and {count} will be replaced
-// by the count of the profile run (starts at 1 with each process).
-BASE_EXPORT void StartProfiling(const std::string& name);
-
-// Stop profiling and write out data.
-BASE_EXPORT void StopProfiling();
-
-// Force data to be written to file.
-BASE_EXPORT void FlushProfiling();
-
-// Returns true if process is being profiled.
-BASE_EXPORT bool BeingProfiled();
-
-// Reset profiling after a fork, which disables timers.
-BASE_EXPORT void RestartProfilingAfterFork();
-
-// Returns true iff this executable is instrumented with the Syzygy profiler.
-BASE_EXPORT bool IsBinaryInstrumented();
-
-// Returns true iff this executable supports profiling.
-BASE_EXPORT bool IsProfilingSupported();
-
-// There's a class of profilers that use "return address swizzling" to get a
-// hook on function exits. This class of profilers uses some form of entry hook,
-// like e.g. binary instrumentation, or a compiler flag, that calls a hook each
-// time a function is invoked. The hook then switches the return address on the
-// stack for the address of an exit hook function, and pushes the original
-// return address to a shadow stack of some type. When in due course the CPU
-// executes a return to the exit hook, the exit hook will do whatever work it
-// does on function exit, then arrange to return to the original return address.
-// This class of profiler does not play well with programs that look at the
-// return address, as does e.g. V8. V8 uses the return address to certain
-// runtime functions to find the JIT code that called it, and from there finds
-// the V8 data structures associated to the JS function involved.
-// A return address resolution function is used to fix this. It allows such
-// programs to resolve a location on stack where a return address originally
-// resided, to the shadow stack location where the profiler stashed it.
-typedef uintptr_t (*ReturnAddressLocationResolver)(
- uintptr_t return_addr_location);
-
-// This type declaration must match V8's FunctionEntryHook.
-typedef void (*DynamicFunctionEntryHook)(uintptr_t function,
- uintptr_t return_addr_location);
-
-// The functions below here are to support profiling V8-generated code.
-// V8 has provisions for generating a call to an entry hook for newly generated
-// JIT code, and it can push symbol information on code generation and advise
-// when the garbage collector moves code. The functions declarations below here
-// make glue between V8's facilities and a profiler.
-
-// This type declaration must match V8's FunctionEntryHook.
-typedef void (*DynamicFunctionEntryHook)(uintptr_t function,
- uintptr_t return_addr_location);
-
-typedef void (*AddDynamicSymbol)(const void* address,
- size_t length,
- const char* name,
- size_t name_len);
-typedef void (*MoveDynamicSymbol)(const void* address, const void* new_address);
-
-
-// If this binary is instrumented and the instrumentation supplies a function
-// for each of those purposes, find and return the function in question.
-// Otherwise returns NULL.
-BASE_EXPORT ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc();
-BASE_EXPORT DynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc();
-BASE_EXPORT AddDynamicSymbol GetProfilerAddDynamicSymbolFunc();
-BASE_EXPORT MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc();
-
-} // namespace debug
-} // namespace base
-
-#endif // BASE_DEBUG_PROFILER_H_
diff --git a/base/debug/stack_trace.cc b/base/debug/stack_trace.cc
index af4a6efc3e..ac0ead76be 100644
--- a/base/debug/stack_trace.cc
+++ b/base/debug/stack_trace.cc
@@ -9,146 +9,47 @@
#include <algorithm>
#include <sstream>
-#include "base/logging.h"
#include "base/macros.h"
-#if HAVE_TRACE_STACK_FRAME_POINTERS
-
-#if defined(OS_LINUX) || defined(OS_ANDROID)
+#if HAVE_TRACE_STACK_FRAME_POINTERS && defined(OS_ANDROID)
#include <pthread.h>
#include "base/process/process_handle.h"
#include "base/threading/platform_thread.h"
#endif
-#if defined(OS_MACOSX)
-#include <pthread.h>
-#endif
-
-#if defined(OS_LINUX) && defined(__GLIBC__)
-extern "C" void* __libc_stack_end;
-#endif
-
-#endif // HAVE_TRACE_STACK_FRAME_POINTERS
-
namespace base {
namespace debug {
-namespace {
-
-#if HAVE_TRACE_STACK_FRAME_POINTERS
-
-#if defined(__arm__) && defined(__GNUC__) && !defined(__clang__)
-// GCC and LLVM generate slightly different frames on ARM, see
-// https://llvm.org/bugs/show_bug.cgi?id=18505 - LLVM generates
-// x86-compatible frame, while GCC needs adjustment.
-constexpr size_t kStackFrameAdjustment = sizeof(uintptr_t);
-#else
-constexpr size_t kStackFrameAdjustment = 0;
-#endif
-
-uintptr_t GetNextStackFrame(uintptr_t fp) {
- return reinterpret_cast<const uintptr_t*>(fp)[0] - kStackFrameAdjustment;
+StackTrace::StackTrace(const void* const* trace, size_t count) {
+ count = std::min(count, arraysize(trace_));
+ if (count)
+ memcpy(trace_, trace, count * sizeof(trace_[0]));
+ count_ = count;
}
-uintptr_t GetStackFramePC(uintptr_t fp) {
- return reinterpret_cast<const uintptr_t*>(fp)[1];
+StackTrace::~StackTrace() {
}
-bool IsStackFrameValid(uintptr_t fp, uintptr_t prev_fp, uintptr_t stack_end) {
- // With the stack growing downwards, older stack frame must be
- // at a greater address that the current one.
- if (fp <= prev_fp) return false;
-
- // Assume huge stack frames are bogus.
- if (fp - prev_fp > 100000) return false;
-
- // Check alignment.
- if (fp & (sizeof(uintptr_t) - 1)) return false;
-
- if (stack_end) {
- // Both fp[0] and fp[1] must be within the stack.
- if (fp > stack_end - 2 * sizeof(uintptr_t)) return false;
-
- // Additional check to filter out false positives.
- if (GetStackFramePC(fp) < 32768) return false;
- }
-
- return true;
-};
-
-// ScanStackForNextFrame() scans the stack for a valid frame to allow unwinding
-// past system libraries. Only supported on Linux where system libraries are
-// usually in the middle of the trace:
-//
-// TraceStackFramePointers
-// <more frames from Chrome>
-// base::WorkSourceDispatch <-- unwinding stops (next frame is invalid),
-// g_main_context_dispatch ScanStackForNextFrame() is called
-// <more frames from glib>
-// g_main_context_iteration
-// base::MessagePumpGlib::Run <-- ScanStackForNextFrame() finds valid frame,
-// base::RunLoop::Run unwinding resumes
-// <more frames from Chrome>
-// __libc_start_main
-//
-// For stack scanning to be efficient it's very important for the thread to
-// be started by Chrome. In that case we naturally terminate unwinding once
-// we reach the origin of the stack (i.e. GetStackEnd()). If the thread is
-// not started by Chrome (e.g. Android's main thread), then we end up always
-// scanning area at the origin of the stack, wasting time and not finding any
-// frames (since Android libraries don't have frame pointers).
-//
-// ScanStackForNextFrame() returns 0 if it couldn't find a valid frame
-// (or if stack scanning is not supported on the current platform).
-uintptr_t ScanStackForNextFrame(uintptr_t fp, uintptr_t stack_end) {
-#if defined(OS_LINUX)
- // Enough to resume almost all prematurely terminated traces.
- constexpr size_t kMaxStackScanArea = 8192;
-
- if (!stack_end) {
- // Too dangerous to scan without knowing where the stack ends.
- return 0;
- }
-
- fp += sizeof(uintptr_t); // current frame is known to be invalid
- uintptr_t last_fp_to_scan = std::min(fp + kMaxStackScanArea, stack_end) -
- sizeof(uintptr_t);
- for (;fp <= last_fp_to_scan; fp += sizeof(uintptr_t)) {
- uintptr_t next_fp = GetNextStackFrame(fp);
- if (IsStackFrameValid(next_fp, fp, stack_end)) {
- // Check two frames deep. Since stack frame is just a pointer to
- // a higher address on the stack, it's relatively easy to find
- // something that looks like one. However two linked frames are
- // far less likely to be bogus.
- uintptr_t next2_fp = GetNextStackFrame(next_fp);
- if (IsStackFrameValid(next2_fp, next_fp, stack_end)) {
- return fp;
- }
- }
- }
-#endif // defined(OS_LINUX)
-
- return 0;
+const void *const *StackTrace::Addresses(size_t* count) const {
+ *count = count_;
+ if (count_)
+ return trace_;
+ return NULL;
}
-// Links stack frame |fp| to |parent_fp|, so that during stack unwinding
-// TraceStackFramePointers() visits |parent_fp| after visiting |fp|.
-// Both frame pointers must come from __builtin_frame_address().
-// Returns previous stack frame |fp| was linked to.
-void* LinkStackFrames(void* fpp, void* parent_fp) {
- uintptr_t fp = reinterpret_cast<uintptr_t>(fpp) - kStackFrameAdjustment;
- void* prev_parent_fp = reinterpret_cast<void**>(fp)[0];
- reinterpret_cast<void**>(fp)[0] = parent_fp;
- return prev_parent_fp;
+std::string StackTrace::ToString() const {
+ std::stringstream stream;
+#if !defined(__UCLIBC__)
+ OutputToStream(&stream);
+#endif
+ return stream.str();
}
-#endif // HAVE_TRACE_STACK_FRAME_POINTERS
-
-} // namespace
-
#if HAVE_TRACE_STACK_FRAME_POINTERS
-uintptr_t GetStackEnd() {
+
#if defined(OS_ANDROID)
+
+static uintptr_t GetStackEnd() {
// Bionic reads proc/maps on every call to pthread_getattr_np() when called
// from the main thread. So we need to cache end of stack in that case to get
// acceptable performance.
@@ -157,6 +58,7 @@ uintptr_t GetStackEnd() {
static uintptr_t main_stack_end = 0;
bool is_main_thread = GetCurrentProcId() == PlatformThread::CurrentId();
+
if (is_main_thread && main_stack_end) {
return main_stack_end;
}
@@ -167,7 +69,9 @@ uintptr_t GetStackEnd() {
int error = pthread_getattr_np(pthread_self(), &attributes);
if (!error) {
error = pthread_attr_getstack(
- &attributes, reinterpret_cast<void**>(&stack_begin), &stack_size);
+ &attributes,
+ reinterpret_cast<void**>(&stack_begin),
+ &stack_size);
pthread_attr_destroy(&attributes);
}
DCHECK(!error);
@@ -176,99 +80,65 @@ uintptr_t GetStackEnd() {
if (is_main_thread) {
main_stack_end = stack_end;
}
- return stack_end; // 0 in case of error
-
-#elif defined(OS_LINUX) && defined(__GLIBC__)
-
- if (GetCurrentProcId() == PlatformThread::CurrentId()) {
- // For the main thread we have a shortcut.
- return reinterpret_cast<uintptr_t>(__libc_stack_end);
- }
-
-// No easy way to get end of the stack for non-main threads,
-// see crbug.com/617730.
-#elif defined(OS_MACOSX)
- return reinterpret_cast<uintptr_t>(pthread_get_stackaddr_np(pthread_self()));
-#endif
-
- // Don't know how to get end of the stack.
- return 0;
+ return stack_end;
}
-#endif // HAVE_TRACE_STACK_FRAME_POINTERS
-StackTrace::StackTrace() : StackTrace(arraysize(trace_)) {}
-
-StackTrace::StackTrace(const void* const* trace, size_t count) {
- count = std::min(count, arraysize(trace_));
- if (count)
- memcpy(trace_, trace, count * sizeof(trace_[0]));
- count_ = count;
-}
-
-const void *const *StackTrace::Addresses(size_t* count) const {
- *count = count_;
- if (count_)
- return trace_;
- return NULL;
-}
-
-std::string StackTrace::ToString() const {
- std::stringstream stream;
-#if !defined(__UCLIBC__)
- OutputToStream(&stream);
-#endif
- return stream.str();
-}
-
-#if HAVE_TRACE_STACK_FRAME_POINTERS
+#endif // defined(OS_ANDROID)
size_t TraceStackFramePointers(const void** out_trace,
size_t max_depth,
size_t skip_initial) {
// Usage of __builtin_frame_address() enables frame pointers in this
- // function even if they are not enabled globally. So 'fp' will always
+ // function even if they are not enabled globally. So 'sp' will always
// be valid.
- uintptr_t fp = reinterpret_cast<uintptr_t>(__builtin_frame_address(0)) -
- kStackFrameAdjustment;
+ uintptr_t sp = reinterpret_cast<uintptr_t>(__builtin_frame_address(0));
+#if defined(OS_ANDROID)
uintptr_t stack_end = GetStackEnd();
+#endif
size_t depth = 0;
while (depth < max_depth) {
+#if defined(__arm__) && defined(__GNUC__) && !defined(__clang__)
+ // GCC and LLVM generate slightly different frames on ARM, see
+ // https://llvm.org/bugs/show_bug.cgi?id=18505 - LLVM generates
+ // x86-compatible frame, while GCC needs adjustment.
+ sp -= sizeof(uintptr_t);
+#endif
+
+#if defined(OS_ANDROID)
+ // Both sp[0] and s[1] must be valid.
+ if (sp + 2 * sizeof(uintptr_t) > stack_end) {
+ break;
+ }
+#endif
+
if (skip_initial != 0) {
skip_initial--;
} else {
- out_trace[depth++] = reinterpret_cast<const void*>(GetStackFramePC(fp));
+ out_trace[depth++] = reinterpret_cast<const void**>(sp)[1];
}
- uintptr_t next_fp = GetNextStackFrame(fp);
- if (IsStackFrameValid(next_fp, fp, stack_end)) {
- fp = next_fp;
- continue;
- }
+ // Find out next frame pointer
+ // (heuristics are from TCMalloc's stacktrace functions)
+ {
+ uintptr_t next_sp = reinterpret_cast<const uintptr_t*>(sp)[0];
- next_fp = ScanStackForNextFrame(fp, stack_end);
- if (next_fp) {
- fp = next_fp;
- continue;
- }
+ // With the stack growing downwards, older stack frame must be
+ // at a greater address that the current one.
+ if (next_sp <= sp) break;
- // Failed to find next frame.
- break;
- }
+ // Assume stack frames larger than 100,000 bytes are bogus.
+ if (next_sp - sp > 100000) break;
- return depth;
-}
+ // Check alignment.
+ if (sp & (sizeof(void*) - 1)) break;
-ScopedStackFrameLinker::ScopedStackFrameLinker(void* fp, void* parent_fp)
- : fp_(fp),
- parent_fp_(parent_fp),
- original_parent_fp_(LinkStackFrames(fp, parent_fp)) {}
+ sp = next_sp;
+ }
+ }
-ScopedStackFrameLinker::~ScopedStackFrameLinker() {
- void* previous_parent_fp = LinkStackFrames(fp_, original_parent_fp_);
- CHECK_EQ(parent_fp_, previous_parent_fp)
- << "Stack frame's parent pointer has changed!";
+ return depth;
}
#endif // HAVE_TRACE_STACK_FRAME_POINTERS
diff --git a/base/debug/stack_trace.h b/base/debug/stack_trace.h
index 4c9b73e87d..23e7b5164b 100644
--- a/base/debug/stack_trace.h
+++ b/base/debug/stack_trace.h
@@ -11,7 +11,6 @@
#include <string>
#include "base/base_export.h"
-#include "base/macros.h"
#include "build/build_config.h"
#if defined(OS_POSIX)
@@ -45,11 +44,6 @@ namespace debug {
// done in official builds because it has security implications).
BASE_EXPORT bool EnableInProcessStackDumping();
-// Returns end of the stack, or 0 if we couldn't get it.
-#if HAVE_TRACE_STACK_FRAME_POINTERS
-BASE_EXPORT uintptr_t GetStackEnd();
-#endif
-
// A stacktrace can be helpful in debugging. For example, you can include a
// stacktrace member in a object (probably around #ifndef NDEBUG) so that you
// can later see where the given object was created from.
@@ -58,13 +52,9 @@ class BASE_EXPORT StackTrace {
// Creates a stacktrace from the current location.
StackTrace();
- // Creates a stacktrace from the current location, of up to |count| entries.
- // |count| will be limited to at most |kMaxTraces|.
- explicit StackTrace(size_t count);
-
// Creates a stacktrace from an existing array of instruction
// pointers (such as returned by Addresses()). |count| will be
- // limited to at most |kMaxTraces|.
+ // trimmed to |kMaxTraces|.
StackTrace(const void* const* trace, size_t count);
#if defined(OS_WIN)
@@ -77,6 +67,8 @@ class BASE_EXPORT StackTrace {
// Copying and assignment are allowed with the default functions.
+ ~StackTrace();
+
// Gets an array of instruction pointer values. |*count| will be set to the
// number of elements in the returned array.
const void* const* Addresses(size_t* count) const;
@@ -121,57 +113,6 @@ class BASE_EXPORT StackTrace {
BASE_EXPORT size_t TraceStackFramePointers(const void** out_trace,
size_t max_depth,
size_t skip_initial);
-
-// Links stack frame |fp| to |parent_fp|, so that during stack unwinding
-// TraceStackFramePointers() visits |parent_fp| after visiting |fp|.
-// Both frame pointers must come from __builtin_frame_address().
-// Destructor restores original linkage of |fp| to avoid corrupting caller's
-// frame register on return.
-//
-// This class can be used to repair broken stack frame chain in cases
-// when execution flow goes into code built without frame pointers:
-//
-// void DoWork() {
-// Call_SomeLibrary();
-// }
-// static __thread void* g_saved_fp;
-// void Call_SomeLibrary() {
-// g_saved_fp = __builtin_frame_address(0);
-// some_library_call(...); // indirectly calls SomeLibrary_Callback()
-// }
-// void SomeLibrary_Callback() {
-// ScopedStackFrameLinker linker(__builtin_frame_address(0), g_saved_fp);
-// ...
-// TraceStackFramePointers(...);
-// }
-//
-// This produces the following trace:
-//
-// #0 SomeLibrary_Callback()
-// #1 <address of the code inside SomeLibrary that called #0>
-// #2 DoWork()
-// ...rest of the trace...
-//
-// SomeLibrary doesn't use frame pointers, so when SomeLibrary_Callback()
-// is called, stack frame register contains bogus value that becomes callback'
-// parent frame address. Without ScopedStackFrameLinker unwinding would've
-// stopped at that bogus frame address yielding just two first frames (#0, #1).
-// ScopedStackFrameLinker overwrites callback's parent frame address with
-// Call_SomeLibrary's frame, so unwinder produces full trace without even
-// noticing that stack frame chain was broken.
-class BASE_EXPORT ScopedStackFrameLinker {
- public:
- ScopedStackFrameLinker(void* fp, void* parent_fp);
- ~ScopedStackFrameLinker();
-
- private:
- void* fp_;
- void* parent_fp_;
- void* original_parent_fp_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedStackFrameLinker);
-};
-
#endif // HAVE_TRACE_STACK_FRAME_POINTERS
namespace internal {
diff --git a/base/debug/stack_trace_posix.cc b/base/debug/stack_trace_posix.cc
index 78bc650c79..3c0299cb41 100644
--- a/base/debug/stack_trace_posix.cc
+++ b/base/debug/stack_trace_posix.cc
@@ -16,14 +16,13 @@
#include <sys/types.h>
#include <unistd.h>
-#include <algorithm>
#include <map>
#include <memory>
#include <ostream>
#include <string>
#include <vector>
-#if !defined(USE_SYMBOLIZE)
+#if defined(__GLIBCXX__)
#include <cxxabi.h>
#endif
#if !defined(__UCLIBC__)
@@ -34,11 +33,8 @@
#include <AvailabilityMacros.h>
#endif
-#if defined(OS_LINUX)
-#include "base/debug/proc_maps_linux.h"
-#endif
-
#include "base/debug/debugger.h"
+#include "base/debug/proc_maps_linux.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/free_deleter.h"
@@ -70,18 +66,16 @@ const char kSymbolCharacters[] =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
#endif // !defined(USE_SYMBOLIZE) && defined(__GLIBCXX__)
-#if !defined(USE_SYMBOLIZE) && !defined(__UCLIBC__)
+#if !defined(USE_SYMBOLIZE)
// Demangles C++ symbols in the given text. Example:
//
// "out/Debug/base_unittests(_ZN10StackTraceC1Ev+0x20) [0x817778c]"
// =>
// "out/Debug/base_unittests(StackTrace::StackTrace()+0x20) [0x817778c]"
+#if defined(__GLIBCXX__) && !defined(__UCLIBC__)
void DemangleSymbols(std::string* text) {
// Note: code in this function is NOT async-signal safe (std::string uses
// malloc internally).
- ALLOW_UNUSED_PARAM(text);
-#if defined(__GLIBCXX__) && !defined(__UCLIBC__)
-
std::string::size_type search_from = 0;
while (search_from < text->size()) {
// Look for the start of a mangled symbol, from search_from.
@@ -116,8 +110,11 @@ void DemangleSymbols(std::string* text) {
search_from = mangled_start + 2;
}
}
-#endif // defined(__GLIBCXX__) && !defined(__UCLIBC__)
}
+#elif !defined(__UCLIBC__)
+void DemangleSymbols(std::string* /* text */) {}
+#endif // defined(__GLIBCXX__) && !defined(__UCLIBC__)
+
#endif // !defined(USE_SYMBOLIZE)
class BacktraceOutputHandler {
@@ -128,7 +125,7 @@ class BacktraceOutputHandler {
virtual ~BacktraceOutputHandler() {}
};
-#if !defined(__UCLIBC__)
+#if defined(USE_SYMBOLIZE) || !defined(__UCLIBC__)
void OutputPointer(void* pointer, BacktraceOutputHandler* handler) {
// This should be more than enough to store a 64-bit number in hex:
// 16 hex digits + 1 for null-terminator.
@@ -138,6 +135,7 @@ void OutputPointer(void* pointer, BacktraceOutputHandler* handler) {
buf, sizeof(buf), 16, 12);
handler->HandleOutput(buf);
}
+#endif // defined(USE_SYMBOLIZE) || !defined(__UCLIBC__)
#if defined(USE_SYMBOLIZE)
void OutputFrameId(intptr_t frame_id, BacktraceOutputHandler* handler) {
@@ -151,9 +149,13 @@ void OutputFrameId(intptr_t frame_id, BacktraceOutputHandler* handler) {
}
#endif // defined(USE_SYMBOLIZE)
-void ProcessBacktrace(void *const *trace,
+#if !defined(__UCLIBC__)
+void ProcessBacktrace(void *const * trace,
size_t size,
BacktraceOutputHandler* handler) {
+ (void)trace; // unused based on build context below.
+ (void)size; // unusud based on build context below.
+ (void)handler; // unused based on build context below.
// NOTE: This code MUST be async-signal safe (it's used by in-process
// stack dumping signal handler). NO malloc or stdio is allowed here.
@@ -214,7 +216,7 @@ void PrintToStderr(const char* output) {
}
void StackDumpSignalHandler(int signal, siginfo_t* info, void* void_context) {
- ALLOW_UNUSED_PARAM(void_context); // unused depending on build context
+ (void)void_context; // unused depending on build context
// NOTE: This code MUST be async-signal safe.
// NO malloc or stdio is allowed here.
@@ -384,7 +386,6 @@ void StackDumpSignalHandler(int signal, siginfo_t* info, void* void_context) {
// Non-Mac OSes should probably reraise the signal as well, but the Linux
// sandbox tests break on CrOS devices.
// https://code.google.com/p/chromium/issues/detail?id=551681
- PrintToStderr("Calling _exit(1). Core file will not be generated.\n");
_exit(1);
#endif // defined(OS_MACOSX) && !defined(OS_IOS)
}
@@ -449,6 +450,8 @@ void WarmUpBacktrace() {
StackTrace stack_trace;
}
+} // namespace
+
#if defined(USE_SYMBOLIZE)
// class SandboxSymbolizeHelper.
@@ -464,8 +467,7 @@ class SandboxSymbolizeHelper {
public:
// Returns the singleton instance.
static SandboxSymbolizeHelper* GetInstance() {
- return Singleton<SandboxSymbolizeHelper,
- LeakySingletonTraits<SandboxSymbolizeHelper>>::get();
+ return Singleton<SandboxSymbolizeHelper>::get();
}
private:
@@ -681,8 +683,6 @@ class SandboxSymbolizeHelper {
};
#endif // USE_SYMBOLIZE
-} // namespace
-
bool EnableInProcessStackDumping() {
#if defined(USE_SYMBOLIZE)
SandboxSymbolizeHelper::GetInstance();
@@ -719,18 +719,15 @@ bool EnableInProcessStackDumping() {
return success;
}
-StackTrace::StackTrace(size_t count) {
-// NOTE: This code MUST be async-signal safe (it's used by in-process
-// stack dumping signal handler). NO malloc or stdio is allowed here.
+StackTrace::StackTrace() {
+ // NOTE: This code MUST be async-signal safe (it's used by in-process
+ // stack dumping signal handler). NO malloc or stdio is allowed here.
#if !defined(__UCLIBC__)
- count = std::min(arraysize(trace_), count);
-
// Though the backtrace API man page does not list any possible negative
// return values, we take no chance.
- count_ = base::saturated_cast<size_t>(backtrace(trace_, count));
+ count_ = base::saturated_cast<size_t>(backtrace(trace_, arraysize(trace_)));
#else
- ALLOW_UNUSED_PARAM(count);
count_ = 0;
#endif
}
diff --git a/base/debug/task_annotator.cc b/base/debug/task_annotator.cc
index 46969f28ca..4ba4d91b88 100644
--- a/base/debug/task_annotator.cc
+++ b/base/debug/task_annotator.cc
@@ -4,9 +4,6 @@
#include "base/debug/task_annotator.h"
-#include <array>
-
-#include "base/debug/activity_tracker.h"
#include "base/debug/alias.h"
#include "base/pending_task.h"
#include "base/trace_event/trace_event.h"
@@ -30,37 +27,32 @@ void TaskAnnotator::DidQueueTask(const char* queue_function,
}
void TaskAnnotator::RunTask(const char* queue_function,
- PendingTask* pending_task) {
- ScopedTaskRunActivity task_activity(*pending_task);
-
+ const PendingTask& pending_task) {
tracked_objects::TaskStopwatch stopwatch;
stopwatch.Start();
tracked_objects::Duration queue_duration =
- stopwatch.StartTime() - pending_task->EffectiveTimePosted();
+ stopwatch.StartTime() - pending_task.EffectiveTimePosted();
- TRACE_EVENT_WITH_FLOW1(
- TRACE_DISABLED_BY_DEFAULT("toplevel.flow"), queue_function,
- TRACE_ID_MANGLE(GetTaskTraceID(*pending_task)), TRACE_EVENT_FLAG_FLOW_IN,
- "queue_duration", queue_duration.InMilliseconds());
+ TRACE_EVENT_WITH_FLOW1(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"),
+ queue_function,
+ TRACE_ID_MANGLE(GetTaskTraceID(pending_task)),
+ TRACE_EVENT_FLAG_FLOW_IN,
+ "queue_duration",
+ queue_duration.InMilliseconds());
- // Before running the task, store the task backtrace with the chain of
- // PostTasks that resulted in this call and deliberately alias it to ensure
- // it is on the stack if the task crashes. Be careful not to assume that the
- // variable itself will have the expected value when displayed by the
- // optimizer in an optimized build. Look at a memory dump of the stack.
- static constexpr int kStackTaskTraceSnapshotSize =
- std::tuple_size<decltype(pending_task->task_backtrace)>::value + 1;
- std::array<const void*, kStackTaskTraceSnapshotSize> task_backtrace;
- task_backtrace[0] = pending_task->posted_from.program_counter();
- std::copy(pending_task->task_backtrace.begin(),
- pending_task->task_backtrace.end(), task_backtrace.begin() + 1);
- debug::Alias(&task_backtrace);
+ // Before running the task, store the program counter where it was posted
+ // and deliberately alias it to ensure it is on the stack if the task
+ // crashes. Be careful not to assume that the variable itself will have the
+ // expected value when displayed by the optimizer in an optimized build.
+ // Look at a memory dump of the stack.
+ const void* program_counter = pending_task.posted_from.program_counter();
+ debug::Alias(&program_counter);
- std::move(pending_task->task).Run();
+ pending_task.task.Run();
stopwatch.Stop();
- tracked_objects::ThreadData::TallyRunOnNamedThreadIfTracking(*pending_task,
- stopwatch);
+ tracked_objects::ThreadData::TallyRunOnNamedThreadIfTracking(
+ pending_task, stopwatch);
}
uint64_t TaskAnnotator::GetTaskTraceID(const PendingTask& task) const {
diff --git a/base/debug/task_annotator.h b/base/debug/task_annotator.h
index 34115d8f3d..2687c5c930 100644
--- a/base/debug/task_annotator.h
+++ b/base/debug/task_annotator.h
@@ -28,7 +28,7 @@ class BASE_EXPORT TaskAnnotator {
// Run a previously queued task. |queue_function| should match what was
// passed into |DidQueueTask| for this task.
- void RunTask(const char* queue_function, PendingTask* pending_task);
+ void RunTask(const char* queue_function, const PendingTask& pending_task);
private:
// Creates a process-wide unique ID to represent this task in trace events.
diff --git a/base/debug/task_annotator_unittest.cc b/base/debug/task_annotator_unittest.cc
index 8a1c8bdc87..9f5c442327 100644
--- a/base/debug/task_annotator_unittest.cc
+++ b/base/debug/task_annotator_unittest.cc
@@ -24,7 +24,7 @@ TEST(TaskAnnotatorTest, QueueAndRunTask) {
TaskAnnotator annotator;
annotator.DidQueueTask("TaskAnnotatorTest::Queue", pending_task);
EXPECT_EQ(0, result);
- annotator.RunTask("TaskAnnotatorTest::Queue", &pending_task);
+ annotator.RunTask("TaskAnnotatorTest::Queue", pending_task);
EXPECT_EQ(123, result);
}
diff --git a/base/debug/thread_heap_usage_tracker.h b/base/debug/thread_heap_usage_tracker.h
deleted file mode 100644
index 508a0a3973..0000000000
--- a/base/debug/thread_heap_usage_tracker.h
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright 2016 The Chromium 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_DEBUG_THREAD_HEAP_USAGE_TRACKER_H_
-#define BASE_DEBUG_THREAD_HEAP_USAGE_TRACKER_H_
-
-#include <stdint.h>
-
-#include "base/allocator/features.h"
-#include "base/base_export.h"
-#include "base/threading/thread_checker.h"
-
-namespace base {
-namespace allocator {
-struct AllocatorDispatch;
-} // namespace allocator
-
-namespace debug {
-
-// Used to store the heap allocator usage in a scope.
-struct ThreadHeapUsage {
- // The cumulative number of allocation operations.
- uint64_t alloc_ops;
-
- // The cumulative number of allocated bytes. Where available, this is
- // inclusive heap padding and estimated or actual heap overhead.
- uint64_t alloc_bytes;
-
- // Where available, cumulative number of heap padding and overhead bytes.
- uint64_t alloc_overhead_bytes;
-
- // The cumulative number of free operations.
- uint64_t free_ops;
-
- // The cumulative number of bytes freed.
- // Only recorded if the underlying heap shim can return the size of an
- // allocation.
- uint64_t free_bytes;
-
- // The maximal value of |alloc_bytes| - |free_bytes| seen for this thread.
- // Only recorded if the underlying heap shim supports returning the size of
- // an allocation.
- uint64_t max_allocated_bytes;
-};
-
-// By keeping a tally on heap operations, it's possible to track:
-// - the number of alloc/free operations, where a realloc is zero or one
-// of each, depending on the input parameters (see man realloc).
-// - the number of bytes allocated/freed.
-// - the number of estimated bytes of heap overhead used.
-// - the high-watermark amount of bytes allocated in the scope.
-// This in turn allows measuring the memory usage and memory usage churn over
-// a scope. Scopes must be cleanly nested, and each scope must be
-// destroyed on the thread where it's created.
-//
-// Note that this depends on the capabilities of the underlying heap shim. If
-// that shim can not yield a size estimate for an allocation, it's not possible
-// to keep track of overhead, freed bytes and the allocation high water mark.
-class BASE_EXPORT ThreadHeapUsageTracker {
- public:
- ThreadHeapUsageTracker();
- ~ThreadHeapUsageTracker();
-
- // Start tracking heap usage on this thread.
- // This may only be called on the thread where the instance is created.
- // Note IsHeapTrackingEnabled() must be true.
- void Start();
-
- // Stop tracking heap usage on this thread and store the usage tallied.
- // If |usage_is_exclusive| is true, the usage tallied won't be added to the
- // outer scope's usage. If |usage_is_exclusive| is false, the usage tallied
- // in this scope will also tally to any outer scope.
- // This may only be called on the thread where the instance is created.
- void Stop(bool usage_is_exclusive);
-
- // After Stop() returns the usage tallied from Start() to Stop().
- const ThreadHeapUsage& usage() const { return usage_; }
-
- // Returns this thread's heap usage from the start of the innermost
- // enclosing ThreadHeapUsageTracker instance, if any.
- static ThreadHeapUsage GetUsageSnapshot();
-
- // Enables the heap intercept. May only be called once, and only if the heap
- // shim is available, e.g. if BUILDFLAG(USE_EXPERIMENTAL_ALLOCATOR_SHIM) is
- // true.
- static void EnableHeapTracking();
-
- // Returns true iff heap tracking is enabled.
- static bool IsHeapTrackingEnabled();
-
- protected:
- // Exposed for testing only - note that it's safe to re-EnableHeapTracking()
- // after calling this function in tests.
- static void DisableHeapTrackingForTesting();
-
- // Exposed for testing only.
- static void EnsureTLSInitialized();
-
- // Exposed to allow testing the shim without inserting it in the allocator
- // shim chain.
- static base::allocator::AllocatorDispatch* GetDispatchForTesting();
-
- private:
- ThreadChecker thread_checker_;
-
- // The heap usage at Start(), or the difference from Start() to Stop().
- ThreadHeapUsage usage_;
-
- // This thread's heap usage, non-null from Start() to Stop().
- ThreadHeapUsage* thread_usage_;
-};
-
-} // namespace debug
-} // namespace base
-
-#endif // BASE_DEBUG_THREAD_HEAP_USAGE_TRACKER_H_ \ No newline at end of file
diff --git a/base/feature_list.cc b/base/feature_list.cc
index 353136c12b..435165e10c 100644
--- a/base/feature_list.cc
+++ b/base/feature_list.cc
@@ -10,9 +10,7 @@
#include <vector>
#include "base/logging.h"
-#include "base/memory/ptr_util.h"
#include "base/metrics/field_trial.h"
-#include "base/pickle.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
@@ -28,42 +26,6 @@ FeatureList* g_instance = nullptr;
// Tracks whether the FeatureList instance was initialized via an accessor.
bool g_initialized_from_accessor = false;
-// 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 {
- // SHA1(FeatureEntry): Increment this if structure changes!
- static constexpr uint32_t kPersistentTypeId = 0x06567CA6 + 1;
-
- // Expected size for 32/64-bit check.
- static constexpr size_t kExpectedInstanceSize = 8;
-
- // Specifies whether a feature override enables or disables the feature. Same
- // values as the OverrideState enum in feature_list.h
- uint32_t override_state;
-
- // Size of the pickled structure, NOT the total size of this entry.
- uint32_t pickle_size;
-
- // Reads the feature and trial name from the pickle. Calling this is only
- // valid on an initialized entry that's in shared memory.
- bool GetFeatureAndTrialName(StringPiece* feature_name,
- StringPiece* trial_name) const {
- const char* src =
- reinterpret_cast<const char*>(this) + sizeof(FeatureEntry);
-
- Pickle pickle(src, pickle_size);
- PickleIterator pickle_iter(pickle);
-
- if (!pickle_iter.ReadStringPiece(feature_name))
- return false;
-
- // Return true because we are not guaranteed to have a trial name anyways.
- auto sink = pickle_iter.ReadStringPiece(trial_name);
- ALLOW_UNUSED_LOCAL(sink);
- return true;
- }
-};
-
// Some characters are not allowed to appear in feature names or the associated
// field trial names, as they are used as special characters for command-line
// serialization. This function checks that the strings are ASCII (since they
@@ -93,26 +55,6 @@ void FeatureList::InitializeFromCommandLine(
initialized_from_command_line_ = true;
}
-void FeatureList::InitializeFromSharedMemory(
- PersistentMemoryAllocator* allocator) {
- DCHECK(!initialized_);
-
- PersistentMemoryAllocator::Iterator iter(allocator);
- const FeatureEntry* entry;
- while ((entry = iter.GetNextOfObject<FeatureEntry>()) != nullptr) {
- OverrideState override_state =
- static_cast<OverrideState>(entry->override_state);
-
- StringPiece feature_name;
- StringPiece trial_name;
- if (!entry->GetFeatureAndTrialName(&feature_name, &trial_name))
- continue;
-
- FieldTrial* trial = FieldTrialList::Find(trial_name.as_string());
- RegisterOverride(feature_name, override_state, trial);
- }
-}
-
bool FeatureList::IsFeatureOverriddenFromCommandLine(
const std::string& feature_name,
OverrideState state) const {
@@ -155,30 +97,6 @@ void FeatureList::RegisterFieldTrialOverride(const std::string& feature_name,
RegisterOverride(feature_name, override_state, field_trial);
}
-void FeatureList::AddFeaturesToAllocator(PersistentMemoryAllocator* allocator) {
- DCHECK(initialized_);
-
- for (const auto& override : overrides_) {
- Pickle pickle;
- pickle.WriteString(override.first);
- if (override.second.field_trial)
- pickle.WriteString(override.second.field_trial->trial_name());
-
- size_t total_size = sizeof(FeatureEntry) + pickle.size();
- FeatureEntry* entry = allocator->New<FeatureEntry>(total_size);
- if (!entry)
- return;
-
- entry->override_state = override.second.overridden_state;
- entry->pickle_size = pickle.size();
-
- char* dst = reinterpret_cast<char*>(entry) + sizeof(FeatureEntry);
- memcpy(dst, pickle.data(), pickle.size());
-
- allocator->MakeIterable(entry);
- }
-}
-
void FeatureList::GetFeatureOverrides(std::string* enable_overrides,
std::string* disable_overrides) {
DCHECK(initialized_);
@@ -279,19 +197,10 @@ void FeatureList::SetInstance(std::unique_ptr<FeatureList> instance) {
}
// static
-std::unique_ptr<FeatureList> FeatureList::ClearInstanceForTesting() {
- FeatureList* old_instance = g_instance;
+void FeatureList::ClearInstanceForTesting() {
+ delete g_instance;
g_instance = nullptr;
g_initialized_from_accessor = false;
- return base::WrapUnique(old_instance);
-}
-
-// static
-void FeatureList::RestoreInstanceForTesting(
- std::unique_ptr<FeatureList> instance) {
- DCHECK(!g_instance);
- // Note: Intentional leak of global singleton.
- g_instance = instance.release();
}
void FeatureList::FinalizeInitialization() {
diff --git a/base/feature_list.h b/base/feature_list.h
index 09e8408aa8..e9ed00a124 100644
--- a/base/feature_list.h
+++ b/base/feature_list.h
@@ -13,7 +13,6 @@
#include "base/base_export.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
-#include "base/metrics/persistent_memory_allocator.h"
#include "base/strings/string_piece.h"
#include "base/synchronization/lock.h"
@@ -32,8 +31,6 @@ enum FeatureState {
// for a given feature name - generally defined as a constant global variable or
// file static.
struct BASE_EXPORT Feature {
- constexpr Feature(const char* name, FeatureState default_state)
- : name(name), default_state(default_state) {}
// The name of the feature. This should be unique to each feature and is used
// for enabling/disabling features via command line flags and experiments.
const char* const name;
@@ -95,11 +92,6 @@ class BASE_EXPORT FeatureList {
void InitializeFromCommandLine(const std::string& enable_features,
const std::string& disable_features);
- // Initializes feature overrides through the field trial allocator, which
- // we're using to store the feature names, their override state, and the name
- // of the associated field trial.
- void InitializeFromSharedMemory(PersistentMemoryAllocator* allocator);
-
// Specifies whether a feature override enables or disables the feature.
enum OverrideState {
OVERRIDE_USE_DEFAULT,
@@ -132,9 +124,6 @@ class BASE_EXPORT FeatureList {
OverrideState override_state,
FieldTrial* field_trial);
- // Loops through feature overrides and serializes them all into |allocator|.
- void AddFeaturesToAllocator(PersistentMemoryAllocator* allocator);
-
// Returns comma-separated lists of feature names (in the same format that is
// accepted by InitializeFromCommandLine()) corresponding to features that
// have been overridden - either through command-line or via FieldTrials. For
@@ -174,27 +163,13 @@ class BASE_EXPORT FeatureList {
// Registers the given |instance| to be the singleton feature list for this
// process. This should only be called once and |instance| must not be null.
- // Note: If you are considering using this for the purposes of testing, take
- // a look at using base/test/scoped_feature_list.h instead.
static void SetInstance(std::unique_ptr<FeatureList> instance);
- // Clears the previously-registered singleton instance for tests and returns
- // the old instance.
- // Note: Most tests should never call this directly. Instead consider using
- // base::test::ScopedFeatureList.
- static std::unique_ptr<FeatureList> ClearInstanceForTesting();
-
- // Sets a given (initialized) |instance| to be the singleton feature list,
- // for testing. Existing instance must be null. This is primarily intended
- // to support base::test::ScopedFeatureList helper class.
- static void RestoreInstanceForTesting(std::unique_ptr<FeatureList> instance);
+ // Clears the previously-registered singleton instance for tests.
+ static void ClearInstanceForTesting();
private:
FRIEND_TEST_ALL_PREFIXES(FeatureListTest, CheckFeatureIdentity);
- FRIEND_TEST_ALL_PREFIXES(FeatureListTest,
- StoreAndRetrieveFeaturesFromSharedMemory);
- FRIEND_TEST_ALL_PREFIXES(FeatureListTest,
- StoreAndRetrieveAssociatedFeaturesFromSharedMemory);
struct OverrideEntry {
// The overridden enable (on/off) state of the feature.
diff --git a/base/feature_list_unittest.cc b/base/feature_list_unittest.cc
index fb3b320ae9..9d1dcb72f3 100644
--- a/base/feature_list_unittest.cc
+++ b/base/feature_list_unittest.cc
@@ -13,7 +13,6 @@
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/field_trial.h"
-#include "base/metrics/persistent_memory_allocator.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -22,12 +21,12 @@ namespace base {
namespace {
-constexpr char kFeatureOnByDefaultName[] = "OnByDefault";
+const char kFeatureOnByDefaultName[] = "OnByDefault";
struct Feature kFeatureOnByDefault {
kFeatureOnByDefaultName, FEATURE_ENABLED_BY_DEFAULT
};
-constexpr char kFeatureOffByDefaultName[] = "OffByDefault";
+const char kFeatureOffByDefaultName[] = "OffByDefault";
struct Feature kFeatureOffByDefault {
kFeatureOffByDefaultName, FEATURE_DISABLED_BY_DEFAULT
};
@@ -469,68 +468,4 @@ TEST_F(FeatureListTest, UninitializedInstance_IsEnabledReturnsFalse) {
EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
}
-TEST_F(FeatureListTest, StoreAndRetrieveFeaturesFromSharedMemory) {
- std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
-
- // Create some overrides.
- feature_list->RegisterOverride(kFeatureOffByDefaultName,
- FeatureList::OVERRIDE_ENABLE_FEATURE, nullptr);
- feature_list->RegisterOverride(
- kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE, nullptr);
- feature_list->FinalizeInitialization();
-
- // Create an allocator and store the overrides.
- std::unique_ptr<SharedMemory> shm(new SharedMemory());
- shm->CreateAndMapAnonymous(4 << 10);
- SharedPersistentMemoryAllocator allocator(std::move(shm), 1, "", false);
- feature_list->AddFeaturesToAllocator(&allocator);
-
- std::unique_ptr<base::FeatureList> feature_list2(new base::FeatureList);
-
- // Check that the new feature list is empty.
- EXPECT_FALSE(feature_list2->IsFeatureOverriddenFromCommandLine(
- kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE));
- EXPECT_FALSE(feature_list2->IsFeatureOverriddenFromCommandLine(
- kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE));
-
- feature_list2->InitializeFromSharedMemory(&allocator);
- // Check that the new feature list now has 2 overrides.
- EXPECT_TRUE(feature_list2->IsFeatureOverriddenFromCommandLine(
- kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE));
- EXPECT_TRUE(feature_list2->IsFeatureOverriddenFromCommandLine(
- kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE));
-}
-
-TEST_F(FeatureListTest, StoreAndRetrieveAssociatedFeaturesFromSharedMemory) {
- FieldTrialList field_trial_list(nullptr);
- std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
-
- // Create some overrides.
- FieldTrial* trial1 = FieldTrialList::CreateFieldTrial("TrialExample1", "A");
- FieldTrial* trial2 = FieldTrialList::CreateFieldTrial("TrialExample2", "B");
- feature_list->RegisterFieldTrialOverride(
- kFeatureOnByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial1);
- feature_list->RegisterFieldTrialOverride(
- kFeatureOffByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial2);
- feature_list->FinalizeInitialization();
-
- // Create an allocator and store the overrides.
- std::unique_ptr<SharedMemory> shm(new SharedMemory());
- shm->CreateAndMapAnonymous(4 << 10);
- SharedPersistentMemoryAllocator allocator(std::move(shm), 1, "", false);
- feature_list->AddFeaturesToAllocator(&allocator);
-
- std::unique_ptr<base::FeatureList> feature_list2(new base::FeatureList);
- feature_list2->InitializeFromSharedMemory(&allocator);
- feature_list2->FinalizeInitialization();
-
- // Check that the field trials are still associated.
- FieldTrial* associated_trial1 =
- feature_list2->GetAssociatedFieldTrial(kFeatureOnByDefault);
- FieldTrial* associated_trial2 =
- feature_list2->GetAssociatedFieldTrial(kFeatureOffByDefault);
- EXPECT_EQ(associated_trial1, trial1);
- EXPECT_EQ(associated_trial2, trial2);
-}
-
} // namespace base
diff --git a/base/file_version_info_unittest.cc b/base/file_version_info_unittest.cc
new file mode 100644
index 0000000000..67edc7737f
--- /dev/null
+++ b/base/file_version_info_unittest.cc
@@ -0,0 +1,144 @@
+// 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/file_version_info.h"
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+#include "base/path_service.h"
+#include "base/file_version_info_win.h"
+#endif
+
+using base::FilePath;
+
+namespace {
+
+#if defined(OS_WIN)
+FilePath GetTestDataPath() {
+ FilePath path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &path);
+ path = path.AppendASCII("base");
+ path = path.AppendASCII("test");
+ path = path.AppendASCII("data");
+ path = path.AppendASCII("file_version_info_unittest");
+ return path;
+}
+#endif
+
+} // namespace
+
+#if defined(OS_WIN)
+TEST(FileVersionInfoTest, HardCodedProperties) {
+ const wchar_t kDLLName[] = {L"FileVersionInfoTest1.dll"};
+
+ const wchar_t* const kExpectedValues[15] = {
+ // FileVersionInfoTest.dll
+ L"Goooooogle", // company_name
+ L"Google", // company_short_name
+ L"This is the product name", // product_name
+ L"This is the product short name", // product_short_name
+ L"The Internal Name", // internal_name
+ L"4.3.2.1", // product_version
+ L"Private build property", // private_build
+ L"Special build property", // special_build
+ L"This is a particularly interesting comment", // comments
+ L"This is the original filename", // original_filename
+ L"This is my file description", // file_description
+ L"1.2.3.4", // file_version
+ L"This is the legal copyright", // legal_copyright
+ L"This is the legal trademarks", // legal_trademarks
+ L"This is the last change", // last_change
+ };
+
+ FilePath dll_path = GetTestDataPath();
+ dll_path = dll_path.Append(kDLLName);
+
+ std::unique_ptr<FileVersionInfo> version_info(
+ FileVersionInfo::CreateFileVersionInfo(dll_path));
+
+ int j = 0;
+ EXPECT_EQ(kExpectedValues[j++], version_info->company_name());
+ EXPECT_EQ(kExpectedValues[j++], version_info->company_short_name());
+ EXPECT_EQ(kExpectedValues[j++], version_info->product_name());
+ EXPECT_EQ(kExpectedValues[j++], version_info->product_short_name());
+ EXPECT_EQ(kExpectedValues[j++], version_info->internal_name());
+ EXPECT_EQ(kExpectedValues[j++], version_info->product_version());
+ EXPECT_EQ(kExpectedValues[j++], version_info->private_build());
+ EXPECT_EQ(kExpectedValues[j++], version_info->special_build());
+ EXPECT_EQ(kExpectedValues[j++], version_info->comments());
+ EXPECT_EQ(kExpectedValues[j++], version_info->original_filename());
+ EXPECT_EQ(kExpectedValues[j++], version_info->file_description());
+ EXPECT_EQ(kExpectedValues[j++], version_info->file_version());
+ EXPECT_EQ(kExpectedValues[j++], version_info->legal_copyright());
+ EXPECT_EQ(kExpectedValues[j++], version_info->legal_trademarks());
+ EXPECT_EQ(kExpectedValues[j++], version_info->last_change());
+}
+#endif
+
+#if defined(OS_WIN)
+TEST(FileVersionInfoTest, IsOfficialBuild) {
+ const wchar_t* kDLLNames[] = {
+ L"FileVersionInfoTest1.dll",
+ L"FileVersionInfoTest2.dll"
+ };
+
+ const bool kExpected[] = {
+ true,
+ false,
+ };
+
+ // Test consistency check.
+ ASSERT_EQ(arraysize(kDLLNames), arraysize(kExpected));
+
+ for (size_t i = 0; i < arraysize(kDLLNames); ++i) {
+ FilePath dll_path = GetTestDataPath();
+ dll_path = dll_path.Append(kDLLNames[i]);
+
+ std::unique_ptr<FileVersionInfo> version_info(
+ FileVersionInfo::CreateFileVersionInfo(dll_path));
+
+ EXPECT_EQ(kExpected[i], version_info->is_official_build());
+ }
+}
+#endif
+
+#if defined(OS_WIN)
+TEST(FileVersionInfoTest, CustomProperties) {
+ FilePath dll_path = GetTestDataPath();
+ dll_path = dll_path.AppendASCII("FileVersionInfoTest1.dll");
+
+ std::unique_ptr<FileVersionInfo> version_info(
+ FileVersionInfo::CreateFileVersionInfo(dll_path));
+
+ // Test few existing properties.
+ std::wstring str;
+ FileVersionInfoWin* version_info_win =
+ static_cast<FileVersionInfoWin*>(version_info.get());
+ EXPECT_TRUE(version_info_win->GetValue(L"Custom prop 1", &str));
+ EXPECT_EQ(L"Un", str);
+ EXPECT_EQ(L"Un", version_info_win->GetStringValue(L"Custom prop 1"));
+
+ EXPECT_TRUE(version_info_win->GetValue(L"Custom prop 2", &str));
+ EXPECT_EQ(L"Deux", str);
+ EXPECT_EQ(L"Deux", version_info_win->GetStringValue(L"Custom prop 2"));
+
+ EXPECT_TRUE(version_info_win->GetValue(L"Custom prop 3", &str));
+ EXPECT_EQ(L"1600 Amphitheatre Parkway Mountain View, CA 94043", str);
+ EXPECT_EQ(L"1600 Amphitheatre Parkway Mountain View, CA 94043",
+ version_info_win->GetStringValue(L"Custom prop 3"));
+
+ // Test an non-existing property.
+ EXPECT_FALSE(version_info_win->GetValue(L"Unknown property", &str));
+ EXPECT_EQ(L"", version_info_win->GetStringValue(L"Unknown property"));
+}
+#endif
diff --git a/base/files/dir_reader_posix_unittest.cc b/base/files/dir_reader_posix_unittest.cc
index 5d7fd8b139..a75858feeb 100644
--- a/base/files/dir_reader_posix_unittest.cc
+++ b/base/files/dir_reader_posix_unittest.cc
@@ -29,7 +29,7 @@ TEST(DirReaderPosixUnittest, Read) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- const char* dir = temp_dir.GetPath().value().c_str();
+ const char* dir = temp_dir.path().value().c_str();
ASSERT_TRUE(dir);
const int prev_wd = open(".", O_RDONLY | O_DIRECTORY);
diff --git a/base/files/file.cc b/base/files/file.cc
index 1b2224e323..ab05630062 100644
--- a/base/files/file.cc
+++ b/base/files/file.cc
@@ -138,4 +138,12 @@ std::string File::ErrorToString(Error error) {
return "";
}
+bool File::Flush() {
+ ElapsedTimer timer;
+ SCOPED_FILE_TRACE("Flush");
+ bool return_value = DoFlush();
+ UMA_HISTOGRAM_TIMES("PlatformFile.FlushTime", timer.Elapsed());
+ return return_value;
+}
+
} // namespace base
diff --git a/base/files/file.h b/base/files/file.h
index 94a9d5cf49..ae2bd1b61b 100644
--- a/base/files/file.h
+++ b/base/files/file.h
@@ -63,31 +63,28 @@ class BASE_EXPORT File {
// FLAG_EXCLUSIVE_(READ|WRITE) only grant exclusive access to the file on
// creation on POSIX; for existing files, consider using Lock().
enum Flags {
- FLAG_OPEN = 1 << 0, // Opens a file, only if it exists.
- FLAG_CREATE = 1 << 1, // Creates a new file, only if it does not
- // already exist.
- FLAG_OPEN_ALWAYS = 1 << 2, // May create a new file.
- FLAG_CREATE_ALWAYS = 1 << 3, // May overwrite an old file.
- FLAG_OPEN_TRUNCATED = 1 << 4, // Opens a file and truncates it, only if it
- // exists.
+ FLAG_OPEN = 1 << 0, // Opens a file, only if it exists.
+ FLAG_CREATE = 1 << 1, // Creates a new file, only if it does not
+ // already exist.
+ FLAG_OPEN_ALWAYS = 1 << 2, // May create a new file.
+ FLAG_CREATE_ALWAYS = 1 << 3, // May overwrite an old file.
+ FLAG_OPEN_TRUNCATED = 1 << 4, // Opens a file and truncates it, only if it
+ // exists.
FLAG_READ = 1 << 5,
FLAG_WRITE = 1 << 6,
FLAG_APPEND = 1 << 7,
- FLAG_EXCLUSIVE_READ = 1 << 8, // EXCLUSIVE is opposite of Windows SHARE.
+ FLAG_EXCLUSIVE_READ = 1 << 8, // EXCLUSIVE is opposite of Windows SHARE.
FLAG_EXCLUSIVE_WRITE = 1 << 9,
FLAG_ASYNC = 1 << 10,
- FLAG_TEMPORARY = 1 << 11, // Used on Windows only.
- FLAG_HIDDEN = 1 << 12, // Used on Windows only.
+ FLAG_TEMPORARY = 1 << 11, // Used on Windows only.
+ FLAG_HIDDEN = 1 << 12, // Used on Windows only.
FLAG_DELETE_ON_CLOSE = 1 << 13,
- FLAG_WRITE_ATTRIBUTES = 1 << 14, // Used on Windows only.
- FLAG_SHARE_DELETE = 1 << 15, // Used on Windows only.
- FLAG_TERMINAL_DEVICE = 1 << 16, // Serial port flags.
- FLAG_BACKUP_SEMANTICS = 1 << 17, // Used on Windows only.
- FLAG_EXECUTE = 1 << 18, // Used on Windows only.
- FLAG_SEQUENTIAL_SCAN = 1 << 19, // Used on Windows only.
- FLAG_CAN_DELETE_ON_CLOSE = 1 << 20, // Requests permission to delete a file
- // via DeleteOnClose() (Windows only).
- // See DeleteOnClose() for details.
+ FLAG_WRITE_ATTRIBUTES = 1 << 14, // Used on Windows only.
+ FLAG_SHARE_DELETE = 1 << 15, // Used on Windows only.
+ FLAG_TERMINAL_DEVICE = 1 << 16, // Serial port flags.
+ FLAG_BACKUP_SEMANTICS = 1 << 17, // Used on Windows only.
+ FLAG_EXECUTE = 1 << 18, // Used on Windows only.
+ FLAG_SEQUENTIAL_SCAN = 1 << 19, // Used on Windows only.
};
// This enum has been recorded in multiple histograms. If the order of the
@@ -293,41 +290,11 @@ class BASE_EXPORT File {
// object that was created or initialized with this flag will have unlinked
// the underlying file when it was created or opened. On Windows, the
// underlying file is deleted when the last handle to it is closed.
- File Duplicate() const;
+ File Duplicate();
bool async() const { return async_; }
#if defined(OS_WIN)
- // Sets or clears the DeleteFile disposition on the handle. Returns true if
- // the disposition was set or cleared, as indicated by |delete_on_close|.
- //
- // Microsoft Windows deletes a file only when the last handle to the
- // underlying kernel object is closed when the DeleteFile disposition has been
- // set by any handle holder. This disposition is be set by:
- // - Calling the Win32 DeleteFile function with the path to a file.
- // - Opening/creating a file with FLAG_DELETE_ON_CLOSE.
- // - Opening/creating a file with FLAG_CAN_DELETE_ON_CLOSE and subsequently
- // calling DeleteOnClose(true).
- //
- // In all cases, all pre-existing handles to the file must have been opened
- // with FLAG_SHARE_DELETE.
- //
- // So:
- // - Use FLAG_SHARE_DELETE when creating/opening a file to allow another
- // entity on the system to cause it to be deleted when it is closed. (Note:
- // another entity can delete the file the moment after it is closed, so not
- // using this permission doesn't provide any protections.)
- // - Use FLAG_DELETE_ON_CLOSE for any file that is to be deleted after use.
- // The OS will ensure it is deleted even in the face of process termination.
- // - Use FLAG_CAN_DELETE_ON_CLOSE in conjunction with DeleteOnClose() to alter
- // the DeleteFile disposition on an open handle. This fine-grained control
- // allows for marking a file for deletion during processing so that it is
- // deleted in the event of untimely process termination, and then clearing
- // this state once the file is suitable for persistence.
- bool DeleteOnClose(bool delete_on_close);
-#endif
-
-#if defined(OS_WIN)
static Error OSErrorToFileError(DWORD last_error);
#elif defined(OS_POSIX)
static Error OSErrorToFileError(int saved_errno);
@@ -343,6 +310,10 @@ class BASE_EXPORT File {
// traversal ('..') components.
void DoInitialize(const FilePath& path, uint32_t flags);
+ // TODO(tnagel): Reintegrate into Flush() once histogram isn't needed anymore,
+ // cf. issue 473337.
+ bool DoFlush();
+
void SetPlatformFile(PlatformFile file);
#if defined(OS_WIN)
diff --git a/base/files/file_descriptor_watcher_posix.cc b/base/files/file_descriptor_watcher_posix.cc
deleted file mode 100644
index 9746e35ea7..0000000000
--- a/base/files/file_descriptor_watcher_posix.cc
+++ /dev/null
@@ -1,210 +0,0 @@
-// Copyright 2016 The Chromium 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/files/file_descriptor_watcher_posix.h"
-
-#include "base/bind.h"
-#include "base/lazy_instance.h"
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/sequenced_task_runner.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/sequenced_task_runner_handle.h"
-#include "base/threading/thread_checker.h"
-#include "base/threading/thread_local.h"
-
-namespace base {
-
-namespace {
-
-// MessageLoopForIO used to watch file descriptors for which callbacks are
-// registered from a given thread.
-LazyInstance<ThreadLocalPointer<MessageLoopForIO>>::Leaky
- tls_message_loop_for_io = LAZY_INSTANCE_INITIALIZER;
-
-} // namespace
-
-FileDescriptorWatcher::Controller::~Controller() {
- DCHECK(sequence_checker_.CalledOnValidSequence());
-
- // Delete |watcher_| on the MessageLoopForIO.
- //
- // If the MessageLoopForIO is deleted before Watcher::StartWatching() runs,
- // |watcher_| is leaked. If the MessageLoopForIO is deleted after
- // Watcher::StartWatching() runs but before the DeleteSoon task runs,
- // |watcher_| is deleted from Watcher::WillDestroyCurrentMessageLoop().
- message_loop_for_io_task_runner_->DeleteSoon(FROM_HERE, watcher_.release());
-
- // Since WeakPtrs are invalidated by the destructor, RunCallback() won't be
- // invoked after this returns.
-}
-
-class FileDescriptorWatcher::Controller::Watcher
- : public MessageLoopForIO::Watcher,
- public MessageLoop::DestructionObserver {
- public:
- Watcher(WeakPtr<Controller> controller, MessageLoopForIO::Mode mode, int fd);
- ~Watcher() override;
-
- void StartWatching();
-
- private:
- friend class FileDescriptorWatcher;
-
- // MessageLoopForIO::Watcher:
- void OnFileCanReadWithoutBlocking(int fd) override;
- void OnFileCanWriteWithoutBlocking(int fd) override;
-
- // MessageLoop::DestructionObserver:
- void WillDestroyCurrentMessageLoop() override;
-
- // Used to instruct the MessageLoopForIO to stop watching the file descriptor.
- MessageLoopForIO::FileDescriptorWatcher file_descriptor_watcher_;
-
- // Runs tasks on the sequence on which this was instantiated (i.e. the
- // sequence on which the callback must run).
- const scoped_refptr<SequencedTaskRunner> callback_task_runner_ =
- SequencedTaskRunnerHandle::Get();
-
- // The Controller that created this Watcher.
- WeakPtr<Controller> controller_;
-
- // Whether this Watcher is notified when |fd_| becomes readable or writable
- // without blocking.
- const MessageLoopForIO::Mode mode_;
-
- // The watched file descriptor.
- const int fd_;
-
- // Except for the constructor, every method of this class must run on the same
- // MessageLoopForIO thread.
- ThreadChecker thread_checker_;
-
- // Whether this Watcher was registered as a DestructionObserver on the
- // MessageLoopForIO thread.
- bool registered_as_destruction_observer_ = false;
-
- DISALLOW_COPY_AND_ASSIGN(Watcher);
-};
-
-FileDescriptorWatcher::Controller::Watcher::Watcher(
- WeakPtr<Controller> controller,
- MessageLoopForIO::Mode mode,
- int fd)
- : file_descriptor_watcher_(FROM_HERE),
- controller_(controller),
- mode_(mode),
- fd_(fd) {
- DCHECK(callback_task_runner_);
- thread_checker_.DetachFromThread();
-}
-
-FileDescriptorWatcher::Controller::Watcher::~Watcher() {
- DCHECK(thread_checker_.CalledOnValidThread());
- MessageLoopForIO::current()->RemoveDestructionObserver(this);
-}
-
-void FileDescriptorWatcher::Controller::Watcher::StartWatching() {
- DCHECK(thread_checker_.CalledOnValidThread());
-
- MessageLoopForIO::current()->WatchFileDescriptor(
- fd_, false, mode_, &file_descriptor_watcher_, this);
-
- if (!registered_as_destruction_observer_) {
- MessageLoopForIO::current()->AddDestructionObserver(this);
- registered_as_destruction_observer_ = true;
- }
-}
-
-void FileDescriptorWatcher::Controller::Watcher::OnFileCanReadWithoutBlocking(
- int fd) {
- DCHECK_EQ(fd_, fd);
- DCHECK_EQ(MessageLoopForIO::WATCH_READ, mode_);
- DCHECK(thread_checker_.CalledOnValidThread());
-
- // Run the callback on the sequence on which the watch was initiated.
- callback_task_runner_->PostTask(FROM_HERE,
- Bind(&Controller::RunCallback, controller_));
-}
-
-void FileDescriptorWatcher::Controller::Watcher::OnFileCanWriteWithoutBlocking(
- int fd) {
- DCHECK_EQ(fd_, fd);
- DCHECK_EQ(MessageLoopForIO::WATCH_WRITE, mode_);
- DCHECK(thread_checker_.CalledOnValidThread());
-
- // Run the callback on the sequence on which the watch was initiated.
- callback_task_runner_->PostTask(FROM_HERE,
- Bind(&Controller::RunCallback, controller_));
-}
-
-void FileDescriptorWatcher::Controller::Watcher::
- WillDestroyCurrentMessageLoop() {
- DCHECK(thread_checker_.CalledOnValidThread());
-
- // A Watcher is owned by a Controller. When the Controller is deleted, it
- // transfers ownership of the Watcher to a delete task posted to the
- // MessageLoopForIO. If the MessageLoopForIO is deleted before the delete task
- // runs, the following line takes care of deleting the Watcher.
- delete this;
-}
-
-FileDescriptorWatcher::Controller::Controller(MessageLoopForIO::Mode mode,
- int fd,
- const Closure& callback)
- : callback_(callback),
- message_loop_for_io_task_runner_(
- tls_message_loop_for_io.Get().Get()->task_runner()),
- weak_factory_(this) {
- DCHECK(!callback_.is_null());
- DCHECK(message_loop_for_io_task_runner_);
- watcher_ = MakeUnique<Watcher>(weak_factory_.GetWeakPtr(), mode, fd);
- StartWatching();
-}
-
-void FileDescriptorWatcher::Controller::StartWatching() {
- DCHECK(sequence_checker_.CalledOnValidSequence());
- // It is safe to use Unretained() below because |watcher_| can only be deleted
- // by a delete task posted to |message_loop_for_io_task_runner_| by this
- // Controller's destructor. Since this delete task hasn't been posted yet, it
- // can't run before the task posted below.
- message_loop_for_io_task_runner_->PostTask(
- FROM_HERE, Bind(&Watcher::StartWatching, Unretained(watcher_.get())));
-}
-
-void FileDescriptorWatcher::Controller::RunCallback() {
- DCHECK(sequence_checker_.CalledOnValidSequence());
-
- WeakPtr<Controller> weak_this = weak_factory_.GetWeakPtr();
-
- callback_.Run();
-
- // If |this| wasn't deleted, re-enable the watch.
- if (weak_this)
- StartWatching();
-}
-
-FileDescriptorWatcher::FileDescriptorWatcher(
- MessageLoopForIO* message_loop_for_io) {
- DCHECK(message_loop_for_io);
- DCHECK(!tls_message_loop_for_io.Get().Get());
- tls_message_loop_for_io.Get().Set(message_loop_for_io);
-}
-
-FileDescriptorWatcher::~FileDescriptorWatcher() {
- tls_message_loop_for_io.Get().Set(nullptr);
-}
-
-std::unique_ptr<FileDescriptorWatcher::Controller>
-FileDescriptorWatcher::WatchReadable(int fd, const Closure& callback) {
- return WrapUnique(new Controller(MessageLoopForIO::WATCH_READ, fd, callback));
-}
-
-std::unique_ptr<FileDescriptorWatcher::Controller>
-FileDescriptorWatcher::WatchWritable(int fd, const Closure& callback) {
- return WrapUnique(
- new Controller(MessageLoopForIO::WATCH_WRITE, fd, callback));
-}
-
-} // namespace base
diff --git a/base/files/file_descriptor_watcher_posix.h b/base/files/file_descriptor_watcher_posix.h
deleted file mode 100644
index 6cc011bb3e..0000000000
--- a/base/files/file_descriptor_watcher_posix.h
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2016 The Chromium 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_FILES_FILE_DESCRIPTOR_WATCHER_POSIX_H_
-#define BASE_FILES_FILE_DESCRIPTOR_WATCHER_POSIX_H_
-
-#include <memory>
-
-#include "base/base_export.h"
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/message_loop/message_loop.h"
-#include "base/sequence_checker.h"
-
-namespace base {
-
-class SingleThreadTaskRunner;
-
-// The FileDescriptorWatcher API allows callbacks to be invoked when file
-// descriptors are readable or writable without blocking.
-class BASE_EXPORT FileDescriptorWatcher {
- public:
- // Instantiated and returned by WatchReadable() or WatchWritable(). The
- // constructor registers a callback to be invoked when a file descriptor is
- // readable or writable without blocking and the destructor unregisters it.
- class Controller {
- public:
- // Unregisters the callback registered by the constructor.
- ~Controller();
-
- private:
- friend class FileDescriptorWatcher;
- class Watcher;
-
- // Registers |callback| to be invoked when |fd| is readable or writable
- // without blocking (depending on |mode|).
- Controller(MessageLoopForIO::Mode mode, int fd, const Closure& callback);
-
- // Starts watching the file descriptor.
- void StartWatching();
-
- // Runs |callback_|.
- void RunCallback();
-
- // The callback to run when the watched file descriptor is readable or
- // writable without blocking.
- Closure callback_;
-
- // TaskRunner associated with the MessageLoopForIO that watches the file
- // descriptor.
- const scoped_refptr<SingleThreadTaskRunner>
- message_loop_for_io_task_runner_;
-
- // Notified by the MessageLoopForIO associated with
- // |message_loop_for_io_task_runner_| when the watched file descriptor is
- // readable or writable without blocking. Posts a task to run RunCallback()
- // on the sequence on which the Controller was instantiated. When the
- // Controller is deleted, ownership of |watcher_| is transfered to a delete
- // task posted to the MessageLoopForIO. This ensures that |watcher_| isn't
- // deleted while it is being used by the MessageLoopForIO.
- std::unique_ptr<Watcher> watcher_;
-
- // Validates that the Controller is used on the sequence on which it was
- // instantiated.
- SequenceChecker sequence_checker_;
-
- WeakPtrFactory<Controller> weak_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(Controller);
- };
-
- // Registers |message_loop_for_io| to watch file descriptors for which
- // callbacks are registered from the current thread via WatchReadable() or
- // WatchWritable(). |message_loop_for_io| may run on another thread. The
- // constructed FileDescriptorWatcher must not outlive |message_loop_for_io|.
- FileDescriptorWatcher(MessageLoopForIO* message_loop_for_io);
- ~FileDescriptorWatcher();
-
- // Registers |callback| to be invoked on the current sequence when |fd| is
- // readable or writable without blocking. |callback| is unregistered when the
- // returned Controller is deleted (deletion must happen on the current
- // sequence). To call these methods, a FileDescriptorWatcher must have been
- // instantiated on the current thread and SequencedTaskRunnerHandle::IsSet()
- // must return true.
- static std::unique_ptr<Controller> WatchReadable(int fd,
- const Closure& callback);
- static std::unique_ptr<Controller> WatchWritable(int fd,
- const Closure& callback);
-
- private:
- DISALLOW_COPY_AND_ASSIGN(FileDescriptorWatcher);
-};
-
-} // namespace base
-
-#endif // BASE_FILES_FILE_DESCRIPTOR_WATCHER_POSIX_H_
diff --git a/base/files/file_descriptor_watcher_posix_unittest.cc b/base/files/file_descriptor_watcher_posix_unittest.cc
deleted file mode 100644
index 7ff40c5fee..0000000000
--- a/base/files/file_descriptor_watcher_posix_unittest.cc
+++ /dev/null
@@ -1,318 +0,0 @@
-// Copyright 2016 The Chromium 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/files/file_descriptor_watcher_posix.h"
-
-#include <unistd.h>
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/files/file_util.h"
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/message_loop/message_loop.h"
-#include "base/posix/eintr_wrapper.h"
-#include "base/run_loop.h"
-#include "base/test/test_timeouts.h"
-#include "base/threading/platform_thread.h"
-#include "base/threading/thread.h"
-#include "base/threading/thread_checker_impl.h"
-#include "build/build_config.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace base {
-
-namespace {
-
-class Mock {
- public:
- Mock() = default;
-
- MOCK_METHOD0(ReadableCallback, void());
- MOCK_METHOD0(WritableCallback, void());
-
- private:
- DISALLOW_COPY_AND_ASSIGN(Mock);
-};
-
-enum class FileDescriptorWatcherTestType {
- MESSAGE_LOOP_FOR_IO_ON_MAIN_THREAD,
- MESSAGE_LOOP_FOR_IO_ON_OTHER_THREAD,
-};
-
-class FileDescriptorWatcherTest
- : public testing::TestWithParam<FileDescriptorWatcherTestType> {
- public:
- FileDescriptorWatcherTest()
- : message_loop_(GetParam() == FileDescriptorWatcherTestType::
- MESSAGE_LOOP_FOR_IO_ON_MAIN_THREAD
- ? new MessageLoopForIO
- : new MessageLoop),
- other_thread_("FileDescriptorWatcherTest_OtherThread") {}
- ~FileDescriptorWatcherTest() override = default;
-
- void SetUp() override {
- ASSERT_EQ(0, pipe(pipe_fds_));
-
- MessageLoop* message_loop_for_io;
- if (GetParam() ==
- FileDescriptorWatcherTestType::MESSAGE_LOOP_FOR_IO_ON_OTHER_THREAD) {
- Thread::Options options;
- options.message_loop_type = MessageLoop::TYPE_IO;
- ASSERT_TRUE(other_thread_.StartWithOptions(options));
- message_loop_for_io = other_thread_.message_loop();
- } else {
- message_loop_for_io = message_loop_.get();
- }
-
- ASSERT_TRUE(message_loop_for_io->IsType(MessageLoop::TYPE_IO));
- file_descriptor_watcher_ = MakeUnique<FileDescriptorWatcher>(
- static_cast<MessageLoopForIO*>(message_loop_for_io));
- }
-
- void TearDown() override {
- if (GetParam() ==
- FileDescriptorWatcherTestType::MESSAGE_LOOP_FOR_IO_ON_MAIN_THREAD &&
- message_loop_) {
- // Allow the delete task posted by the Controller's destructor to run.
- base::RunLoop().RunUntilIdle();
- }
-
- EXPECT_EQ(0, IGNORE_EINTR(close(pipe_fds_[0])));
- EXPECT_EQ(0, IGNORE_EINTR(close(pipe_fds_[1])));
- }
-
- protected:
- int read_file_descriptor() const { return pipe_fds_[0]; }
- int write_file_descriptor() const { return pipe_fds_[1]; }
-
- // Waits for a short delay and run pending tasks.
- void WaitAndRunPendingTasks() {
- PlatformThread::Sleep(TestTimeouts::tiny_timeout());
- RunLoop().RunUntilIdle();
- }
-
- // Registers ReadableCallback() to be called on |mock_| when
- // read_file_descriptor() is readable without blocking.
- std::unique_ptr<FileDescriptorWatcher::Controller> WatchReadable() {
- std::unique_ptr<FileDescriptorWatcher::Controller> controller =
- FileDescriptorWatcher::WatchReadable(
- read_file_descriptor(),
- Bind(&Mock::ReadableCallback, Unretained(&mock_)));
- EXPECT_TRUE(controller);
-
- // Unless read_file_descriptor() was readable before the callback was
- // registered, this shouldn't do anything.
- WaitAndRunPendingTasks();
-
- return controller;
- }
-
- // Registers WritableCallback() to be called on |mock_| when
- // write_file_descriptor() is writable without blocking.
- std::unique_ptr<FileDescriptorWatcher::Controller> WatchWritable() {
- std::unique_ptr<FileDescriptorWatcher::Controller> controller =
- FileDescriptorWatcher::WatchWritable(
- read_file_descriptor(),
- Bind(&Mock::WritableCallback, Unretained(&mock_)));
- EXPECT_TRUE(controller);
- return controller;
- }
-
- void WriteByte() {
- constexpr char kByte = '!';
- ASSERT_TRUE(
- WriteFileDescriptor(write_file_descriptor(), &kByte, sizeof(kByte)));
- }
-
- void ReadByte() {
- // This is always called as part of the WatchReadable() callback, which
- // should run on the main thread.
- EXPECT_TRUE(thread_checker_.CalledOnValidThread());
-
- char buffer;
- ASSERT_TRUE(ReadFromFD(read_file_descriptor(), &buffer, sizeof(buffer)));
- }
-
- // Mock on wich callbacks are invoked.
- testing::StrictMock<Mock> mock_;
-
- // MessageLoop bound to the main thread.
- std::unique_ptr<MessageLoop> message_loop_;
-
- // Thread running a MessageLoopForIO. Used when the test type is
- // MESSAGE_LOOP_FOR_IO_ON_OTHER_THREAD.
- Thread other_thread_;
-
- private:
- // Determines which MessageLoopForIO is used to watch file descriptors for
- // which callbacks are registered on the main thread.
- std::unique_ptr<FileDescriptorWatcher> file_descriptor_watcher_;
-
- // Watched file descriptors.
- int pipe_fds_[2];
-
- // Used to verify that callbacks run on the thread on which they are
- // registered.
- ThreadCheckerImpl thread_checker_;
-
- DISALLOW_COPY_AND_ASSIGN(FileDescriptorWatcherTest);
-};
-
-} // namespace
-
-TEST_P(FileDescriptorWatcherTest, WatchWritable) {
- auto controller = WatchWritable();
-
-// On Mac and iOS, the write end of a newly created pipe is writable without
-// blocking.
-#if defined(OS_MACOSX)
- RunLoop run_loop;
- EXPECT_CALL(mock_, WritableCallback())
- .WillOnce(testing::Invoke(&run_loop, &RunLoop::Quit));
- run_loop.Run();
-#endif // defined(OS_MACOSX)
-}
-
-TEST_P(FileDescriptorWatcherTest, WatchReadableOneByte) {
- auto controller = WatchReadable();
-
- // Write 1 byte to the pipe, making it readable without blocking. Expect one
- // call to ReadableCallback() which will read 1 byte from the pipe.
- WriteByte();
- RunLoop run_loop;
- EXPECT_CALL(mock_, ReadableCallback())
- .WillOnce(testing::Invoke([this, &run_loop]() {
- ReadByte();
- run_loop.Quit();
- }));
- run_loop.Run();
- testing::Mock::VerifyAndClear(&mock_);
-
- // No more call to ReadableCallback() is expected.
- WaitAndRunPendingTasks();
-}
-
-TEST_P(FileDescriptorWatcherTest, WatchReadableTwoBytes) {
- auto controller = WatchReadable();
-
- // Write 2 bytes to the pipe. Expect two calls to ReadableCallback() which
- // will each read 1 byte from the pipe.
- WriteByte();
- WriteByte();
- RunLoop run_loop;
- EXPECT_CALL(mock_, ReadableCallback())
- .WillOnce(testing::Invoke([this]() { ReadByte(); }))
- .WillOnce(testing::Invoke([this, &run_loop]() {
- ReadByte();
- run_loop.Quit();
- }));
- run_loop.Run();
- testing::Mock::VerifyAndClear(&mock_);
-
- // No more call to ReadableCallback() is expected.
- WaitAndRunPendingTasks();
-}
-
-TEST_P(FileDescriptorWatcherTest, WatchReadableByteWrittenFromCallback) {
- auto controller = WatchReadable();
-
- // Write 1 byte to the pipe. Expect one call to ReadableCallback() from which
- // 1 byte is read and 1 byte is written to the pipe. Then, expect another call
- // to ReadableCallback() from which the remaining byte is read from the pipe.
- WriteByte();
- RunLoop run_loop;
- EXPECT_CALL(mock_, ReadableCallback())
- .WillOnce(testing::Invoke([this]() {
- ReadByte();
- WriteByte();
- }))
- .WillOnce(testing::Invoke([this, &run_loop]() {
- ReadByte();
- run_loop.Quit();
- }));
- run_loop.Run();
- testing::Mock::VerifyAndClear(&mock_);
-
- // No more call to ReadableCallback() is expected.
- WaitAndRunPendingTasks();
-}
-
-TEST_P(FileDescriptorWatcherTest, DeleteControllerFromCallback) {
- auto controller = WatchReadable();
-
- // Write 1 byte to the pipe. Expect one call to ReadableCallback() from which
- // |controller| is deleted.
- WriteByte();
- RunLoop run_loop;
- EXPECT_CALL(mock_, ReadableCallback())
- .WillOnce(testing::Invoke([&run_loop, &controller]() {
- controller = nullptr;
- run_loop.Quit();
- }));
- run_loop.Run();
- testing::Mock::VerifyAndClear(&mock_);
-
- // Since |controller| has been deleted, no call to ReadableCallback() is
- // expected even though the pipe is still readable without blocking.
- WaitAndRunPendingTasks();
-}
-
-TEST_P(FileDescriptorWatcherTest,
- DeleteControllerBeforeFileDescriptorReadable) {
- auto controller = WatchReadable();
-
- // Cancel the watch.
- controller = nullptr;
-
- // Write 1 byte to the pipe to make it readable without blocking.
- WriteByte();
-
- // No call to ReadableCallback() is expected.
- WaitAndRunPendingTasks();
-}
-
-TEST_P(FileDescriptorWatcherTest, DeleteControllerAfterFileDescriptorReadable) {
- auto controller = WatchReadable();
-
- // Write 1 byte to the pipe to make it readable without blocking.
- WriteByte();
-
- // Cancel the watch.
- controller = nullptr;
-
- // No call to ReadableCallback() is expected.
- WaitAndRunPendingTasks();
-}
-
-TEST_P(FileDescriptorWatcherTest, DeleteControllerAfterDeleteMessageLoopForIO) {
- auto controller = WatchReadable();
-
- // Delete the MessageLoopForIO.
- if (GetParam() ==
- FileDescriptorWatcherTestType::MESSAGE_LOOP_FOR_IO_ON_MAIN_THREAD) {
- message_loop_ = nullptr;
- } else {
- other_thread_.Stop();
- }
-
- // Deleting |controller| shouldn't crash even though that causes a task to be
- // posted to the MessageLoopForIO thread.
- controller = nullptr;
-}
-
-INSTANTIATE_TEST_CASE_P(
- MessageLoopForIOOnMainThread,
- FileDescriptorWatcherTest,
- ::testing::Values(
- FileDescriptorWatcherTestType::MESSAGE_LOOP_FOR_IO_ON_MAIN_THREAD));
-INSTANTIATE_TEST_CASE_P(
- MessageLoopForIOOnOtherThread,
- FileDescriptorWatcherTest,
- ::testing::Values(
- FileDescriptorWatcherTestType::MESSAGE_LOOP_FOR_IO_ON_OTHER_THREAD));
-
-} // namespace base
diff --git a/base/files/file_path.cc b/base/files/file_path.cc
index 9f67f9bc49..29f12a80aa 100644
--- a/base/files/file_path.cc
+++ b/base/files/file_path.cc
@@ -176,7 +176,6 @@ FilePath::FilePath() {
FilePath::FilePath(const FilePath& that) : path_(that.path_) {
}
-FilePath::FilePath(FilePath&& that) = default;
FilePath::FilePath(StringPieceType path) {
path.CopyToString(&path_);
@@ -193,8 +192,6 @@ FilePath& FilePath::operator=(const FilePath& that) {
return *this;
}
-FilePath& FilePath::operator=(FilePath&& that) = default;
-
bool FilePath::operator==(const FilePath& that) const {
#if defined(FILE_PATH_USES_DRIVE_LETTERS)
return EqualDriveLetterCaseInsensitive(this->path_, that.path_);
diff --git a/base/files/file_path.h b/base/files/file_path.h
index 02846f6892..3234df7bfb 100644
--- a/base/files/file_path.h
+++ b/base/files/file_path.h
@@ -182,13 +182,6 @@ class BASE_EXPORT FilePath {
~FilePath();
FilePath& operator=(const FilePath& that);
- // Constructs FilePath with the contents of |that|, which is left in valid but
- // unspecified state.
- FilePath(FilePath&& that);
- // Replaces the contents with those of |that|, which is left in valid but
- // unspecified state.
- FilePath& operator=(FilePath&& that);
-
bool operator==(const FilePath& that) const;
bool operator!=(const FilePath& that) const;
diff --git a/base/files/file_path_unittest.cc b/base/files/file_path_unittest.cc
index a091e62dd1..d8c5969513 100644
--- a/base/files/file_path_unittest.cc
+++ b/base/files/file_path_unittest.cc
@@ -9,6 +9,7 @@
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_locale.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
diff --git a/base/files/file_path_watcher.cc b/base/files/file_path_watcher.cc
index 245bd8efe2..a4624ab609 100644
--- a/base/files/file_path_watcher.cc
+++ b/base/files/file_path_watcher.cc
@@ -8,16 +8,22 @@
#include "base/files/file_path_watcher.h"
#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
#include "build/build_config.h"
namespace base {
FilePathWatcher::~FilePathWatcher() {
- DCHECK(sequence_checker_.CalledOnValidSequence());
impl_->Cancel();
}
// static
+void FilePathWatcher::CancelWatch(
+ const scoped_refptr<PlatformDelegate>& delegate) {
+ delegate->CancelOnMessageLoopThread();
+}
+
+// static
bool FilePathWatcher::RecursiveWatchAvailable() {
#if (defined(OS_MACOSX) && !defined(OS_IOS)) || defined(OS_WIN) || \
defined(OS_LINUX) || defined(OS_ANDROID)
@@ -38,7 +44,6 @@ FilePathWatcher::PlatformDelegate::~PlatformDelegate() {
bool FilePathWatcher::Watch(const FilePath& path,
bool recursive,
const Callback& callback) {
- DCHECK(sequence_checker_.CalledOnValidSequence());
DCHECK(path.IsAbsolute());
return impl_->Watch(path, recursive, callback);
}
diff --git a/base/files/file_path_watcher.h b/base/files/file_path_watcher.h
index 9e29d0a9d5..d5c6db1acf 100644
--- a/base/files/file_path_watcher.h
+++ b/base/files/file_path_watcher.h
@@ -7,15 +7,12 @@
#ifndef BASE_FILES_FILE_PATH_WATCHER_H_
#define BASE_FILES_FILE_PATH_WATCHER_H_
-#include <memory>
-
#include "base/base_export.h"
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
-#include "base/sequence_checker.h"
-#include "base/sequenced_task_runner.h"
+#include "base/single_thread_task_runner.h"
namespace base {
@@ -28,8 +25,6 @@ namespace base {
// detect the creation and deletion of files in a watched directory, but will
// not detect modifications to those files. See file_path_watcher_kqueue.cc for
// details.
-//
-// Must be destroyed on the sequence that invokes Watch().
class BASE_EXPORT FilePathWatcher {
public:
// Callback type for Watch(). |path| points to the file that was updated,
@@ -38,10 +33,9 @@ class BASE_EXPORT FilePathWatcher {
typedef base::Callback<void(const FilePath& path, bool error)> Callback;
// Used internally to encapsulate different members on different platforms.
- class PlatformDelegate {
+ class PlatformDelegate : public base::RefCountedThreadSafe<PlatformDelegate> {
public:
PlatformDelegate();
- virtual ~PlatformDelegate();
// Start watching for the given |path| and notify |delegate| about changes.
virtual bool Watch(const FilePath& path,
@@ -50,16 +44,25 @@ class BASE_EXPORT FilePathWatcher {
// Stop watching. This is called from FilePathWatcher's dtor in order to
// allow to shut down properly while the object is still alive.
+ // It can be called from any thread.
virtual void Cancel() = 0;
protected:
+ friend class base::RefCountedThreadSafe<PlatformDelegate>;
friend class FilePathWatcher;
- scoped_refptr<SequencedTaskRunner> task_runner() const {
+ virtual ~PlatformDelegate();
+
+ // Stop watching. This is only called on the thread of the appropriate
+ // message loop. Since it can also be called more than once, it should
+ // check |is_cancelled()| to avoid duplicate work.
+ virtual void CancelOnMessageLoopThread() = 0;
+
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner() const {
return task_runner_;
}
- void set_task_runner(scoped_refptr<SequencedTaskRunner> runner) {
+ void set_task_runner(scoped_refptr<base::SingleThreadTaskRunner> runner) {
task_runner_ = std::move(runner);
}
@@ -73,34 +76,32 @@ class BASE_EXPORT FilePathWatcher {
}
private:
- scoped_refptr<SequencedTaskRunner> task_runner_;
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
bool cancelled_;
-
- DISALLOW_COPY_AND_ASSIGN(PlatformDelegate);
};
FilePathWatcher();
- ~FilePathWatcher();
+ virtual ~FilePathWatcher();
+
+ // A callback that always cleans up the PlatformDelegate, either when executed
+ // or when deleted without having been executed at all, as can happen during
+ // shutdown.
+ static void CancelWatch(const scoped_refptr<PlatformDelegate>& delegate);
// Returns true if the platform and OS version support recursive watches.
static bool RecursiveWatchAvailable();
// Invokes |callback| whenever updates to |path| are detected. This should be
- // called at most once. Set |recursive| to true to watch |path| and its
- // children. The callback will be invoked on the same sequence. Returns true
- // on success.
- //
- // On POSIX, this must be called from a thread that supports
- // FileDescriptorWatcher.
+ // called at most once, and from a MessageLoop of TYPE_IO. Set |recursive| to
+ // true, to watch |path| and its children. The callback will be invoked on
+ // the same loop. Returns true on success.
//
// Recursive watch is not supported on all platforms and file systems.
// Watch() will return false in the case of failure.
bool Watch(const FilePath& path, bool recursive, const Callback& callback);
private:
- std::unique_ptr<PlatformDelegate> impl_;
-
- SequenceChecker sequence_checker_;
+ scoped_refptr<PlatformDelegate> impl_;
DISALLOW_COPY_AND_ASSIGN(FilePathWatcher);
};
diff --git a/base/files/file_path_watcher_fsevents.cc b/base/files/file_path_watcher_fsevents.cc
index e9a87b0e05..e9d25080e7 100644
--- a/base/files/file_path_watcher_fsevents.cc
+++ b/base/files/file_path_watcher_fsevents.cc
@@ -13,8 +13,10 @@
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/mac/scoped_cftyperef.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
#include "base/strings/stringprintf.h"
-#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/threading/thread_task_runner_handle.h"
namespace base {
@@ -68,21 +70,16 @@ FilePath ResolvePath(const FilePath& path) {
FilePathWatcherFSEvents::FilePathWatcherFSEvents()
: queue_(dispatch_queue_create(
- base::StringPrintf("org.chromium.base.FilePathWatcher.%p", this)
- .c_str(),
+ base::StringPrintf(
+ "org.chromium.base.FilePathWatcher.%p", this).c_str(),
DISPATCH_QUEUE_SERIAL)),
- fsevent_stream_(nullptr),
- weak_factory_(this) {}
-
-FilePathWatcherFSEvents::~FilePathWatcherFSEvents() {
- DCHECK(!task_runner() || task_runner()->RunsTasksOnCurrentThread());
- DCHECK(callback_.is_null())
- << "Cancel() must be called before FilePathWatcher is destroyed.";
+ fsevent_stream_(nullptr) {
}
bool FilePathWatcherFSEvents::Watch(const FilePath& path,
bool recursive,
const FilePathWatcher::Callback& callback) {
+ DCHECK(MessageLoopForIO::current());
DCHECK(!callback.is_null());
DCHECK(callback_.is_null());
@@ -91,7 +88,7 @@ bool FilePathWatcherFSEvents::Watch(const FilePath& path,
if (!recursive)
return false;
- set_task_runner(SequencedTaskRunnerHandle::Get());
+ set_task_runner(ThreadTaskRunnerHandle::Get());
callback_ = callback;
FSEventStreamEventId start_event = FSEventsGetCurrentEventId();
@@ -110,15 +107,11 @@ void FilePathWatcherFSEvents::Cancel() {
set_cancelled();
callback_.Reset();
- // Switch to the dispatch queue to tear down the event stream. As the queue is
- // owned by |this|, and this method is called from the destructor, execute the
- // block synchronously.
+ // Switch to the dispatch queue to tear down the event stream. As the queue
+ // is owned by this object, and this method is called from the destructor,
+ // execute the block synchronously.
dispatch_sync(queue_, ^{
- if (fsevent_stream_) {
- DestroyEventStream();
- target_.clear();
- resolved_target_.clear();
- }
+ CancelOnMessageLoopThread();
});
}
@@ -149,40 +142,31 @@ void FilePathWatcherFSEvents::FSEventsCallback(
// the directory to be watched gets created.
if (root_changed) {
// Resetting the event stream from within the callback fails (FSEvents spews
- // bad file descriptor errors), so do the reset asynchronously.
- //
- // We can't dispatch_async a call to UpdateEventStream() directly because
- // there would be no guarantee that |watcher| still exists when it runs.
- //
- // Instead, bounce on task_runner() and use a WeakPtr to verify that
- // |watcher| still exists. If it does, dispatch_async a call to
- // UpdateEventStream(). Because the destructor of |watcher| runs on
- // task_runner() and calls dispatch_sync, it is guaranteed that |watcher|
- // still exists when UpdateEventStream() runs.
- watcher->task_runner()->PostTask(
- FROM_HERE, Bind(
- [](WeakPtr<FilePathWatcherFSEvents> weak_watcher,
- FSEventStreamEventId root_change_at) {
- if (!weak_watcher)
- return;
- FilePathWatcherFSEvents* watcher = weak_watcher.get();
- dispatch_async(watcher->queue_, ^{
- watcher->UpdateEventStream(root_change_at);
- });
- },
- watcher->weak_factory_.GetWeakPtr(), root_change_at));
+ // bad file descriptor errors), so post a task to do the reset.
+ dispatch_async(watcher->queue_, ^{
+ watcher->UpdateEventStream(root_change_at);
+ });
}
watcher->OnFilePathsChanged(paths);
}
+FilePathWatcherFSEvents::~FilePathWatcherFSEvents() {
+ // This method may be called on either the libdispatch or task_runner()
+ // thread. Checking callback_ on the libdispatch thread here is safe because
+ // it is executing in a task posted by Cancel() which first reset callback_.
+ // PostTask forms a sufficient memory barrier to ensure that the value is
+ // consistent on the target thread.
+ DCHECK(callback_.is_null())
+ << "Cancel() must be called before FilePathWatcher is destroyed.";
+}
+
void FilePathWatcherFSEvents::OnFilePathsChanged(
const std::vector<FilePath>& paths) {
DCHECK(!resolved_target_.empty());
task_runner()->PostTask(
- FROM_HERE,
- Bind(&FilePathWatcherFSEvents::DispatchEvents, weak_factory_.GetWeakPtr(),
- paths, target_, resolved_target_));
+ FROM_HERE, Bind(&FilePathWatcherFSEvents::DispatchEvents, this, paths,
+ target_, resolved_target_));
}
void FilePathWatcherFSEvents::DispatchEvents(const std::vector<FilePath>& paths,
@@ -203,6 +187,18 @@ void FilePathWatcherFSEvents::DispatchEvents(const std::vector<FilePath>& paths,
}
}
+void FilePathWatcherFSEvents::CancelOnMessageLoopThread() {
+ // For all other implementations, the "message loop thread" is the IO thread,
+ // as returned by task_runner(). This implementation, however, needs to
+ // cancel pending work on the Dispatch Queue thread.
+
+ if (fsevent_stream_) {
+ DestroyEventStream();
+ target_.clear();
+ resolved_target_.clear();
+ }
+}
+
void FilePathWatcherFSEvents::UpdateEventStream(
FSEventStreamEventId start_event) {
// It can happen that the watcher gets canceled while tasks that call this
@@ -238,9 +234,8 @@ void FilePathWatcherFSEvents::UpdateEventStream(
FSEventStreamSetDispatchQueue(fsevent_stream_, queue_);
if (!FSEventStreamStart(fsevent_stream_)) {
- task_runner()->PostTask(FROM_HERE,
- Bind(&FilePathWatcherFSEvents::ReportError,
- weak_factory_.GetWeakPtr(), target_));
+ task_runner()->PostTask(
+ FROM_HERE, Bind(&FilePathWatcherFSEvents::ReportError, this, target_));
}
}
@@ -249,9 +244,8 @@ bool FilePathWatcherFSEvents::ResolveTargetPath() {
bool changed = resolved != resolved_target_;
resolved_target_ = resolved;
if (resolved_target_.empty()) {
- task_runner()->PostTask(FROM_HERE,
- Bind(&FilePathWatcherFSEvents::ReportError,
- weak_factory_.GetWeakPtr(), target_));
+ task_runner()->PostTask(
+ FROM_HERE, Bind(&FilePathWatcherFSEvents::ReportError, this, target_));
}
return changed;
}
diff --git a/base/files/file_path_watcher_fsevents.h b/base/files/file_path_watcher_fsevents.h
index dcdf2fbf9d..cfbe020b51 100644
--- a/base/files/file_path_watcher_fsevents.h
+++ b/base/files/file_path_watcher_fsevents.h
@@ -14,7 +14,6 @@
#include "base/files/file_path_watcher.h"
#include "base/mac/scoped_dispatch_object.h"
#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
namespace base {
@@ -27,7 +26,6 @@ namespace base {
class FilePathWatcherFSEvents : public FilePathWatcher::PlatformDelegate {
public:
FilePathWatcherFSEvents();
- ~FilePathWatcherFSEvents() override;
// FilePathWatcher::PlatformDelegate overrides.
bool Watch(const FilePath& path,
@@ -43,6 +41,8 @@ class FilePathWatcherFSEvents : public FilePathWatcher::PlatformDelegate {
const FSEventStreamEventFlags flags[],
const FSEventStreamEventId event_ids[]);
+ ~FilePathWatcherFSEvents() override;
+
// Called from FSEventsCallback whenever there is a change to the paths.
void OnFilePathsChanged(const std::vector<FilePath>& paths);
@@ -53,6 +53,9 @@ class FilePathWatcherFSEvents : public FilePathWatcher::PlatformDelegate {
const FilePath& target,
const FilePath& resolved_target);
+ // Cleans up and stops the event stream.
+ void CancelOnMessageLoopThread() override;
+
// (Re-)Initialize the event stream to start reporting events from
// |start_event|.
void UpdateEventStream(FSEventStreamEventId start_event);
@@ -89,8 +92,6 @@ class FilePathWatcherFSEvents : public FilePathWatcher::PlatformDelegate {
// (Only accessed from the libdispatch queue.)
FSEventStreamRef fsevent_stream_;
- WeakPtrFactory<FilePathWatcherFSEvents> weak_factory_;
-
DISALLOW_COPY_AND_ASSIGN(FilePathWatcherFSEvents);
};
diff --git a/base/files/file_path_watcher_kqueue.cc b/base/files/file_path_watcher_kqueue.cc
index a28726acb0..6d034cd9a2 100644
--- a/base/files/file_path_watcher_kqueue.cc
+++ b/base/files/file_path_watcher_kqueue.cc
@@ -12,7 +12,7 @@
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/strings/stringprintf.h"
-#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/threading/thread_task_runner_handle.h"
// On some platforms these are not defined.
#if !defined(EV_RECEIPT)
@@ -26,9 +26,7 @@ namespace base {
FilePathWatcherKQueue::FilePathWatcherKQueue() : kqueue_(-1) {}
-FilePathWatcherKQueue::~FilePathWatcherKQueue() {
- DCHECK(!task_runner() || task_runner()->RunsTasksOnCurrentThread());
-}
+FilePathWatcherKQueue::~FilePathWatcherKQueue() {}
void FilePathWatcherKQueue::ReleaseEvent(struct kevent& event) {
CloseFileDescriptor(&event.ident);
@@ -38,6 +36,7 @@ void FilePathWatcherKQueue::ReleaseEvent(struct kevent& event) {
}
int FilePathWatcherKQueue::EventsForPath(FilePath path, EventVector* events) {
+ DCHECK(MessageLoopForIO::current());
// Make sure that we are working with a clean slate.
DCHECK(events->empty());
@@ -231,74 +230,9 @@ bool FilePathWatcherKQueue::UpdateWatches(bool* target_file_affected) {
return true;
}
-bool FilePathWatcherKQueue::Watch(const FilePath& path,
- bool recursive,
- const FilePathWatcher::Callback& callback) {
- DCHECK(target_.value().empty()); // Can only watch one path.
- DCHECK(!callback.is_null());
- DCHECK_EQ(kqueue_, -1);
- // Recursive watch is not supported using kqueue.
- DCHECK(!recursive);
-
- callback_ = callback;
- target_ = path;
-
- set_task_runner(SequencedTaskRunnerHandle::Get());
-
- kqueue_ = kqueue();
- if (kqueue_ == -1) {
- DPLOG(ERROR) << "kqueue";
- return false;
- }
-
- int last_entry = EventsForPath(target_, &events_);
- DCHECK_NE(last_entry, 0);
-
- EventVector responses(last_entry);
-
- int count = HANDLE_EINTR(kevent(kqueue_, &events_[0], last_entry,
- &responses[0], last_entry, NULL));
- if (!AreKeventValuesValid(&responses[0], count)) {
- // Calling Cancel() here to close any file descriptors that were opened.
- // This would happen in the destructor anyways, but FilePathWatchers tend to
- // be long lived, and if an error has occurred, there is no reason to waste
- // the file descriptors.
- Cancel();
- return false;
- }
-
- // It's safe to use Unretained() because the watch is cancelled and the
- // callback cannot be invoked after |kqueue_watch_controller_| (which is a
- // member of |this|) has been deleted.
- kqueue_watch_controller_ = FileDescriptorWatcher::WatchReadable(
- kqueue_,
- Bind(&FilePathWatcherKQueue::OnKQueueReadable, Unretained(this)));
-
- return true;
-}
-
-void FilePathWatcherKQueue::Cancel() {
- if (!task_runner()) {
- set_cancelled();
- return;
- }
-
- DCHECK(task_runner()->RunsTasksOnCurrentThread());
- if (!is_cancelled()) {
- set_cancelled();
- kqueue_watch_controller_.reset();
- if (IGNORE_EINTR(close(kqueue_)) != 0) {
- DPLOG(ERROR) << "close kqueue";
- }
- kqueue_ = -1;
- std::for_each(events_.begin(), events_.end(), ReleaseEvent);
- events_.clear();
- callback_.Reset();
- }
-}
-
-void FilePathWatcherKQueue::OnKQueueReadable() {
- DCHECK(task_runner()->RunsTasksOnCurrentThread());
+void FilePathWatcherKQueue::OnFileCanReadWithoutBlocking(int fd) {
+ DCHECK(MessageLoopForIO::current());
+ DCHECK_EQ(fd, kqueue_);
DCHECK(events_.size());
// Request the file system update notifications that have occurred and return
@@ -369,4 +303,89 @@ void FilePathWatcherKQueue::OnKQueueReadable() {
}
}
+void FilePathWatcherKQueue::OnFileCanWriteWithoutBlocking(int /* fd */) {
+ NOTREACHED();
+}
+
+void FilePathWatcherKQueue::WillDestroyCurrentMessageLoop() {
+ CancelOnMessageLoopThread();
+}
+
+bool FilePathWatcherKQueue::Watch(const FilePath& path,
+ bool recursive,
+ const FilePathWatcher::Callback& callback) {
+ DCHECK(MessageLoopForIO::current());
+ DCHECK(target_.value().empty()); // Can only watch one path.
+ DCHECK(!callback.is_null());
+ DCHECK_EQ(kqueue_, -1);
+
+ if (recursive) {
+ // Recursive watch is not supported using kqueue.
+ NOTIMPLEMENTED();
+ return false;
+ }
+
+ callback_ = callback;
+ target_ = path;
+
+ MessageLoop::current()->AddDestructionObserver(this);
+ io_task_runner_ = ThreadTaskRunnerHandle::Get();
+
+ kqueue_ = kqueue();
+ if (kqueue_ == -1) {
+ DPLOG(ERROR) << "kqueue";
+ return false;
+ }
+
+ int last_entry = EventsForPath(target_, &events_);
+ DCHECK_NE(last_entry, 0);
+
+ EventVector responses(last_entry);
+
+ int count = HANDLE_EINTR(kevent(kqueue_, &events_[0], last_entry,
+ &responses[0], last_entry, NULL));
+ if (!AreKeventValuesValid(&responses[0], count)) {
+ // Calling Cancel() here to close any file descriptors that were opened.
+ // This would happen in the destructor anyways, but FilePathWatchers tend to
+ // be long lived, and if an error has occurred, there is no reason to waste
+ // the file descriptors.
+ Cancel();
+ return false;
+ }
+
+ return MessageLoopForIO::current()->WatchFileDescriptor(
+ kqueue_, true, MessageLoopForIO::WATCH_READ, &kqueue_watcher_, this);
+}
+
+void FilePathWatcherKQueue::Cancel() {
+ SingleThreadTaskRunner* task_runner = io_task_runner_.get();
+ if (!task_runner) {
+ set_cancelled();
+ return;
+ }
+ if (!task_runner->BelongsToCurrentThread()) {
+ task_runner->PostTask(FROM_HERE,
+ base::Bind(&FilePathWatcherKQueue::Cancel, this));
+ return;
+ }
+ CancelOnMessageLoopThread();
+}
+
+void FilePathWatcherKQueue::CancelOnMessageLoopThread() {
+ DCHECK(MessageLoopForIO::current());
+ if (!is_cancelled()) {
+ set_cancelled();
+ kqueue_watcher_.StopWatchingFileDescriptor();
+ if (IGNORE_EINTR(close(kqueue_)) != 0) {
+ DPLOG(ERROR) << "close kqueue";
+ }
+ kqueue_ = -1;
+ std::for_each(events_.begin(), events_.end(), ReleaseEvent);
+ events_.clear();
+ io_task_runner_ = NULL;
+ MessageLoop::current()->RemoveDestructionObserver(this);
+ callback_.Reset();
+ }
+}
+
} // namespace base
diff --git a/base/files/file_path_watcher_kqueue.h b/base/files/file_path_watcher_kqueue.h
index ef79be5596..d9db8c2587 100644
--- a/base/files/file_path_watcher_kqueue.h
+++ b/base/files/file_path_watcher_kqueue.h
@@ -6,14 +6,13 @@
#define BASE_FILES_FILE_PATH_WATCHER_KQUEUE_H_
#include <sys/event.h>
-
-#include <memory>
#include <vector>
-#include "base/files/file_descriptor_watcher_posix.h"
#include "base/files/file_path.h"
#include "base/files/file_path_watcher.h"
#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/single_thread_task_runner.h"
namespace base {
@@ -28,10 +27,18 @@ namespace base {
// detect the creation and deletion of files, just not the modification of
// files. It does however detect the attribute changes that the FSEvents impl
// would miss.
-class FilePathWatcherKQueue : public FilePathWatcher::PlatformDelegate {
+class FilePathWatcherKQueue : public FilePathWatcher::PlatformDelegate,
+ public MessageLoopForIO::Watcher,
+ public MessageLoop::DestructionObserver {
public:
FilePathWatcherKQueue();
- ~FilePathWatcherKQueue() override;
+
+ // MessageLoopForIO::Watcher overrides.
+ void OnFileCanReadWithoutBlocking(int fd) override;
+ void OnFileCanWriteWithoutBlocking(int fd) override;
+
+ // MessageLoop::DestructionObserver overrides.
+ void WillDestroyCurrentMessageLoop() override;
// FilePathWatcher::PlatformDelegate overrides.
bool Watch(const FilePath& path,
@@ -39,6 +46,9 @@ class FilePathWatcherKQueue : public FilePathWatcher::PlatformDelegate {
const FilePathWatcher::Callback& callback) override;
void Cancel() override;
+ protected:
+ ~FilePathWatcherKQueue() override;
+
private:
class EventData {
public:
@@ -50,8 +60,8 @@ class FilePathWatcherKQueue : public FilePathWatcher::PlatformDelegate {
typedef std::vector<struct kevent> EventVector;
- // Called when data is available in |kqueue_|.
- void OnKQueueReadable();
+ // Can only be called on |io_task_runner_|'s thread.
+ void CancelOnMessageLoopThread() override;
// Returns true if the kevent values are error free.
bool AreKeventValuesValid(struct kevent* kevents, int count);
@@ -109,14 +119,12 @@ class FilePathWatcherKQueue : public FilePathWatcher::PlatformDelegate {
}
EventVector events_;
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+ MessageLoopForIO::FileDescriptorWatcher kqueue_watcher_;
FilePathWatcher::Callback callback_;
FilePath target_;
int kqueue_;
- // Throughout the lifetime of this, OnKQueueReadable() will be called when
- // data is available in |kqueue_|.
- std::unique_ptr<FileDescriptorWatcher::Controller> kqueue_watch_controller_;
-
DISALLOW_COPY_AND_ASSIGN(FilePathWatcherKQueue);
};
diff --git a/base/files/file_path_watcher_linux.cc b/base/files/file_path_watcher_linux.cc
index 1dc833dc88..87bddd3dea 100644
--- a/base/files/file_path_watcher_linux.cc
+++ b/base/files/file_path_watcher_linux.cc
@@ -28,14 +28,12 @@
#include "base/location.h"
#include "base/logging.h"
#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/memory/weak_ptr.h"
#include "base/posix/eintr_wrapper.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/synchronization/lock.h"
-#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread.h"
+#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
namespace base {
@@ -63,14 +61,12 @@ class InotifyReader {
void OnInotifyEvent(const inotify_event* event);
private:
- friend struct LazyInstanceTraitsBase<InotifyReader>;
+ friend struct DefaultLazyInstanceTraits<InotifyReader>;
typedef std::set<FilePathWatcherImpl*> WatcherSet;
InotifyReader();
- // There is no destructor because |g_inotify_reader| is a
- // base::LazyInstace::Leaky object. Having a destructor causes build
- // issues with GCC 6 (http://crbug.com/636346).
+ ~InotifyReader();
// We keep track of which delegates want to be notified on which watches.
hash_map<Watch, WatcherSet> watchers_;
@@ -84,16 +80,19 @@ class InotifyReader {
// File descriptor returned by inotify_init.
const int inotify_fd_;
+ // Use self-pipe trick to unblock select during shutdown.
+ int shutdown_pipe_[2];
+
// Flag set to true when startup was successful.
bool valid_;
DISALLOW_COPY_AND_ASSIGN(InotifyReader);
};
-class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate {
+class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate,
+ public MessageLoop::DestructionObserver {
public:
FilePathWatcherImpl();
- ~FilePathWatcherImpl() override;
// Called for each event coming from the watch. |fired_watch| identifies the
// watch that fired, |child| indicates what has changed, and is relative to
@@ -108,13 +107,10 @@ class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate {
bool deleted,
bool is_dir);
- private:
- void OnFilePathChangedOnOriginSequence(InotifyReader::Watch fired_watch,
- const FilePath::StringType& child,
- bool created,
- bool deleted,
- bool is_dir);
+ protected:
+ ~FilePathWatcherImpl() override {}
+ private:
// Start watching |path| for changes and notify |delegate| on each change.
// Returns true if watch for |path| has been added successfully.
bool Watch(const FilePath& path,
@@ -124,6 +120,14 @@ class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate {
// Cancel the watch. This unregisters the instance with InotifyReader.
void Cancel() override;
+ // Cleans up and stops observing the message_loop() thread.
+ void CancelOnMessageLoopThread() override;
+
+ // Deletion of the FilePathWatcher will call Cancel() to dispose of this
+ // object in the right thread. This also observes destruction of the required
+ // cleanup thread, in case it quits before Cancel() is called.
+ void WillDestroyCurrentMessageLoop() override;
+
// Inotify watches are installed for all directory components of |target_|.
// A WatchEntry instance holds:
// - |watch|: the watch descriptor for a component.
@@ -187,15 +191,16 @@ class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate {
hash_map<InotifyReader::Watch, FilePath> recursive_paths_by_watch_;
std::map<FilePath, InotifyReader::Watch> recursive_watches_by_path_;
- WeakPtrFactory<FilePathWatcherImpl> weak_factory_;
-
DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl);
};
-void InotifyReaderCallback(InotifyReader* reader, int inotify_fd) {
+void InotifyReaderCallback(InotifyReader* reader, int inotify_fd,
+ int shutdown_fd) {
// Make sure the file descriptors are good for use with select().
CHECK_LE(0, inotify_fd);
CHECK_GT(FD_SETSIZE, inotify_fd);
+ CHECK_LE(0, shutdown_fd);
+ CHECK_GT(FD_SETSIZE, shutdown_fd);
trace_event::TraceLog::GetInstance()->SetCurrentThreadBlocksMessageLoop();
@@ -203,15 +208,20 @@ void InotifyReaderCallback(InotifyReader* reader, int inotify_fd) {
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(inotify_fd, &rfds);
+ FD_SET(shutdown_fd, &rfds);
// Wait until some inotify events are available.
int select_result =
- HANDLE_EINTR(select(inotify_fd + 1, &rfds, NULL, NULL, NULL));
+ HANDLE_EINTR(select(std::max(inotify_fd, shutdown_fd) + 1,
+ &rfds, NULL, NULL, NULL));
if (select_result < 0) {
DPLOG(WARNING) << "select failed";
return;
}
+ if (FD_ISSET(shutdown_fd, &rfds))
+ return;
+
// Adjust buffer size to current event queue size.
int buffer_size;
int ioctl_result = HANDLE_EINTR(ioctl(inotify_fd, FIONREAD,
@@ -253,14 +263,33 @@ InotifyReader::InotifyReader()
if (inotify_fd_ < 0)
PLOG(ERROR) << "inotify_init() failed";
- if (inotify_fd_ >= 0 && thread_.Start()) {
+ shutdown_pipe_[0] = -1;
+ shutdown_pipe_[1] = -1;
+ if (inotify_fd_ >= 0 && pipe(shutdown_pipe_) == 0 && thread_.Start()) {
thread_.task_runner()->PostTask(
FROM_HERE,
- Bind(&InotifyReaderCallback, this, inotify_fd_));
+ Bind(&InotifyReaderCallback, this, inotify_fd_, shutdown_pipe_[0]));
valid_ = true;
}
}
+InotifyReader::~InotifyReader() {
+ if (valid_) {
+ // Write to the self-pipe so that the select call in InotifyReaderTask
+ // returns.
+ ssize_t ret = HANDLE_EINTR(write(shutdown_pipe_[1], "", 1));
+ DPCHECK(ret > 0);
+ DCHECK_EQ(ret, 1);
+ thread_.Stop();
+ }
+ if (inotify_fd_ >= 0)
+ close(inotify_fd_);
+ if (shutdown_pipe_[0] >= 0)
+ close(shutdown_pipe_[0]);
+ if (shutdown_pipe_[1] >= 0)
+ close(shutdown_pipe_[1]);
+}
+
InotifyReader::Watch InotifyReader::AddWatch(
const FilePath& path, FilePathWatcherImpl* watcher) {
if (!valid_)
@@ -314,10 +343,7 @@ void InotifyReader::OnInotifyEvent(const inotify_event* event) {
}
FilePathWatcherImpl::FilePathWatcherImpl()
- : recursive_(false), weak_factory_(this) {}
-
-FilePathWatcherImpl::~FilePathWatcherImpl() {
- DCHECK(!task_runner() || task_runner()->RunsTasksOnCurrentThread());
+ : recursive_(false) {
}
void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch,
@@ -325,25 +351,22 @@ void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch,
bool created,
bool deleted,
bool is_dir) {
- DCHECK(!task_runner()->RunsTasksOnCurrentThread());
-
- // This method is invoked on the Inotify thread. Switch to task_runner() to
- // access |watches_| safely. Use a WeakPtr to prevent the callback from
- // running after |this| is destroyed (i.e. after the watch is cancelled).
- task_runner()->PostTask(
- FROM_HERE, Bind(&FilePathWatcherImpl::OnFilePathChangedOnOriginSequence,
- weak_factory_.GetWeakPtr(), fired_watch, child, created,
- deleted, is_dir));
-}
+ if (!task_runner()->BelongsToCurrentThread()) {
+ // Switch to task_runner() to access |watches_| safely.
+ task_runner()->PostTask(FROM_HERE,
+ Bind(&FilePathWatcherImpl::OnFilePathChanged, this,
+ fired_watch, child, created, deleted, is_dir));
+ return;
+ }
-void FilePathWatcherImpl::OnFilePathChangedOnOriginSequence(
- InotifyReader::Watch fired_watch,
- const FilePath::StringType& child,
- bool created,
- bool deleted,
- bool is_dir) {
- DCHECK(task_runner()->RunsTasksOnCurrentThread());
- DCHECK(!watches_.empty());
+ // Check to see if CancelOnMessageLoopThread() has already been called.
+ // May happen when code flow reaches here from the PostTask() above.
+ if (watches_.empty()) {
+ DCHECK(target_.empty());
+ return;
+ }
+
+ DCHECK(MessageLoopForIO::current());
DCHECK(HasValidWatchVector());
// Used below to avoid multiple recursive updates.
@@ -428,11 +451,13 @@ bool FilePathWatcherImpl::Watch(const FilePath& path,
bool recursive,
const FilePathWatcher::Callback& callback) {
DCHECK(target_.empty());
+ DCHECK(MessageLoopForIO::current());
- set_task_runner(SequencedTaskRunnerHandle::Get());
+ set_task_runner(ThreadTaskRunnerHandle::Get());
callback_ = callback;
target_ = path;
recursive_ = recursive;
+ MessageLoop::current()->AddDestructionObserver(this);
std::vector<FilePath::StringType> comps;
target_.GetComponents(&comps);
@@ -445,29 +470,47 @@ bool FilePathWatcherImpl::Watch(const FilePath& path,
}
void FilePathWatcherImpl::Cancel() {
- if (!callback_) {
- // Watch() was never called.
+ if (callback_.is_null()) {
+ // Watch was never called, or the message_loop() thread is already gone.
set_cancelled();
return;
}
- DCHECK(task_runner()->RunsTasksOnCurrentThread());
- DCHECK(!is_cancelled());
+ // Switch to the message_loop() if necessary so we can access |watches_|.
+ if (!task_runner()->BelongsToCurrentThread()) {
+ task_runner()->PostTask(FROM_HERE, Bind(&FilePathWatcher::CancelWatch,
+ make_scoped_refptr(this)));
+ } else {
+ CancelOnMessageLoopThread();
+ }
+}
+void FilePathWatcherImpl::CancelOnMessageLoopThread() {
+ DCHECK(task_runner()->BelongsToCurrentThread());
set_cancelled();
- callback_.Reset();
+
+ if (!callback_.is_null()) {
+ MessageLoop::current()->RemoveDestructionObserver(this);
+ callback_.Reset();
+ }
for (size_t i = 0; i < watches_.size(); ++i)
g_inotify_reader.Get().RemoveWatch(watches_[i].watch, this);
watches_.clear();
target_.clear();
- RemoveRecursiveWatches();
+
+ if (recursive_)
+ RemoveRecursiveWatches();
+}
+
+void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() {
+ CancelOnMessageLoopThread();
}
void FilePathWatcherImpl::UpdateWatches() {
- // Ensure this runs on the task_runner() exclusively in order to avoid
+ // Ensure this runs on the message_loop() exclusively in order to avoid
// concurrency issues.
- DCHECK(task_runner()->RunsTasksOnCurrentThread());
+ DCHECK(task_runner()->BelongsToCurrentThread());
DCHECK(HasValidWatchVector());
// Walk the list of watches and update them as we go.
@@ -498,8 +541,6 @@ void FilePathWatcherImpl::UpdateWatches() {
void FilePathWatcherImpl::UpdateRecursiveWatches(
InotifyReader::Watch fired_watch,
bool is_dir) {
- DCHECK(HasValidWatchVector());
-
if (!recursive_)
return;
@@ -510,8 +551,7 @@ void FilePathWatcherImpl::UpdateRecursiveWatches(
// Check to see if this is a forced update or if some component of |target_|
// has changed. For these cases, redo the watches for |target_| and below.
- if (!ContainsKey(recursive_paths_by_watch_, fired_watch) &&
- fired_watch != watches_.back().watch) {
+ if (!ContainsKey(recursive_paths_by_watch_, fired_watch)) {
UpdateRecursiveWatchesForPath(target_);
return;
}
@@ -520,10 +560,7 @@ void FilePathWatcherImpl::UpdateRecursiveWatches(
if (!is_dir)
return;
- const FilePath& changed_dir =
- ContainsKey(recursive_paths_by_watch_, fired_watch) ?
- recursive_paths_by_watch_[fired_watch] :
- target_;
+ const FilePath& changed_dir = recursive_paths_by_watch_[fired_watch];
std::map<FilePath, InotifyReader::Watch>::iterator start_it =
recursive_watches_by_path_.lower_bound(changed_dir);
@@ -646,8 +683,7 @@ bool FilePathWatcherImpl::HasValidWatchVector() const {
} // namespace
FilePathWatcher::FilePathWatcher() {
- sequence_checker_.DetachFromSequence();
- impl_ = MakeUnique<FilePathWatcherImpl>();
+ impl_ = new FilePathWatcherImpl();
}
} // namespace base
diff --git a/base/files/file_path_watcher_mac.cc b/base/files/file_path_watcher_mac.cc
new file mode 100644
index 0000000000..7338eafa44
--- /dev/null
+++ b/base/files/file_path_watcher_mac.cc
@@ -0,0 +1,61 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/file_path_watcher.h"
+#include "base/files/file_path_watcher_kqueue.h"
+#include "build/build_config.h"
+
+#if !defined(OS_IOS)
+#include "base/files/file_path_watcher_fsevents.h"
+#endif
+
+namespace base {
+
+namespace {
+
+class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate {
+ public:
+ bool Watch(const FilePath& path,
+ bool recursive,
+ const FilePathWatcher::Callback& callback) override {
+ // Use kqueue for non-recursive watches and FSEvents for recursive ones.
+ DCHECK(!impl_.get());
+ if (recursive) {
+ if (!FilePathWatcher::RecursiveWatchAvailable())
+ return false;
+#if !defined(OS_IOS)
+ impl_ = new FilePathWatcherFSEvents();
+#endif // OS_IOS
+ } else {
+ impl_ = new FilePathWatcherKQueue();
+ }
+ DCHECK(impl_.get());
+ return impl_->Watch(path, recursive, callback);
+ }
+
+ void Cancel() override {
+ if (impl_.get())
+ impl_->Cancel();
+ set_cancelled();
+ }
+
+ void CancelOnMessageLoopThread() override {
+ if (impl_.get())
+ impl_->Cancel();
+ set_cancelled();
+ }
+
+ protected:
+ ~FilePathWatcherImpl() override {}
+
+ scoped_refptr<PlatformDelegate> impl_;
+};
+
+} // namespace
+
+FilePathWatcher::FilePathWatcher() {
+ impl_ = new FilePathWatcherImpl();
+}
+
+} // namespace base
diff --git a/base/files/file_path_watcher_unittest.cc b/base/files/file_path_watcher_unittest.cc
index d2ec37bbec..a40e4858b4 100644
--- a/base/files/file_path_watcher_unittest.cc
+++ b/base/files/file_path_watcher_unittest.cc
@@ -28,6 +28,7 @@
#include "base/synchronization/waitable_event.h"
#include "base/test/test_file_util.h"
#include "base/test/test_timeouts.h"
+#include "base/threading/thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -36,10 +37,6 @@
#include "base/android/path_utils.h"
#endif // defined(OS_ANDROID)
-#if defined(OS_POSIX)
-#include "base/files/file_descriptor_watcher_posix.h"
-#endif // defined(OS_POSIX)
-
namespace base {
namespace {
@@ -134,19 +131,30 @@ class TestDelegate : public TestDelegateBase {
DISALLOW_COPY_AND_ASSIGN(TestDelegate);
};
+void SetupWatchCallback(const FilePath& target,
+ FilePathWatcher* watcher,
+ TestDelegateBase* delegate,
+ bool recursive_watch,
+ bool* result,
+ base::WaitableEvent* completion) {
+ *result = watcher->Watch(target, recursive_watch,
+ base::Bind(&TestDelegateBase::OnFileChanged,
+ delegate->AsWeakPtr()));
+ completion->Signal();
+}
+
class FilePathWatcherTest : public testing::Test {
public:
FilePathWatcherTest()
-#if defined(OS_POSIX)
- : file_descriptor_watcher_(&loop_)
-#endif
- {
- }
+ : file_thread_("FilePathWatcherTest") {}
~FilePathWatcherTest() override {}
protected:
void SetUp() override {
+ // Create a separate file thread in order to test proper thread usage.
+ base::Thread::Options options(MessageLoop::TYPE_IO, 0);
+ ASSERT_TRUE(file_thread_.StartWithOptions(options));
#if defined(OS_ANDROID)
// Watching files is only permitted when all parent directories are
// accessible, which is not the case for the default temp directory
@@ -163,12 +171,16 @@ class FilePathWatcherTest : public testing::Test {
void TearDown() override { RunLoop().RunUntilIdle(); }
+ void DeleteDelegateOnFileThread(TestDelegate* delegate) {
+ file_thread_.task_runner()->DeleteSoon(FROM_HERE, delegate);
+ }
+
FilePath test_file() {
- return temp_dir_.GetPath().AppendASCII("FilePathWatcherTest");
+ return temp_dir_.path().AppendASCII("FilePathWatcherTest");
}
FilePath test_link() {
- return temp_dir_.GetPath().AppendASCII("FilePathWatcherTest.lnk");
+ return temp_dir_.path().AppendASCII("FilePathWatcherTest.lnk");
}
// Write |content| to |file|. Returns true on success.
@@ -184,23 +196,18 @@ class FilePathWatcherTest : public testing::Test {
bool WaitForEvents() WARN_UNUSED_RESULT {
collector_->Reset();
-
- RunLoop run_loop;
// Make sure we timeout if we don't get notified.
- ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE, run_loop.QuitWhenIdleClosure(),
- TestTimeouts::action_timeout());
- run_loop.Run();
+ loop_.PostDelayedTask(FROM_HERE,
+ MessageLoop::QuitWhenIdleClosure(),
+ TestTimeouts::action_timeout());
+ RunLoop().Run();
return collector_->Success();
}
NotificationCollector* collector() { return collector_.get(); }
- MessageLoopForIO loop_;
-#if defined(OS_POSIX)
- FileDescriptorWatcher file_descriptor_watcher_;
-#endif
-
+ MessageLoop loop_;
+ base::Thread file_thread_;
ScopedTempDir temp_dir_;
scoped_refptr<NotificationCollector> collector_;
@@ -212,9 +219,14 @@ bool FilePathWatcherTest::SetupWatch(const FilePath& target,
FilePathWatcher* watcher,
TestDelegateBase* delegate,
bool recursive_watch) {
- return watcher->Watch(
- target, recursive_watch,
- base::Bind(&TestDelegateBase::OnFileChanged, delegate->AsWeakPtr()));
+ base::WaitableEvent completion(WaitableEvent::ResetPolicy::AUTOMATIC,
+ WaitableEvent::InitialState::NOT_SIGNALED);
+ bool result;
+ file_thread_.task_runner()->PostTask(
+ FROM_HERE, base::Bind(SetupWatchCallback, target, watcher, delegate,
+ recursive_watch, &result, &completion));
+ completion.Wait();
+ return result;
}
// Basic test: Create the file and verify that we notice.
@@ -225,6 +237,7 @@ TEST_F(FilePathWatcherTest, NewFile) {
ASSERT_TRUE(WriteFile(test_file(), "content"));
ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
}
// Verify that modifying the file is caught.
@@ -238,11 +251,12 @@ TEST_F(FilePathWatcherTest, ModifiedFile) {
// Now make sure we get notified if the file is modified.
ASSERT_TRUE(WriteFile(test_file(), "new content"));
ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
}
// Verify that moving the file into place is caught.
TEST_F(FilePathWatcherTest, MovedFile) {
- FilePath source_file(temp_dir_.GetPath().AppendASCII("source"));
+ FilePath source_file(temp_dir_.path().AppendASCII("source"));
ASSERT_TRUE(WriteFile(source_file, "content"));
FilePathWatcher watcher;
@@ -252,6 +266,7 @@ TEST_F(FilePathWatcherTest, MovedFile) {
// Now make sure we get notified if the file is modified.
ASSERT_TRUE(base::Move(source_file, test_file()));
ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
}
TEST_F(FilePathWatcherTest, DeletedFile) {
@@ -264,6 +279,7 @@ TEST_F(FilePathWatcherTest, DeletedFile) {
// Now make sure we get notified if the file is deleted.
base::DeleteFile(test_file(), false);
ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
}
// Used by the DeleteDuringNotify test below.
@@ -311,9 +327,11 @@ TEST_F(FilePathWatcherTest, DeleteDuringNotify) {
// Flaky on MacOS (and ARM linux): http://crbug.com/85930
TEST_F(FilePathWatcherTest, DISABLED_DestroyWithPendingNotification) {
std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
- FilePathWatcher watcher;
- ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false));
+ FilePathWatcher* watcher = new FilePathWatcher;
+ ASSERT_TRUE(SetupWatch(test_file(), watcher, delegate.get(), false));
ASSERT_TRUE(WriteFile(test_file(), "content"));
+ file_thread_.task_runner()->DeleteSoon(FROM_HERE, watcher);
+ DeleteDelegateOnFileThread(delegate.release());
}
TEST_F(FilePathWatcherTest, MultipleWatchersSingleFile) {
@@ -325,13 +343,15 @@ TEST_F(FilePathWatcherTest, MultipleWatchersSingleFile) {
ASSERT_TRUE(WriteFile(test_file(), "content"));
ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate1.release());
+ DeleteDelegateOnFileThread(delegate2.release());
}
// Verify that watching a file whose parent directory doesn't exist yet works if
// the directory and file are created eventually.
TEST_F(FilePathWatcherTest, NonExistentDirectory) {
FilePathWatcher watcher;
- FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
+ FilePath dir(temp_dir_.path().AppendASCII("dir"));
FilePath file(dir.AppendASCII("file"));
std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get(), false));
@@ -350,12 +370,13 @@ TEST_F(FilePathWatcherTest, NonExistentDirectory) {
ASSERT_TRUE(base::DeleteFile(file, false));
VLOG(1) << "Waiting for file deletion";
ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
}
// Exercises watch reconfiguration for the case that directories on the path
// are rapidly created.
TEST_F(FilePathWatcherTest, DirectoryChain) {
- FilePath path(temp_dir_.GetPath());
+ FilePath path(temp_dir_.path());
std::vector<std::string> dir_names;
for (int i = 0; i < 20; i++) {
std::string dir(base::StringPrintf("d%d", i));
@@ -368,7 +389,7 @@ TEST_F(FilePathWatcherTest, DirectoryChain) {
std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get(), false));
- FilePath sub_path(temp_dir_.GetPath());
+ FilePath sub_path(temp_dir_.path());
for (std::vector<std::string>::const_iterator d(dir_names.begin());
d != dir_names.end(); ++d) {
sub_path = sub_path.AppendASCII(*d);
@@ -382,6 +403,7 @@ TEST_F(FilePathWatcherTest, DirectoryChain) {
ASSERT_TRUE(WriteFile(file, "content v2"));
VLOG(1) << "Waiting for file modification";
ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
}
#if defined(OS_MACOSX)
@@ -390,7 +412,7 @@ TEST_F(FilePathWatcherTest, DirectoryChain) {
#endif
TEST_F(FilePathWatcherTest, DisappearingDirectory) {
FilePathWatcher watcher;
- FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
+ FilePath dir(temp_dir_.path().AppendASCII("dir"));
FilePath file(dir.AppendASCII("file"));
ASSERT_TRUE(base::CreateDirectory(dir));
ASSERT_TRUE(WriteFile(file, "content"));
@@ -399,6 +421,7 @@ TEST_F(FilePathWatcherTest, DisappearingDirectory) {
ASSERT_TRUE(base::DeleteFile(dir, true));
ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
}
// Tests that a file that is deleted and reappears is tracked correctly.
@@ -415,11 +438,12 @@ TEST_F(FilePathWatcherTest, DeleteAndRecreate) {
ASSERT_TRUE(WriteFile(test_file(), "content"));
VLOG(1) << "Waiting for file creation";
ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
}
TEST_F(FilePathWatcherTest, WatchDirectory) {
FilePathWatcher watcher;
- FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
+ FilePath dir(temp_dir_.path().AppendASCII("dir"));
FilePath file1(dir.AppendASCII("file1"));
FilePath file2(dir.AppendASCII("file2"));
std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
@@ -447,13 +471,14 @@ TEST_F(FilePathWatcherTest, WatchDirectory) {
ASSERT_TRUE(WriteFile(file2, "content"));
VLOG(1) << "Waiting for file2 creation";
ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
}
TEST_F(FilePathWatcherTest, MoveParent) {
FilePathWatcher file_watcher;
FilePathWatcher subdir_watcher;
- FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
- FilePath dest(temp_dir_.GetPath().AppendASCII("dest"));
+ FilePath dir(temp_dir_.path().AppendASCII("dir"));
+ FilePath dest(temp_dir_.path().AppendASCII("dest"));
FilePath subdir(dir.AppendASCII("subdir"));
FilePath file(subdir.AppendASCII("file"));
std::unique_ptr<TestDelegate> file_delegate(new TestDelegate(collector()));
@@ -472,15 +497,18 @@ TEST_F(FilePathWatcherTest, MoveParent) {
base::Move(dir, dest);
VLOG(1) << "Waiting for directory move";
ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(file_delegate.release());
+ DeleteDelegateOnFileThread(subdir_delegate.release());
}
TEST_F(FilePathWatcherTest, RecursiveWatch) {
FilePathWatcher watcher;
- FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
+ FilePath dir(temp_dir_.path().AppendASCII("dir"));
std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
bool setup_result = SetupWatch(dir, &watcher, delegate.get(), true);
if (!FilePathWatcher::RecursiveWatchAvailable()) {
ASSERT_FALSE(setup_result);
+ DeleteDelegateOnFileThread(delegate.release());
return;
}
ASSERT_TRUE(setup_result);
@@ -536,6 +564,7 @@ TEST_F(FilePathWatcherTest, RecursiveWatch) {
// Delete "$dir/subdir/subdir_child_dir/child_dir_file1".
ASSERT_TRUE(base::DeleteFile(child_dir_file1, false));
ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
}
#if defined(OS_POSIX)
@@ -552,14 +581,14 @@ TEST_F(FilePathWatcherTest, RecursiveWithSymLink) {
return;
FilePathWatcher watcher;
- FilePath test_dir(temp_dir_.GetPath().AppendASCII("test_dir"));
+ FilePath test_dir(temp_dir_.path().AppendASCII("test_dir"));
ASSERT_TRUE(base::CreateDirectory(test_dir));
FilePath symlink(test_dir.AppendASCII("symlink"));
std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
ASSERT_TRUE(SetupWatch(symlink, &watcher, delegate.get(), true));
// Link creation.
- FilePath target1(temp_dir_.GetPath().AppendASCII("target1"));
+ FilePath target1(temp_dir_.path().AppendASCII("target1"));
ASSERT_TRUE(base::CreateSymbolicLink(target1, symlink));
ASSERT_TRUE(WaitForEvents());
@@ -573,7 +602,7 @@ TEST_F(FilePathWatcherTest, RecursiveWithSymLink) {
ASSERT_TRUE(WaitForEvents());
// Link change.
- FilePath target2(temp_dir_.GetPath().AppendASCII("target2"));
+ FilePath target2(temp_dir_.path().AppendASCII("target2"));
ASSERT_TRUE(base::CreateDirectory(target2));
ASSERT_TRUE(base::DeleteFile(symlink, false));
ASSERT_TRUE(base::CreateSymbolicLink(target2, symlink));
@@ -583,16 +612,18 @@ TEST_F(FilePathWatcherTest, RecursiveWithSymLink) {
FilePath target2_file(target2.AppendASCII("file"));
ASSERT_TRUE(WriteFile(target2_file, "content"));
ASSERT_TRUE(WaitForEvents());
+
+ DeleteDelegateOnFileThread(delegate.release());
}
#endif // OS_POSIX
TEST_F(FilePathWatcherTest, MoveChild) {
FilePathWatcher file_watcher;
FilePathWatcher subdir_watcher;
- FilePath source_dir(temp_dir_.GetPath().AppendASCII("source"));
+ FilePath source_dir(temp_dir_.path().AppendASCII("source"));
FilePath source_subdir(source_dir.AppendASCII("subdir"));
FilePath source_file(source_subdir.AppendASCII("file"));
- FilePath dest_dir(temp_dir_.GetPath().AppendASCII("dest"));
+ FilePath dest_dir(temp_dir_.path().AppendASCII("dest"));
FilePath dest_subdir(dest_dir.AppendASCII("subdir"));
FilePath dest_file(dest_subdir.AppendASCII("file"));
@@ -609,6 +640,8 @@ TEST_F(FilePathWatcherTest, MoveChild) {
// Move the directory into place, s.t. the watched file appears.
ASSERT_TRUE(base::Move(source_dir, dest_dir));
ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(file_delegate.release());
+ DeleteDelegateOnFileThread(subdir_delegate.release());
}
// Verify that changing attributes on a file is caught
@@ -629,6 +662,7 @@ TEST_F(FilePathWatcherTest, FileAttributesChanged) {
// Now make sure we get notified if the file is modified.
ASSERT_TRUE(base::MakeFileUnreadable(test_file()));
ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
}
#if defined(OS_LINUX)
@@ -644,6 +678,7 @@ TEST_F(FilePathWatcherTest, CreateLink) {
// Note that test_file() doesn't have to exist.
ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
}
// Verify that deleting a symlink is caught.
@@ -659,6 +694,7 @@ TEST_F(FilePathWatcherTest, DeleteLink) {
// Now make sure we get notified if the link is deleted.
ASSERT_TRUE(base::DeleteFile(test_link(), false));
ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
}
// Verify that modifying a target file that a link is pointing to
@@ -674,6 +710,7 @@ TEST_F(FilePathWatcherTest, ModifiedLinkedFile) {
// Now make sure we get notified if the file is modified.
ASSERT_TRUE(WriteFile(test_file(), "new content"));
ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
}
// Verify that creating a target file that a link is pointing to
@@ -688,6 +725,7 @@ TEST_F(FilePathWatcherTest, CreateTargetLinkedFile) {
// Now make sure we get notified if the target file is created.
ASSERT_TRUE(WriteFile(test_file(), "content"));
ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
}
// Verify that deleting a target file that a link is pointing to
@@ -703,14 +741,15 @@ TEST_F(FilePathWatcherTest, DeleteTargetLinkedFile) {
// Now make sure we get notified if the target file is deleted.
ASSERT_TRUE(base::DeleteFile(test_file(), false));
ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
}
// Verify that watching a file whose parent directory is a link that
// doesn't exist yet works if the symlink is created eventually.
TEST_F(FilePathWatcherTest, LinkedDirectoryPart1) {
FilePathWatcher watcher;
- FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
- FilePath link_dir(temp_dir_.GetPath().AppendASCII("dir.lnk"));
+ FilePath dir(temp_dir_.path().AppendASCII("dir"));
+ FilePath link_dir(temp_dir_.path().AppendASCII("dir.lnk"));
FilePath file(dir.AppendASCII("file"));
FilePath linkfile(link_dir.AppendASCII("file"));
std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
@@ -731,14 +770,15 @@ TEST_F(FilePathWatcherTest, LinkedDirectoryPart1) {
ASSERT_TRUE(base::DeleteFile(file, false));
VLOG(1) << "Waiting for file deletion";
ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
}
// Verify that watching a file whose parent directory is a
// dangling symlink works if the directory is created eventually.
TEST_F(FilePathWatcherTest, LinkedDirectoryPart2) {
FilePathWatcher watcher;
- FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
- FilePath link_dir(temp_dir_.GetPath().AppendASCII("dir.lnk"));
+ FilePath dir(temp_dir_.path().AppendASCII("dir"));
+ FilePath link_dir(temp_dir_.path().AppendASCII("dir.lnk"));
FilePath file(dir.AppendASCII("file"));
FilePath linkfile(link_dir.AppendASCII("file"));
std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
@@ -760,14 +800,15 @@ TEST_F(FilePathWatcherTest, LinkedDirectoryPart2) {
ASSERT_TRUE(base::DeleteFile(file, false));
VLOG(1) << "Waiting for file deletion";
ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
}
// Verify that watching a file with a symlink on the path
// to the file works.
TEST_F(FilePathWatcherTest, LinkedDirectoryPart3) {
FilePathWatcher watcher;
- FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
- FilePath link_dir(temp_dir_.GetPath().AppendASCII("dir.lnk"));
+ FilePath dir(temp_dir_.path().AppendASCII("dir"));
+ FilePath link_dir(temp_dir_.path().AppendASCII("dir.lnk"));
FilePath file(dir.AppendASCII("file"));
FilePath linkfile(link_dir.AppendASCII("file"));
std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
@@ -787,6 +828,7 @@ TEST_F(FilePathWatcherTest, LinkedDirectoryPart3) {
ASSERT_TRUE(base::DeleteFile(file, false));
VLOG(1) << "Waiting for file deletion";
ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
}
#endif // OS_LINUX
@@ -837,8 +879,7 @@ bool ChangeFilePermissions(const FilePath& path, Permission perm, bool allow) {
// Verify that changing attributes on a directory works.
TEST_F(FilePathWatcherTest, DirAttributesChanged) {
- FilePath test_dir1(
- temp_dir_.GetPath().AppendASCII("DirAttributesChangedDir1"));
+ FilePath test_dir1(temp_dir_.path().AppendASCII("DirAttributesChangedDir1"));
FilePath test_dir2(test_dir1.AppendASCII("DirAttributesChangedDir2"));
FilePath test_file(test_dir2.AppendASCII("DirAttributesChangedFile"));
// Setup a directory hierarchy.
@@ -864,6 +905,7 @@ TEST_F(FilePathWatcherTest, DirAttributesChanged) {
ASSERT_TRUE(ChangeFilePermissions(test_dir1, Execute, false));
ASSERT_TRUE(WaitForEvents());
ASSERT_TRUE(ChangeFilePermissions(test_dir1, Execute, true));
+ DeleteDelegateOnFileThread(delegate.release());
}
#endif // OS_MACOSX
diff --git a/base/files/file_posix.cc b/base/files/file_posix.cc
index 2738d6c45c..12f80c4f8f 100644
--- a/base/files/file_posix.cc
+++ b/base/files/file_posix.cc
@@ -11,7 +11,7 @@
#include <unistd.h>
#include "base/logging.h"
-#include "base/metrics/histogram_macros.h"
+#include "base/metrics/sparse_histogram.h"
#include "base/posix/eintr_wrapper.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
@@ -323,7 +323,7 @@ int64_t File::GetLength() {
stat_wrapper_t file_info;
if (CallFstat(file_.get(), &file_info))
- return -1;
+ return false;
return file_info.st_size;
}
@@ -372,7 +372,7 @@ File::Error File::Unlock() {
return CallFcntlFlock(file_.get(), false);
}
-File File::Duplicate() const {
+File File::Duplicate() {
if (!IsValid())
return File();
@@ -513,10 +513,9 @@ void File::DoInitialize(const FilePath& path, uint32_t flags) {
}
#endif // !defined(OS_NACL)
-bool File::Flush() {
+bool File::DoFlush() {
ThreadRestrictions::AssertIOAllowed();
DCHECK(IsValid());
- SCOPED_FILE_TRACE("Flush");
#if defined(OS_NACL)
NOTIMPLEMENTED(); // NaCl doesn't implement fsync.
diff --git a/base/files/file_tracing.cc b/base/files/file_tracing.cc
index 48f57412f9..6d11cbc746 100644
--- a/base/files/file_tracing.cc
+++ b/base/files/file_tracing.cc
@@ -4,62 +4,47 @@
#include "base/files/file_tracing.h"
-#include "base/atomicops.h"
#include "base/files/file.h"
-using base::subtle::AtomicWord;
-
namespace base {
namespace {
-AtomicWord g_provider;
-}
-
-FileTracing::Provider* GetProvider() {
- AtomicWord provider = base::subtle::Acquire_Load(&g_provider);
- return reinterpret_cast<FileTracing::Provider*>(provider);
+FileTracing::Provider* g_provider = nullptr;
}
// static
bool FileTracing::IsCategoryEnabled() {
- FileTracing::Provider* provider = GetProvider();
- return provider && provider->FileTracingCategoryIsEnabled();
+ return g_provider && g_provider->FileTracingCategoryIsEnabled();
}
// static
void FileTracing::SetProvider(FileTracing::Provider* provider) {
- base::subtle::Release_Store(&g_provider,
- reinterpret_cast<AtomicWord>(provider));
+ g_provider = provider;
}
FileTracing::ScopedEnabler::ScopedEnabler() {
- FileTracing::Provider* provider = GetProvider();
- if (provider)
- provider->FileTracingEnable(this);
+ if (g_provider)
+ g_provider->FileTracingEnable(this);
}
FileTracing::ScopedEnabler::~ScopedEnabler() {
- FileTracing::Provider* provider = GetProvider();
- if (provider)
- provider->FileTracingDisable(this);
+ if (g_provider)
+ g_provider->FileTracingDisable(this);
}
FileTracing::ScopedTrace::ScopedTrace() : id_(nullptr) {}
FileTracing::ScopedTrace::~ScopedTrace() {
- if (id_) {
- FileTracing::Provider* provider = GetProvider();
- if (provider)
- provider->FileTracingEventEnd(name_, id_);
- }
+ if (id_ && g_provider)
+ g_provider->FileTracingEventEnd(name_, id_);
}
void FileTracing::ScopedTrace::Initialize(const char* name,
- const File* file,
+ File* file,
int64_t size) {
id_ = &file->trace_enabler_;
name_ = name;
- GetProvider()->FileTracingEventBegin(name_, id_, file->tracing_path_, size);
+ g_provider->FileTracingEventBegin(name_, id_, file->tracing_path_, size);
}
} // namespace base
diff --git a/base/files/file_tracing.h b/base/files/file_tracing.h
index 1fbfcd4498..bedd7be64b 100644
--- a/base/files/file_tracing.h
+++ b/base/files/file_tracing.h
@@ -37,21 +37,21 @@ class BASE_EXPORT FileTracing {
virtual bool FileTracingCategoryIsEnabled() const = 0;
// Enables file tracing for |id|. Must be called before recording events.
- virtual void FileTracingEnable(const void* id) = 0;
+ virtual void FileTracingEnable(void* id) = 0;
// Disables file tracing for |id|.
- virtual void FileTracingDisable(const void* id) = 0;
+ virtual void FileTracingDisable(void* id) = 0;
// Begins an event for |id| with |name|. |path| tells where in the directory
// structure the event is happening (and may be blank). |size| is the number
// of bytes involved in the event.
virtual void FileTracingEventBegin(const char* name,
- const void* id,
+ void* id,
const FilePath& path,
int64_t size) = 0;
// Ends an event for |id| with |name|.
- virtual void FileTracingEventEnd(const char* name, const void* id) = 0;
+ virtual void FileTracingEventEnd(const char* name, void* id) = 0;
};
// Sets a global file tracing provider to query categories and record events.
@@ -73,12 +73,12 @@ class BASE_EXPORT FileTracing {
// event to trace (e.g. "Read", "Write") and must have an application
// lifetime (e.g. static or literal). |file| is the file being traced; must
// outlive this class. |size| is the size (in bytes) of this event.
- void Initialize(const char* name, const File* file, int64_t size);
+ void Initialize(const char* name, File* file, int64_t size);
private:
// The ID of this trace. Based on the |file| passed to |Initialize()|. Must
// outlive this class.
- const void* id_;
+ void* id_;
// The name of the event to trace (e.g. "Read", "Write"). Prefixed with
// "File".
diff --git a/base/files/file_unittest.cc b/base/files/file_unittest.cc
index 66c312b60d..2445f7e128 100644
--- a/base/files/file_unittest.cc
+++ b/base/files/file_unittest.cc
@@ -9,7 +9,6 @@
#include <utility>
#include "base/files/file_util.h"
-#include "base/files/memory_mapped_file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/time/time.h"
#include "build/build_config.h"
@@ -21,7 +20,7 @@ using base::FilePath;
TEST(FileTest, Create) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath file_path = temp_dir.GetPath().AppendASCII("create_file_1");
+ FilePath file_path = temp_dir.path().AppendASCII("create_file_1");
{
// Don't create a File at all.
@@ -93,7 +92,7 @@ TEST(FileTest, Create) {
{
// Create a delete-on-close file.
- file_path = temp_dir.GetPath().AppendASCII("create_file_2");
+ file_path = temp_dir.path().AppendASCII("create_file_2");
File file(file_path,
base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ |
base::File::FLAG_DELETE_ON_CLOSE);
@@ -108,7 +107,7 @@ TEST(FileTest, Create) {
TEST(FileTest, Async) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath file_path = temp_dir.GetPath().AppendASCII("create_file");
+ FilePath file_path = temp_dir.path().AppendASCII("create_file");
{
File file(file_path, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_ASYNC);
@@ -126,7 +125,7 @@ TEST(FileTest, Async) {
TEST(FileTest, DeleteOpenFile) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath file_path = temp_dir.GetPath().AppendASCII("create_file_1");
+ FilePath file_path = temp_dir.path().AppendASCII("create_file_1");
// Create a file.
File file(file_path,
@@ -153,7 +152,7 @@ TEST(FileTest, DeleteOpenFile) {
TEST(FileTest, ReadWrite) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath file_path = temp_dir.GetPath().AppendASCII("read_write_file");
+ FilePath file_path = temp_dir.path().AppendASCII("read_write_file");
File file(file_path,
base::File::FLAG_CREATE | base::File::FLAG_READ |
base::File::FLAG_WRITE);
@@ -225,7 +224,7 @@ TEST(FileTest, ReadWrite) {
TEST(FileTest, Append) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath file_path = temp_dir.GetPath().AppendASCII("append_file");
+ FilePath file_path = temp_dir.path().AppendASCII("append_file");
File file(file_path, base::File::FLAG_CREATE | base::File::FLAG_APPEND);
ASSERT_TRUE(file.IsValid());
@@ -273,7 +272,7 @@ TEST(FileTest, Append) {
TEST(FileTest, Length) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath file_path = temp_dir.GetPath().AppendASCII("truncate_file");
+ FilePath file_path = temp_dir.path().AppendASCII("truncate_file");
File file(file_path,
base::File::FLAG_CREATE | base::File::FLAG_READ |
base::File::FLAG_WRITE);
@@ -325,7 +324,7 @@ TEST(FileTest, DISABLED_TouchGetInfo) {
#endif
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- File file(temp_dir.GetPath().AppendASCII("touch_get_info_file"),
+ File file(temp_dir.path().AppendASCII("touch_get_info_file"),
base::File::FLAG_CREATE | base::File::FLAG_WRITE |
base::File::FLAG_WRITE_ATTRIBUTES);
ASSERT_TRUE(file.IsValid());
@@ -388,8 +387,7 @@ TEST(FileTest, DISABLED_TouchGetInfo) {
TEST(FileTest, ReadAtCurrentPosition) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath file_path =
- temp_dir.GetPath().AppendASCII("read_at_current_position");
+ FilePath file_path = temp_dir.path().AppendASCII("read_at_current_position");
File file(file_path,
base::File::FLAG_CREATE | base::File::FLAG_READ |
base::File::FLAG_WRITE);
@@ -413,8 +411,7 @@ TEST(FileTest, ReadAtCurrentPosition) {
TEST(FileTest, WriteAtCurrentPosition) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath file_path =
- temp_dir.GetPath().AppendASCII("write_at_current_position");
+ FilePath file_path = temp_dir.path().AppendASCII("write_at_current_position");
File file(file_path,
base::File::FLAG_CREATE | base::File::FLAG_READ |
base::File::FLAG_WRITE);
@@ -437,7 +434,7 @@ TEST(FileTest, WriteAtCurrentPosition) {
TEST(FileTest, Seek) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath file_path = temp_dir.GetPath().AppendASCII("seek_file");
+ FilePath file_path = temp_dir.path().AppendASCII("seek_file");
File file(file_path,
base::File::FLAG_CREATE | base::File::FLAG_READ |
base::File::FLAG_WRITE);
@@ -454,7 +451,7 @@ TEST(FileTest, Seek) {
TEST(FileTest, Duplicate) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath file_path = temp_dir.GetPath().AppendASCII("file");
+ FilePath file_path = temp_dir.path().AppendASCII("file");
File file(file_path,(base::File::FLAG_CREATE |
base::File::FLAG_READ |
base::File::FLAG_WRITE));
@@ -481,7 +478,7 @@ TEST(FileTest, Duplicate) {
TEST(FileTest, DuplicateDeleteOnClose) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath file_path = temp_dir.GetPath().AppendASCII("file");
+ FilePath file_path = temp_dir.path().AppendASCII("file");
File file(file_path,(base::File::FLAG_CREATE |
base::File::FLAG_READ |
base::File::FLAG_WRITE |
@@ -498,8 +495,7 @@ TEST(FileTest, DuplicateDeleteOnClose) {
TEST(FileTest, GetInfoForDirectory) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath empty_dir =
- temp_dir.GetPath().Append(FILE_PATH_LITERAL("gpfi_test"));
+ FilePath empty_dir = temp_dir.path().Append(FILE_PATH_LITERAL("gpfi_test"));
ASSERT_TRUE(CreateDirectory(empty_dir));
base::File dir(
@@ -518,158 +514,4 @@ TEST(FileTest, GetInfoForDirectory) {
EXPECT_FALSE(info.is_symbolic_link);
EXPECT_EQ(0, info.size);
}
-
-TEST(FileTest, DeleteNoop) {
- base::ScopedTempDir temp_dir;
- ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath file_path = temp_dir.GetPath().AppendASCII("file");
-
- // Creating and closing a file with DELETE perms should do nothing special.
- File file(file_path,
- (base::File::FLAG_CREATE | base::File::FLAG_READ |
- base::File::FLAG_WRITE | base::File::FLAG_CAN_DELETE_ON_CLOSE));
- ASSERT_TRUE(file.IsValid());
- file.Close();
- ASSERT_TRUE(base::PathExists(file_path));
-}
-
-TEST(FileTest, Delete) {
- base::ScopedTempDir temp_dir;
- ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath file_path = temp_dir.GetPath().AppendASCII("file");
-
- // Creating a file with DELETE and then marking for delete on close should
- // delete it.
- File file(file_path,
- (base::File::FLAG_CREATE | base::File::FLAG_READ |
- base::File::FLAG_WRITE | base::File::FLAG_CAN_DELETE_ON_CLOSE));
- ASSERT_TRUE(file.IsValid());
- ASSERT_TRUE(file.DeleteOnClose(true));
- file.Close();
- ASSERT_FALSE(base::PathExists(file_path));
-}
-
-TEST(FileTest, DeleteThenRevoke) {
- base::ScopedTempDir temp_dir;
- ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath file_path = temp_dir.GetPath().AppendASCII("file");
-
- // Creating a file with DELETE, marking it for delete, then clearing delete on
- // close should not delete it.
- File file(file_path,
- (base::File::FLAG_CREATE | base::File::FLAG_READ |
- base::File::FLAG_WRITE | base::File::FLAG_CAN_DELETE_ON_CLOSE));
- ASSERT_TRUE(file.IsValid());
- ASSERT_TRUE(file.DeleteOnClose(true));
- ASSERT_TRUE(file.DeleteOnClose(false));
- file.Close();
- ASSERT_TRUE(base::PathExists(file_path));
-}
-
-TEST(FileTest, IrrevokableDeleteOnClose) {
- base::ScopedTempDir temp_dir;
- ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath file_path = temp_dir.GetPath().AppendASCII("file");
-
- // DELETE_ON_CLOSE cannot be revoked by this opener.
- File file(
- file_path,
- (base::File::FLAG_CREATE | base::File::FLAG_READ |
- base::File::FLAG_WRITE | base::File::FLAG_DELETE_ON_CLOSE |
- base::File::FLAG_SHARE_DELETE | base::File::FLAG_CAN_DELETE_ON_CLOSE));
- ASSERT_TRUE(file.IsValid());
- // https://msdn.microsoft.com/library/windows/desktop/aa364221.aspx says that
- // setting the dispositon has no effect if the handle was opened with
- // FLAG_DELETE_ON_CLOSE. Do not make the test's success dependent on whether
- // or not SetFileInformationByHandle indicates success or failure. (It happens
- // to indicate success on Windows 10.)
- file.DeleteOnClose(false);
- file.Close();
- ASSERT_FALSE(base::PathExists(file_path));
-}
-
-TEST(FileTest, IrrevokableDeleteOnCloseOther) {
- base::ScopedTempDir temp_dir;
- ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath file_path = temp_dir.GetPath().AppendASCII("file");
-
- // DELETE_ON_CLOSE cannot be revoked by another opener.
- File file(
- file_path,
- (base::File::FLAG_CREATE | base::File::FLAG_READ |
- base::File::FLAG_WRITE | base::File::FLAG_DELETE_ON_CLOSE |
- base::File::FLAG_SHARE_DELETE | base::File::FLAG_CAN_DELETE_ON_CLOSE));
- ASSERT_TRUE(file.IsValid());
-
- File file2(
- file_path,
- (base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE |
- base::File::FLAG_SHARE_DELETE | base::File::FLAG_CAN_DELETE_ON_CLOSE));
- ASSERT_TRUE(file2.IsValid());
-
- file2.DeleteOnClose(false);
- file2.Close();
- ASSERT_TRUE(base::PathExists(file_path));
- file.Close();
- ASSERT_FALSE(base::PathExists(file_path));
-}
-
-TEST(FileTest, DeleteWithoutPermission) {
- base::ScopedTempDir temp_dir;
- ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath file_path = temp_dir.GetPath().AppendASCII("file");
-
- // It should not be possible to mark a file for deletion when it was not
- // created/opened with DELETE.
- File file(file_path, (base::File::FLAG_CREATE | base::File::FLAG_READ |
- base::File::FLAG_WRITE));
- ASSERT_TRUE(file.IsValid());
- ASSERT_FALSE(file.DeleteOnClose(true));
- file.Close();
- ASSERT_TRUE(base::PathExists(file_path));
-}
-
-TEST(FileTest, UnsharedDeleteOnClose) {
- base::ScopedTempDir temp_dir;
- ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath file_path = temp_dir.GetPath().AppendASCII("file");
-
- // Opening with DELETE_ON_CLOSE when a previous opener hasn't enabled sharing
- // will fail.
- File file(file_path, (base::File::FLAG_CREATE | base::File::FLAG_READ |
- base::File::FLAG_WRITE));
- ASSERT_TRUE(file.IsValid());
- File file2(
- file_path,
- (base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE |
- base::File::FLAG_DELETE_ON_CLOSE | base::File::FLAG_SHARE_DELETE));
- ASSERT_FALSE(file2.IsValid());
-
- file.Close();
- ASSERT_TRUE(base::PathExists(file_path));
-}
-
-TEST(FileTest, NoDeleteOnCloseWithMappedFile) {
- base::ScopedTempDir temp_dir;
- ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath file_path = temp_dir.GetPath().AppendASCII("file");
-
- // Mapping a file into memory blocks DeleteOnClose.
- File file(file_path,
- (base::File::FLAG_CREATE | base::File::FLAG_READ |
- base::File::FLAG_WRITE | base::File::FLAG_CAN_DELETE_ON_CLOSE));
- ASSERT_TRUE(file.IsValid());
- ASSERT_EQ(5, file.WriteAtCurrentPos("12345", 5));
-
- {
- base::MemoryMappedFile mapping;
- ASSERT_TRUE(mapping.Initialize(file.Duplicate()));
- ASSERT_EQ(5U, mapping.length());
-
- EXPECT_FALSE(file.DeleteOnClose(true));
- }
-
- file.Close();
- ASSERT_TRUE(base::PathExists(file_path));
-}
#endif // defined(OS_WIN)
diff --git a/base/files/file_util.h b/base/files/file_util.h
index 5ada35f9a4..420dcaee61 100644
--- a/base/files/file_util.h
+++ b/base/files/file_util.h
@@ -294,6 +294,10 @@ BASE_EXPORT bool DevicePathToDriveLetterPath(const FilePath& device_path,
// be resolved with this function.
BASE_EXPORT bool NormalizeToNativeFilePath(const FilePath& path,
FilePath* nt_path);
+
+// Given an existing file in |path|, returns whether this file is on a network
+// drive or not. If |path| does not exist, this function returns false.
+BASE_EXPORT bool IsOnNetworkDrive(const base::FilePath& path);
#endif
// This function will return if the given file is a symlink or not.
@@ -307,9 +311,7 @@ BASE_EXPORT bool TouchFile(const FilePath& path,
const Time& last_accessed,
const Time& last_modified);
-// Wrapper for fopen-like calls. Returns non-NULL FILE* on success. The
-// underlying file descriptor (POSIX) or handle (Windows) is unconditionally
-// configured to not be propagated to child processes.
+// Wrapper for fopen-like calls. Returns non-NULL FILE* on success.
BASE_EXPORT FILE* OpenFile(const FilePath& filename, const char* mode);
// Closes file opened by OpenFile. Returns true on success.
@@ -363,17 +365,6 @@ BASE_EXPORT int GetUniquePathNumber(const FilePath& path,
BASE_EXPORT bool SetNonBlocking(int fd);
#if defined(OS_POSIX)
-// Creates a non-blocking, close-on-exec pipe.
-// This creates a non-blocking pipe that is not intended to be shared with any
-// child process. This will be done atomically if the operating system supports
-// it. Returns true if it was able to create the pipe, otherwise false.
-BASE_EXPORT bool CreateLocalNonBlockingPipe(int fds[2]);
-
-// Sets the given |fd| to close-on-exec mode.
-// Returns true if it was able to set it in the close-on-exec mode, otherwise
-// false.
-BASE_EXPORT bool SetCloseOnExec(int fd);
-
// Test that |path| can only be changed by a given user and members of
// a given set of groups.
// Specifically, test that all parts of |path| under (and including) |base|:
diff --git a/base/files/file_util_mac.mm b/base/files/file_util_mac.mm
index 5a99aa0e81..e9c6c65159 100644
--- a/base/files/file_util_mac.mm
+++ b/base/files/file_util_mac.mm
@@ -4,9 +4,8 @@
#include "base/files/file_util.h"
-#import <Foundation/Foundation.h>
#include <copyfile.h>
-#include <stdlib.h>
+#import <Foundation/Foundation.h>
#include "base/files/file_path.h"
#include "base/mac/foundation_util.h"
@@ -24,15 +23,6 @@ bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
}
bool GetTempDir(base::FilePath* path) {
- // In order to facilitate hermetic runs on macOS, first check $TMPDIR.
- // NOTE: $TMPDIR is ALMOST ALWAYS set on macOS (unless the user un-set it).
- const char* env_tmpdir = getenv("TMPDIR");
- if (env_tmpdir) {
- *path = base::FilePath(env_tmpdir);
- return true;
- }
-
- // If we didn't find it, fall back to the native function.
NSString* tmp = NSTemporaryDirectory();
if (tmp == nil)
return false;
diff --git a/base/files/file_util_posix.cc b/base/files/file_util_posix.cc
index a03ca8d8d8..85a1b41d46 100644
--- a/base/files/file_util_posix.cc
+++ b/base/files/file_util_posix.cc
@@ -185,19 +185,6 @@ bool DetermineDevShmExecutable() {
#endif // defined(OS_LINUX)
#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)
@@ -289,8 +276,11 @@ bool CopyDirectory(const FilePath& from_path,
FilePath real_from_path = MakeAbsoluteFilePath(from_path);
if (real_from_path.empty())
return false;
- if (real_to_path == real_from_path || real_from_path.IsParent(real_to_path))
+ if (real_to_path.value().size() >= real_from_path.value().size() &&
+ real_to_path.value().compare(0, real_from_path.value().size(),
+ real_from_path.value()) == 0) {
return false;
+ }
int traverse_type = FileEnumerator::FILES | FileEnumerator::SHOW_SYM_LINKS;
if (recursive)
@@ -361,29 +351,6 @@ bool CopyDirectory(const FilePath& from_path,
}
#endif // !defined(OS_NACL_NONSFI)
-bool CreateLocalNonBlockingPipe(int fds[2]) {
-#if defined(OS_LINUX)
- return pipe2(fds, O_CLOEXEC | O_NONBLOCK) == 0;
-#else
- int raw_fds[2];
- if (pipe(raw_fds) != 0)
- return false;
- ScopedFD fd_out(raw_fds[0]);
- ScopedFD fd_in(raw_fds[1]);
- if (!SetCloseOnExec(fd_out.get()))
- return false;
- if (!SetCloseOnExec(fd_in.get()))
- return false;
- if (!SetNonBlocking(fd_out.get()))
- return false;
- if (!SetNonBlocking(fd_in.get()))
- return false;
- fds[0] = fd_out.release();
- fds[1] = fd_in.release();
- return true;
-#endif
-}
-
bool SetNonBlocking(int fd) {
const int flags = fcntl(fd, F_GETFL);
if (flags == -1)
@@ -395,21 +362,6 @@ bool SetNonBlocking(int fd) {
return true;
}
-bool SetCloseOnExec(int fd) {
-#if defined(OS_NACL_NONSFI)
- const int flags = 0;
-#else
- const int flags = fcntl(fd, F_GETFD);
- if (flags == -1)
- return false;
- if (flags & FD_CLOEXEC)
- return true;
-#endif // defined(OS_NACL_NONSFI)
- if (HANDLE_EINTR(fcntl(fd, F_SETFD, flags | FD_CLOEXEC)) == -1)
- return false;
- return true;
-}
-
bool PathExists(const FilePath& path) {
ThreadRestrictions::AssertIOAllowed();
#if defined(OS_ANDROID)
@@ -725,29 +677,11 @@ bool GetFileInfo(const FilePath& file_path, File::Info* results) {
#endif // !defined(OS_NACL_NONSFI)
FILE* OpenFile(const FilePath& filename, const char* mode) {
- // 'e' is unconditionally added below, so be sure there is not one already
- // present before a comma in |mode|.
- DCHECK(
- strchr(mode, 'e') == nullptr ||
- (strchr(mode, ',') != nullptr && strchr(mode, 'e') > strchr(mode, ',')));
ThreadRestrictions::AssertIOAllowed();
FILE* result = NULL;
-#if defined(OS_MACOSX)
- // macOS does not provide a mode character to set O_CLOEXEC; see
- // https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/fopen.3.html.
- const char* the_mode = mode;
-#else
- std::string mode_with_e(AppendModeCharacter(mode, 'e'));
- const char* the_mode = mode_with_e.c_str();
-#endif
do {
- result = fopen(filename.value().c_str(), the_mode);
+ result = fopen(filename.value().c_str(), mode);
} while (!result && errno == EINTR);
-#if defined(OS_MACOSX)
- // Mark the descriptor as close-on-exec.
- if (result)
- SetCloseOnExec(fileno(result));
-#endif
return result;
}
diff --git a/base/files/important_file_writer.cc b/base/files/important_file_writer.cc
index b46846277b..28550ad52f 100644
--- a/base/files/important_file_writer.cc
+++ b/base/files/important_file_writer.cc
@@ -18,7 +18,7 @@
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/macros.h"
-#include "base/metrics/histogram_macros.h"
+#include "base/metrics/histogram.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
@@ -57,18 +57,9 @@ void LogFailure(const FilePath& path, TempFileFailure failure_code,
// Helper function to call WriteFileAtomically() with a
// std::unique_ptr<std::string>.
-void WriteScopedStringToFileAtomically(
- const FilePath& path,
- std::unique_ptr<std::string> data,
- Closure before_write_callback,
- Callback<void(bool success)> after_write_callback) {
- if (!before_write_callback.is_null())
- before_write_callback.Run();
-
- bool result = ImportantFileWriter::WriteFileAtomically(path, *data);
-
- if (!after_write_callback.is_null())
- after_write_callback.Run(result);
+bool WriteScopedStringToFileAtomically(const FilePath& path,
+ std::unique_ptr<std::string> data) {
+ return ImportantFileWriter::WriteFileAtomically(path, *data);
}
} // namespace
@@ -103,7 +94,6 @@ bool ImportantFileWriter::WriteFileAtomically(const FilePath& path,
File tmp_file(tmp_file_path, File::FLAG_OPEN | File::FLAG_WRITE);
if (!tmp_file.IsValid()) {
LogFailure(path, FAILED_OPENING, "could not open temporary file");
- DeleteFile(tmp_file_path, false);
return false;
}
@@ -178,11 +168,8 @@ void ImportantFileWriter::WriteNow(std::unique_ptr<std::string> data) {
if (HasPendingWrite())
timer_.Stop();
- Closure task = Bind(&WriteScopedStringToFileAtomically, path_, Passed(&data),
- Passed(&before_next_write_callback_),
- Passed(&after_next_write_callback_));
-
- if (!task_runner_->PostTask(FROM_HERE, MakeCriticalClosure(task))) {
+ auto task = Bind(&WriteScopedStringToFileAtomically, path_, Passed(&data));
+ if (!PostWriteTask(task)) {
// Posting the task to background message loop is not expected
// to fail, but if it does, avoid losing data and just hit the disk
// on the current thread.
@@ -216,11 +203,37 @@ void ImportantFileWriter::DoScheduledWrite() {
serializer_ = nullptr;
}
-void ImportantFileWriter::RegisterOnNextWriteCallbacks(
- const Closure& before_next_write_callback,
- const Callback<void(bool success)>& after_next_write_callback) {
- before_next_write_callback_ = before_next_write_callback;
- after_next_write_callback_ = after_next_write_callback;
+void ImportantFileWriter::RegisterOnNextSuccessfulWriteCallback(
+ const Closure& on_next_successful_write) {
+ DCHECK(on_next_successful_write_.is_null());
+ on_next_successful_write_ = on_next_successful_write;
+}
+
+bool ImportantFileWriter::PostWriteTask(const Callback<bool()>& task) {
+ // TODO(gab): This code could always use PostTaskAndReplyWithResult and let
+ // ForwardSuccessfulWrite() no-op if |on_next_successful_write_| is null, but
+ // PostTaskAndReply causes memory leaks in tests (crbug.com/371974) and
+ // suppressing all of those is unrealistic hence we avoid most of them by
+ // using PostTask() in the typical scenario below.
+ if (!on_next_successful_write_.is_null()) {
+ return PostTaskAndReplyWithResult(
+ task_runner_.get(),
+ FROM_HERE,
+ MakeCriticalClosure(task),
+ Bind(&ImportantFileWriter::ForwardSuccessfulWrite,
+ weak_factory_.GetWeakPtr()));
+ }
+ return task_runner_->PostTask(
+ FROM_HERE,
+ MakeCriticalClosure(Bind(IgnoreResult(task))));
+}
+
+void ImportantFileWriter::ForwardSuccessfulWrite(bool result) {
+ DCHECK(CalledOnValidThread());
+ if (result && !on_next_successful_write_.is_null()) {
+ on_next_successful_write_.Run();
+ on_next_successful_write_.Reset();
+ }
}
} // namespace base
diff --git a/base/files/important_file_writer.h b/base/files/important_file_writer.h
index f154b043b2..0bd8a7fd35 100644
--- a/base/files/important_file_writer.h
+++ b/base/files/important_file_writer.h
@@ -20,21 +20,24 @@
namespace base {
class SequencedTaskRunner;
+class Thread;
-// Helper for atomically writing a file to ensure that it won't be corrupted by
-// *application* crash during write (implemented as create, flush, rename).
+// Helper to ensure that a file won't be corrupted by the write (for example on
+// application crash). Consider a naive way to save an important file F:
//
-// As an added benefit, ImportantFileWriter makes it less likely that the file
-// is corrupted by *system* crash, though even if the ImportantFileWriter call
-// has already returned at the time of the crash it is not specified which
-// version of the file (old or new) is preserved. And depending on system
-// configuration (hardware and software) a significant likelihood of file
-// corruption may remain, thus using ImportantFileWriter is not a valid
-// substitute for file integrity checks and recovery codepaths for malformed
-// files.
+// 1. Open F for writing, truncating it.
+// 2. Write new data to F.
//
-// Also note that ImportantFileWriter can be *really* slow (cf. File::Flush()
-// for details) and thus please don't block shutdown on ImportantFileWriter.
+// It's good when it works, but it gets very bad if step 2. doesn't complete.
+// It can be caused by a crash, a computer hang, or a weird I/O error. And you
+// end up with a broken file.
+//
+// To be safe, we don't start with writing directly to F. Instead, we write to
+// to a temporary file. Only after that write is successful, we rename the
+// temporary file to target filename.
+//
+// If you want to know more about this approach and ext3/ext4 fsync issues, see
+// http://blog.valerieaurora.org/2009/04/16/dont-panic-fsync-ext34-and-your-data/
class BASE_EXPORT ImportantFileWriter : public NonThreadSafe {
public:
// Used by ScheduleSave to lazily provide the data to be saved. Allows us
@@ -50,9 +53,8 @@ class BASE_EXPORT ImportantFileWriter : public NonThreadSafe {
virtual ~DataSerializer() {}
};
- // Save |data| to |path| in an atomic manner. Blocks and writes data on the
- // current thread. Does not guarantee file integrity across system crash (see
- // the class comment above).
+ // Save |data| to |path| in an atomic manner (see the class comment above).
+ // Blocks and writes data on the current thread.
static bool WriteFileAtomically(const FilePath& path, StringPiece data);
// Initialize the writer.
@@ -93,26 +95,25 @@ class BASE_EXPORT ImportantFileWriter : public NonThreadSafe {
// Serialize data pending to be saved and execute write on backend thread.
void DoScheduledWrite();
- // Registers |before_next_write_callback| and |after_next_write_callback| to
- // be synchronously invoked from WriteFileAtomically() before its next write
- // and after its next write, respectively. The boolean passed to
- // |after_next_write_callback| indicates whether the write was successful.
- // Both callbacks must be thread safe as they will be called on |task_runner_|
- // and may be called during Chrome shutdown.
- // If called more than once before a write is scheduled on |task_runner|, the
- // latest callbacks clobber the others.
- void RegisterOnNextWriteCallbacks(
- const Closure& before_next_write_callback,
- const Callback<void(bool success)>& after_next_write_callback);
+ // Registers |on_next_successful_write| to be called once, on the next
+ // successful write event. Only one callback can be set at once.
+ void RegisterOnNextSuccessfulWriteCallback(
+ const Closure& on_next_successful_write);
TimeDelta commit_interval() const {
return commit_interval_;
}
private:
- // Invoked synchronously on the next write event.
- Closure before_next_write_callback_;
- Callback<void(bool success)> after_next_write_callback_;
+ // Helper method for WriteNow().
+ bool PostWriteTask(const Callback<bool()>& task);
+
+ // If |result| is true and |on_next_successful_write_| is set, invokes
+ // |on_successful_write_| and then resets it; no-ops otherwise.
+ void ForwardSuccessfulWrite(bool result);
+
+ // Invoked once and then reset on the next successful write event.
+ Closure on_next_successful_write_;
// Path being written to.
const FilePath path_;
diff --git a/base/files/important_file_writer_unittest.cc b/base/files/important_file_writer_unittest.cc
index 9b8dcfd4e3..43e051ebcf 100644
--- a/base/files/important_file_writer_unittest.cc
+++ b/base/files/important_file_writer_unittest.cc
@@ -46,61 +46,39 @@ class DataSerializer : public ImportantFileWriter::DataSerializer {
const std::string data_;
};
-enum WriteCallbackObservationState {
- NOT_CALLED,
- CALLED_WITH_ERROR,
- CALLED_WITH_SUCCESS,
-};
-
-class WriteCallbacksObserver {
+class SuccessfulWriteObserver {
public:
- WriteCallbacksObserver() = default;
+ SuccessfulWriteObserver() : successful_write_observed_(false) {}
- // Register OnBeforeWrite() and OnAfterWrite() to be called on the next write
+ // Register on_successful_write() to be called on the next successful write
// of |writer|.
- void ObserveNextWriteCallbacks(ImportantFileWriter* writer);
+ void ObserveNextSuccessfulWrite(ImportantFileWriter* writer);
- // Returns the |WriteCallbackObservationState| which was observed, then resets
- // it to |NOT_CALLED|.
- WriteCallbackObservationState GetAndResetObservationState();
+ // Returns true if a successful write was observed via on_successful_write()
+ // and resets the observation state to false regardless.
+ bool GetAndResetObservationState();
private:
- void OnBeforeWrite() {
- EXPECT_FALSE(before_write_called_);
- before_write_called_ = true;
- }
-
- void OnAfterWrite(bool success) {
- EXPECT_EQ(NOT_CALLED, after_write_observation_state_);
- after_write_observation_state_ =
- success ? CALLED_WITH_SUCCESS : CALLED_WITH_ERROR;
+ void on_successful_write() {
+ EXPECT_FALSE(successful_write_observed_);
+ successful_write_observed_ = true;
}
- bool before_write_called_ = false;
- WriteCallbackObservationState after_write_observation_state_ = NOT_CALLED;
+ bool successful_write_observed_;
- DISALLOW_COPY_AND_ASSIGN(WriteCallbacksObserver);
+ DISALLOW_COPY_AND_ASSIGN(SuccessfulWriteObserver);
};
-void WriteCallbacksObserver::ObserveNextWriteCallbacks(
+void SuccessfulWriteObserver::ObserveNextSuccessfulWrite(
ImportantFileWriter* writer) {
- writer->RegisterOnNextWriteCallbacks(
- base::Bind(&WriteCallbacksObserver::OnBeforeWrite,
- base::Unretained(this)),
- base::Bind(&WriteCallbacksObserver::OnAfterWrite,
- base::Unretained(this)));
+ writer->RegisterOnNextSuccessfulWriteCallback(base::Bind(
+ &SuccessfulWriteObserver::on_successful_write, base::Unretained(this)));
}
-WriteCallbackObservationState
-WriteCallbacksObserver::GetAndResetObservationState() {
- EXPECT_EQ(after_write_observation_state_ != NOT_CALLED, before_write_called_)
- << "The before-write callback should always be called before the "
- "after-write callback";
-
- WriteCallbackObservationState state = after_write_observation_state_;
- before_write_called_ = false;
- after_write_observation_state_ = NOT_CALLED;
- return state;
+bool SuccessfulWriteObserver::GetAndResetObservationState() {
+ bool was_successful_write_observed = successful_write_observed_;
+ successful_write_observed_ = false;
+ return was_successful_write_observed;
}
} // namespace
@@ -110,11 +88,11 @@ class ImportantFileWriterTest : public testing::Test {
ImportantFileWriterTest() { }
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
- file_ = temp_dir_.GetPath().AppendASCII("test-file");
+ file_ = temp_dir_.path().AppendASCII("test-file");
}
protected:
- WriteCallbacksObserver write_callback_observer_;
+ SuccessfulWriteObserver successful_write_observer_;
FilePath file_;
MessageLoop loop_;
@@ -125,102 +103,49 @@ class ImportantFileWriterTest : public testing::Test {
TEST_F(ImportantFileWriterTest, Basic) {
ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get());
EXPECT_FALSE(PathExists(writer.path()));
- EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
- writer.WriteNow(MakeUnique<std::string>("foo"));
+ EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState());
+ writer.WriteNow(WrapUnique(new std::string("foo")));
RunLoop().RunUntilIdle();
- EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
+ EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState());
ASSERT_TRUE(PathExists(writer.path()));
EXPECT_EQ("foo", GetFileContent(writer.path()));
}
-TEST_F(ImportantFileWriterTest, WriteWithObserver) {
+TEST_F(ImportantFileWriterTest, BasicWithSuccessfulWriteObserver) {
ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get());
EXPECT_FALSE(PathExists(writer.path()));
- EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
-
- // Confirm that the observer is invoked.
- write_callback_observer_.ObserveNextWriteCallbacks(&writer);
- writer.WriteNow(MakeUnique<std::string>("foo"));
+ EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState());
+ successful_write_observer_.ObserveNextSuccessfulWrite(&writer);
+ writer.WriteNow(WrapUnique(new std::string("foo")));
RunLoop().RunUntilIdle();
- EXPECT_EQ(CALLED_WITH_SUCCESS,
- write_callback_observer_.GetAndResetObservationState());
+ // Confirm that the observer is invoked.
+ EXPECT_TRUE(successful_write_observer_.GetAndResetObservationState());
ASSERT_TRUE(PathExists(writer.path()));
EXPECT_EQ("foo", GetFileContent(writer.path()));
// Confirm that re-installing the observer works for another write.
- EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
- write_callback_observer_.ObserveNextWriteCallbacks(&writer);
- writer.WriteNow(MakeUnique<std::string>("bar"));
+ EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState());
+ successful_write_observer_.ObserveNextSuccessfulWrite(&writer);
+ writer.WriteNow(WrapUnique(new std::string("bar")));
RunLoop().RunUntilIdle();
- EXPECT_EQ(CALLED_WITH_SUCCESS,
- write_callback_observer_.GetAndResetObservationState());
+ EXPECT_TRUE(successful_write_observer_.GetAndResetObservationState());
ASSERT_TRUE(PathExists(writer.path()));
EXPECT_EQ("bar", GetFileContent(writer.path()));
// Confirm that writing again without re-installing the observer doesn't
// result in a notification.
- EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
- writer.WriteNow(MakeUnique<std::string>("baz"));
+ EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState());
+ writer.WriteNow(WrapUnique(new std::string("baz")));
RunLoop().RunUntilIdle();
- EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
+ EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState());
ASSERT_TRUE(PathExists(writer.path()));
EXPECT_EQ("baz", GetFileContent(writer.path()));
}
-TEST_F(ImportantFileWriterTest, FailedWriteWithObserver) {
- // Use an invalid file path (relative paths are invalid) to get a
- // FILE_ERROR_ACCESS_DENIED error when trying to write the file.
- ImportantFileWriter writer(FilePath().AppendASCII("bad/../path"),
- ThreadTaskRunnerHandle::Get());
- EXPECT_FALSE(PathExists(writer.path()));
- EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
- write_callback_observer_.ObserveNextWriteCallbacks(&writer);
- writer.WriteNow(MakeUnique<std::string>("foo"));
- RunLoop().RunUntilIdle();
-
- // Confirm that the write observer was invoked with its boolean parameter set
- // to false.
- EXPECT_EQ(CALLED_WITH_ERROR,
- write_callback_observer_.GetAndResetObservationState());
- EXPECT_FALSE(PathExists(writer.path()));
-}
-
-TEST_F(ImportantFileWriterTest, CallbackRunsOnWriterThread) {
- base::Thread file_writer_thread("ImportantFileWriter test thread");
- file_writer_thread.Start();
- ImportantFileWriter writer(file_, file_writer_thread.task_runner());
- EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
-
- // Block execution on |file_writer_thread| to verify that callbacks are
- // executed on it.
- base::WaitableEvent wait_helper(
- base::WaitableEvent::ResetPolicy::MANUAL,
- base::WaitableEvent::InitialState::NOT_SIGNALED);
- file_writer_thread.task_runner()->PostTask(
- FROM_HERE,
- base::Bind(&base::WaitableEvent::Wait, base::Unretained(&wait_helper)));
-
- write_callback_observer_.ObserveNextWriteCallbacks(&writer);
- writer.WriteNow(MakeUnique<std::string>("foo"));
- RunLoop().RunUntilIdle();
-
- // Expect the callback to not have been executed before the
- // |file_writer_thread| is unblocked.
- EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
-
- wait_helper.Signal();
- file_writer_thread.FlushForTesting();
-
- EXPECT_EQ(CALLED_WITH_SUCCESS,
- write_callback_observer_.GetAndResetObservationState());
- ASSERT_TRUE(PathExists(writer.path()));
- EXPECT_EQ("foo", GetFileContent(writer.path()));
-}
-
TEST_F(ImportantFileWriterTest, ScheduleWrite) {
ImportantFileWriter writer(file_,
ThreadTaskRunnerHandle::Get(),
diff --git a/base/files/memory_mapped_file_posix.cc b/base/files/memory_mapped_file_posix.cc
index 90ba6f49c1..4899cf0cda 100644
--- a/base/files/memory_mapped_file_posix.cc
+++ b/base/files/memory_mapped_file_posix.cc
@@ -31,7 +31,7 @@ bool MemoryMappedFile::MapFileRegionToMemory(
if (region == MemoryMappedFile::Region::kWholeFile) {
int64_t file_len = file_.GetLength();
- if (file_len < 0) {
+ if (file_len == -1) {
DPLOG(ERROR) << "fstat " << file_.GetPlatformFile();
return false;
}
@@ -78,12 +78,7 @@ bool MemoryMappedFile::MapFileRegionToMemory(
// POSIX won't auto-extend the file when it is written so it must first
// be explicitly extended to the maximum size. Zeros will fill the new
// space.
- auto file_len = file_.GetLength();
- if (file_len < 0) {
- DPLOG(ERROR) << "fstat " << file_.GetPlatformFile();
- return false;
- }
- file_.SetLength(std::max(file_len, region.offset + region.size));
+ file_.SetLength(std::max(file_.GetLength(), region.offset + region.size));
flags |= PROT_READ | PROT_WRITE;
break;
}
diff --git a/base/files/scoped_file.cc b/base/files/scoped_file.cc
index 78d4ca5263..8ce45b8ba3 100644
--- a/base/files/scoped_file.cc
+++ b/base/files/scoped_file.cc
@@ -37,14 +37,6 @@ void ScopedFDCloseTraits::Free(int fd) {
int close_errno = errno;
base::debug::Alias(&close_errno);
-#if defined(OS_LINUX)
- // NB: Some file descriptors can return errors from close() e.g. network
- // filesystems such as NFS and Linux input devices. On Linux, errors from
- // close other than EBADF do not indicate failure to actually close the fd.
- if (ret != 0 && errno != EBADF)
- ret = 0;
-#endif
-
PCHECK(0 == ret);
}
diff --git a/base/files/scoped_temp_dir.cc b/base/files/scoped_temp_dir.cc
index 26815217c6..27b758ed90 100644
--- a/base/files/scoped_temp_dir.cc
+++ b/base/files/scoped_temp_dir.cc
@@ -76,11 +76,6 @@ FilePath ScopedTempDir::Take() {
return ret;
}
-const FilePath& ScopedTempDir::GetPath() const {
- DCHECK(!path_.empty()) << "Did you call CreateUniqueTempDir* before?";
- return path_;
-}
-
bool ScopedTempDir::IsValid() const {
return !path_.empty() && DirectoryExists(path_);
}
diff --git a/base/files/scoped_temp_dir.h b/base/files/scoped_temp_dir.h
index a5aaf84362..b1f2f5b874 100644
--- a/base/files/scoped_temp_dir.h
+++ b/base/files/scoped_temp_dir.h
@@ -47,9 +47,7 @@ class BASE_EXPORT ScopedTempDir {
// when this object goes out of scope.
FilePath Take();
- // Returns the path to the created directory. Call one of the
- // CreateUniqueTempDir* methods before getting the path.
- const FilePath& GetPath() const;
+ const FilePath& path() const { return path_; }
// Returns true if path_ is non-empty and exists.
bool IsValid() const;
diff --git a/base/files/scoped_temp_dir_unittest.cc b/base/files/scoped_temp_dir_unittest.cc
index 024b438aa0..3b2f28e50e 100644
--- a/base/files/scoped_temp_dir_unittest.cc
+++ b/base/files/scoped_temp_dir_unittest.cc
@@ -53,7 +53,7 @@ TEST(ScopedTempDir, TempDir) {
{
ScopedTempDir dir;
EXPECT_TRUE(dir.CreateUniqueTempDir());
- test_path = dir.GetPath();
+ test_path = dir.path();
EXPECT_TRUE(DirectoryExists(test_path));
FilePath tmp_dir;
EXPECT_TRUE(base::GetTempDir(&tmp_dir));
@@ -72,7 +72,7 @@ TEST(ScopedTempDir, UniqueTempDirUnderPath) {
{
ScopedTempDir dir;
EXPECT_TRUE(dir.CreateUniqueTempDirUnderPath(base_path));
- test_path = dir.GetPath();
+ test_path = dir.path();
EXPECT_TRUE(DirectoryExists(test_path));
EXPECT_TRUE(base_path.IsParent(test_path));
EXPECT_TRUE(test_path.value().find(base_path.value()) != std::string::npos);
@@ -99,12 +99,12 @@ TEST(ScopedTempDir, MultipleInvocations) {
TEST(ScopedTempDir, LockedTempDir) {
ScopedTempDir dir;
EXPECT_TRUE(dir.CreateUniqueTempDir());
- base::File file(dir.GetPath().Append(FILE_PATH_LITERAL("temp")),
+ base::File file(dir.path().Append(FILE_PATH_LITERAL("temp")),
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
EXPECT_TRUE(file.IsValid());
EXPECT_EQ(base::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.
+ EXPECT_FALSE(dir.path().empty()); // We should still have a valid path.
file.Close();
// Now, we should be able to delete.
EXPECT_TRUE(dir.Delete());
diff --git a/base/id_map.h b/base/id_map.h
index d171fb14c1..ef6b1564fa 100644
--- a/base/id_map.h
+++ b/base/id_map.h
@@ -7,16 +7,20 @@
#include <stddef.h>
#include <stdint.h>
-#include <memory>
#include <set>
-#include <type_traits>
-#include <utility>
#include "base/containers/hash_tables.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/sequence_checker.h"
+// Ownership semantics - own pointer means the pointer is deleted in Remove()
+// & during destruction
+enum IDMapOwnershipSemantics {
+ IDMapExternalPointer,
+ IDMapOwnPointer
+};
+
// This object maintains a list of IDs that can be quickly converted to
// pointers to objects. It is implemented as a hash table, optimized for
// relatively small data sets (in the common case, there will be exactly one
@@ -25,24 +29,25 @@
// Items can be inserted into the container with arbitrary ID, but the caller
// must ensure they are unique. Inserting IDs and relying on automatically
// generated ones is not allowed because they can collide.
-
-// The map's value type (the V param) can be any dereferenceable type, such as a
-// raw pointer or smart pointer
-template <typename V, typename K = int32_t>
-class IDMap final {
+//
+// This class does not have a virtual destructor, do not inherit from it when
+// ownership semantics are set to own because pointers will leak.
+template <typename T,
+ IDMapOwnershipSemantics OS = IDMapExternalPointer,
+ typename K = int32_t>
+class IDMap {
public:
using KeyType = K;
private:
- using T = typename std::remove_reference<decltype(*V())>::type;
- using HashTable = base::hash_map<KeyType, V>;
+ typedef base::hash_map<KeyType, T*> HashTable;
public:
IDMap() : iteration_depth_(0), next_id_(1), check_on_null_data_(false) {
// A number of consumers of IDMap create it on one thread but always
// access it from a different, but consistent, thread (or sequence)
- // post-construction. The first call to CalledOnValidSequence() will re-bind
- // it.
+ // post-construction. The first call to CalledOnValidSequencedThread()
+ // will re-bind it.
sequence_checker_.DetachFromSequence();
}
@@ -51,6 +56,7 @@ class IDMap final {
// thread. However, all the accesses may take place on another thread (or
// sequence), such as the IO thread. Detaching again to clean this up.
sequence_checker_.DetachFromSequence();
+ Releaser<OS, 0>::release_all(&data_);
}
// Sets whether Add and Replace should DCHECK if passed in NULL data.
@@ -58,16 +64,29 @@ class IDMap final {
void set_check_on_null_data(bool value) { check_on_null_data_ = value; }
// Adds a view with an automatically generated unique ID. See AddWithID.
- KeyType Add(V data) { return AddInternal(std::move(data)); }
+ KeyType Add(T* data) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ DCHECK(!check_on_null_data_ || data);
+ KeyType this_id = next_id_;
+ DCHECK(data_.find(this_id) == data_.end()) << "Inserting duplicate item";
+ data_[this_id] = data;
+ next_id_++;
+ return this_id;
+ }
// Adds a new data member with the specified ID. The ID must not be in
// the list. The caller either must generate all unique IDs itself and use
// this function, or allow this object to generate IDs and call Add. These
- // two methods may not be mixed, or duplicate IDs may be generated.
- void AddWithID(V data, KeyType id) { AddWithIDInternal(std::move(data), id); }
+ // two methods may not be mixed, or duplicate IDs may be generated
+ void AddWithID(T* data, KeyType id) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ DCHECK(!check_on_null_data_ || data);
+ DCHECK(data_.find(id) == data_.end()) << "Inserting duplicate item";
+ data_[id] = data;
+ }
void Remove(KeyType id) {
- DCHECK(sequence_checker_.CalledOnValidSequence());
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
typename HashTable::iterator i = data_.find(id);
if (i == data_.end()) {
NOTREACHED() << "Attempting to remove an item not in the list";
@@ -75,28 +94,36 @@ class IDMap final {
}
if (iteration_depth_ == 0) {
+ Releaser<OS, 0>::release(i->second);
data_.erase(i);
} else {
removed_ids_.insert(id);
}
}
- // Replaces the value for |id| with |new_data| and returns the existing value.
- // Should only be called with an already added id.
- V Replace(KeyType id, V new_data) {
- DCHECK(sequence_checker_.CalledOnValidSequence());
+ // Replaces the value for |id| with |new_data| and returns a pointer to the
+ // existing value. If there is no entry for |id|, the map is not altered and
+ // nullptr is returned. The OwnershipSemantics of the map have no effect on
+ // how the existing value is treated, the IDMap does not delete the existing
+ // value being replaced.
+ T* Replace(KeyType id, T* new_data) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
DCHECK(!check_on_null_data_ || new_data);
typename HashTable::iterator i = data_.find(id);
- DCHECK(i != data_.end());
+ if (i == data_.end()) {
+ NOTREACHED() << "Attempting to replace an item not in the list";
+ return nullptr;
+ }
- std::swap(i->second, new_data);
- return new_data;
+ T* temp = i->second;
+ i->second = new_data;
+ return temp;
}
void Clear() {
- DCHECK(sequence_checker_.CalledOnValidSequence());
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
if (iteration_depth_ == 0) {
- data_.clear();
+ Releaser<OS, 0>::release_all(&data_);
} else {
for (typename HashTable::iterator i = data_.begin();
i != data_.end(); ++i)
@@ -105,20 +132,20 @@ class IDMap final {
}
bool IsEmpty() const {
- DCHECK(sequence_checker_.CalledOnValidSequence());
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
return size() == 0u;
}
T* Lookup(KeyType id) const {
- DCHECK(sequence_checker_.CalledOnValidSequence());
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
typename HashTable::const_iterator i = data_.find(id);
if (i == data_.end())
- return nullptr;
- return &*i->second;
+ return NULL;
+ return i->second;
}
size_t size() const {
- DCHECK(sequence_checker_.CalledOnValidSequence());
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
return data_.size() - removed_ids_.size();
}
@@ -133,7 +160,9 @@ class IDMap final {
template<class ReturnType>
class Iterator {
public:
- Iterator(IDMap<V, K>* map) : map_(map), iter_(map_->data_.begin()) {
+ Iterator(IDMap<T, OS, K>* map)
+ : map_(map),
+ iter_(map_->data_.begin()) {
Init();
}
@@ -151,7 +180,7 @@ class IDMap final {
}
~Iterator() {
- DCHECK(map_->sequence_checker_.CalledOnValidSequence());
+ DCHECK(map_->sequence_checker_.CalledOnValidSequencedThread());
// We're going to decrement iteration depth. Make sure it's greater than
// zero so that it doesn't become negative.
@@ -162,29 +191,29 @@ class IDMap final {
}
bool IsAtEnd() const {
- DCHECK(map_->sequence_checker_.CalledOnValidSequence());
+ DCHECK(map_->sequence_checker_.CalledOnValidSequencedThread());
return iter_ == map_->data_.end();
}
KeyType GetCurrentKey() const {
- DCHECK(map_->sequence_checker_.CalledOnValidSequence());
+ DCHECK(map_->sequence_checker_.CalledOnValidSequencedThread());
return iter_->first;
}
ReturnType* GetCurrentValue() const {
- DCHECK(map_->sequence_checker_.CalledOnValidSequence());
- return &*iter_->second;
+ DCHECK(map_->sequence_checker_.CalledOnValidSequencedThread());
+ return iter_->second;
}
void Advance() {
- DCHECK(map_->sequence_checker_.CalledOnValidSequence());
+ DCHECK(map_->sequence_checker_.CalledOnValidSequencedThread());
++iter_;
SkipRemovedEntries();
}
private:
void Init() {
- DCHECK(map_->sequence_checker_.CalledOnValidSequence());
+ DCHECK(map_->sequence_checker_.CalledOnValidSequencedThread());
++map_->iteration_depth_;
SkipRemovedEntries();
}
@@ -197,7 +226,7 @@ class IDMap final {
}
}
- IDMap<V, K>* map_;
+ IDMap<T, OS, K>* map_;
typename HashTable::const_iterator iter_;
};
@@ -205,22 +234,24 @@ class IDMap final {
typedef Iterator<const T> const_iterator;
private:
- KeyType AddInternal(V data) {
- DCHECK(sequence_checker_.CalledOnValidSequence());
- DCHECK(!check_on_null_data_ || data);
- KeyType this_id = next_id_;
- DCHECK(data_.find(this_id) == data_.end()) << "Inserting duplicate item";
- data_[this_id] = std::move(data);
- next_id_++;
- return this_id;
- }
- void AddWithIDInternal(V data, KeyType id) {
- DCHECK(sequence_checker_.CalledOnValidSequence());
- DCHECK(!check_on_null_data_ || data);
- DCHECK(data_.find(id) == data_.end()) << "Inserting duplicate item";
- data_[id] = std::move(data);
- }
+ // The dummy parameter is there because C++ standard does not allow
+ // explicitly specialized templates inside classes
+ template<IDMapOwnershipSemantics OI, int dummy> struct Releaser {
+ static inline void release(T* ptr) {}
+ static inline void release_all(HashTable* table) {}
+ };
+
+ template<int dummy> struct Releaser<IDMapOwnPointer, dummy> {
+ static inline void release(T* ptr) { delete ptr;}
+ static inline void release_all(HashTable* table) {
+ for (typename HashTable::iterator i = table->begin();
+ i != table->end(); ++i) {
+ delete i->second;
+ }
+ table->clear();
+ }
+ };
void Compact() {
DCHECK_EQ(0, iteration_depth_);
diff --git a/base/id_map_unittest.cc b/base/id_map_unittest.cc
index 42949bb5b9..a3f0808915 100644
--- a/base/id_map_unittest.cc
+++ b/base/id_map_unittest.cc
@@ -6,9 +6,6 @@
#include <stdint.h>
-#include <memory>
-
-#include "base/memory/ptr_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
@@ -26,7 +23,7 @@ class DestructorCounter {
};
TEST(IDMapTest, Basic) {
- IDMap<TestObject*> map;
+ IDMap<TestObject> map;
EXPECT_TRUE(map.IsEmpty());
EXPECT_EQ(0U, map.size());
@@ -65,7 +62,7 @@ TEST(IDMapTest, Basic) {
}
TEST(IDMapTest, IteratorRemainsValidWhenRemovingCurrentElement) {
- IDMap<TestObject*> map;
+ IDMap<TestObject> map;
TestObject obj1;
TestObject obj2;
@@ -76,7 +73,7 @@ TEST(IDMapTest, IteratorRemainsValidWhenRemovingCurrentElement) {
map.Add(&obj3);
{
- IDMap<TestObject*>::const_iterator iter(&map);
+ IDMap<TestObject>::const_iterator iter(&map);
EXPECT_EQ(1, map.iteration_depth());
@@ -98,7 +95,7 @@ TEST(IDMapTest, IteratorRemainsValidWhenRemovingCurrentElement) {
}
TEST(IDMapTest, IteratorRemainsValidWhenRemovingOtherElements) {
- IDMap<TestObject*> map;
+ IDMap<TestObject> map;
const int kCount = 5;
TestObject obj[kCount];
@@ -110,16 +107,16 @@ TEST(IDMapTest, IteratorRemainsValidWhenRemovingOtherElements) {
int32_t ids_in_iteration_order[kCount];
const TestObject* objs_in_iteration_order[kCount];
int counter = 0;
- for (IDMap<TestObject*>::const_iterator iter(&map); !iter.IsAtEnd();
- iter.Advance()) {
+ for (IDMap<TestObject>::const_iterator iter(&map);
+ !iter.IsAtEnd(); iter.Advance()) {
ids_in_iteration_order[counter] = iter.GetCurrentKey();
objs_in_iteration_order[counter] = iter.GetCurrentValue();
counter++;
}
counter = 0;
- for (IDMap<TestObject*>::const_iterator iter(&map); !iter.IsAtEnd();
- iter.Advance()) {
+ for (IDMap<TestObject>::const_iterator iter(&map);
+ !iter.IsAtEnd(); iter.Advance()) {
EXPECT_EQ(1, map.iteration_depth());
switch (counter) {
@@ -150,7 +147,7 @@ TEST(IDMapTest, IteratorRemainsValidWhenRemovingOtherElements) {
}
TEST(IDMapTest, CopyIterator) {
- IDMap<TestObject*> map;
+ IDMap<TestObject> map;
TestObject obj1;
TestObject obj2;
@@ -163,12 +160,12 @@ TEST(IDMapTest, CopyIterator) {
EXPECT_EQ(0, map.iteration_depth());
{
- IDMap<TestObject*>::const_iterator iter1(&map);
+ IDMap<TestObject>::const_iterator iter1(&map);
EXPECT_EQ(1, map.iteration_depth());
// Make sure that copying the iterator correctly increments
// map's iteration depth.
- IDMap<TestObject*>::const_iterator iter2(iter1);
+ IDMap<TestObject>::const_iterator iter2(iter1);
EXPECT_EQ(2, map.iteration_depth());
}
@@ -178,7 +175,7 @@ TEST(IDMapTest, CopyIterator) {
}
TEST(IDMapTest, AssignIterator) {
- IDMap<TestObject*> map;
+ IDMap<TestObject> map;
TestObject obj1;
TestObject obj2;
@@ -191,10 +188,10 @@ TEST(IDMapTest, AssignIterator) {
EXPECT_EQ(0, map.iteration_depth());
{
- IDMap<TestObject*>::const_iterator iter1(&map);
+ IDMap<TestObject>::const_iterator iter1(&map);
EXPECT_EQ(1, map.iteration_depth());
- IDMap<TestObject*>::const_iterator iter2(&map);
+ IDMap<TestObject>::const_iterator iter2(&map);
EXPECT_EQ(2, map.iteration_depth());
// Make sure that assigning the iterator correctly updates
@@ -208,7 +205,7 @@ TEST(IDMapTest, AssignIterator) {
}
TEST(IDMapTest, IteratorRemainsValidWhenClearing) {
- IDMap<TestObject*> map;
+ IDMap<TestObject> map;
const int kCount = 5;
TestObject obj[kCount];
@@ -220,16 +217,16 @@ TEST(IDMapTest, IteratorRemainsValidWhenClearing) {
int32_t ids_in_iteration_order[kCount];
const TestObject* objs_in_iteration_order[kCount];
int counter = 0;
- for (IDMap<TestObject*>::const_iterator iter(&map); !iter.IsAtEnd();
- iter.Advance()) {
+ for (IDMap<TestObject>::const_iterator iter(&map);
+ !iter.IsAtEnd(); iter.Advance()) {
ids_in_iteration_order[counter] = iter.GetCurrentKey();
objs_in_iteration_order[counter] = iter.GetCurrentValue();
counter++;
}
counter = 0;
- for (IDMap<TestObject*>::const_iterator iter(&map); !iter.IsAtEnd();
- iter.Advance()) {
+ for (IDMap<TestObject>::const_iterator iter(&map);
+ !iter.IsAtEnd(); iter.Advance()) {
switch (counter) {
case 0:
EXPECT_EQ(ids_in_iteration_order[0], iter.GetCurrentKey());
@@ -261,17 +258,18 @@ TEST(IDMapTest, OwningPointersDeletesThemOnRemove) {
int map_external_ids[kCount];
int owned_del_count = 0;
+ DestructorCounter* owned_obj[kCount];
int map_owned_ids[kCount];
- IDMap<DestructorCounter*> map_external;
- IDMap<std::unique_ptr<DestructorCounter>> map_owned;
+ IDMap<DestructorCounter> map_external;
+ IDMap<DestructorCounter, IDMapOwnPointer> map_owned;
for (int i = 0; i < kCount; ++i) {
external_obj[i] = new DestructorCounter(&external_del_count);
map_external_ids[i] = map_external.Add(external_obj[i]);
- map_owned_ids[i] =
- map_owned.Add(base::MakeUnique<DestructorCounter>(&owned_del_count));
+ owned_obj[i] = new DestructorCounter(&owned_del_count);
+ map_owned_ids[i] = map_owned.Add(owned_obj[i]);
}
for (int i = 0; i < kCount; ++i) {
@@ -297,15 +295,17 @@ TEST(IDMapTest, OwningPointersDeletesThemOnClear) {
DestructorCounter* external_obj[kCount];
int owned_del_count = 0;
+ DestructorCounter* owned_obj[kCount];
- IDMap<DestructorCounter*> map_external;
- IDMap<std::unique_ptr<DestructorCounter>> map_owned;
+ IDMap<DestructorCounter> map_external;
+ IDMap<DestructorCounter, IDMapOwnPointer> map_owned;
for (int i = 0; i < kCount; ++i) {
external_obj[i] = new DestructorCounter(&external_del_count);
map_external.Add(external_obj[i]);
- map_owned.Add(base::MakeUnique<DestructorCounter>(&owned_del_count));
+ owned_obj[i] = new DestructorCounter(&owned_del_count);
+ map_owned.Add(owned_obj[i]);
}
EXPECT_EQ(external_del_count, 0);
@@ -332,16 +332,18 @@ TEST(IDMapTest, OwningPointersDeletesThemOnDestruct) {
DestructorCounter* external_obj[kCount];
int owned_del_count = 0;
+ DestructorCounter* owned_obj[kCount];
{
- IDMap<DestructorCounter*> map_external;
- IDMap<std::unique_ptr<DestructorCounter>> map_owned;
+ IDMap<DestructorCounter> map_external;
+ IDMap<DestructorCounter, IDMapOwnPointer> map_owned;
for (int i = 0; i < kCount; ++i) {
external_obj[i] = new DestructorCounter(&external_del_count);
map_external.Add(external_obj[i]);
- map_owned.Add(base::MakeUnique<DestructorCounter>(&owned_del_count));
+ owned_obj[i] = new DestructorCounter(&owned_del_count);
+ map_owned.Add(owned_obj[i]);
}
}
@@ -356,14 +358,14 @@ TEST(IDMapTest, OwningPointersDeletesThemOnDestruct) {
}
TEST(IDMapTest, Int64KeyType) {
- IDMap<TestObject*, int64_t> map;
+ IDMap<TestObject, IDMapExternalPointer, int64_t> map;
TestObject obj1;
const int64_t kId1 = 999999999999999999;
map.AddWithID(&obj1, kId1);
EXPECT_EQ(&obj1, map.Lookup(kId1));
- IDMap<TestObject*, int64_t>::const_iterator iter(&map);
+ IDMap<TestObject, IDMapExternalPointer, int64_t>::const_iterator iter(&map);
ASSERT_FALSE(iter.IsAtEnd());
EXPECT_EQ(kId1, iter.GetCurrentKey());
EXPECT_EQ(&obj1, iter.GetCurrentValue());
diff --git a/base/json/json_file_value_serializer.cc b/base/json/json_file_value_serializer.cc
index 661d25d798..1a9b7a23b2 100644
--- a/base/json/json_file_value_serializer.cc
+++ b/base/json/json_file_value_serializer.cc
@@ -53,9 +53,11 @@ bool JSONFileValueSerializer::SerializeInternal(const base::Value& root,
}
JSONFileValueDeserializer::JSONFileValueDeserializer(
- const base::FilePath& json_file_path,
- int options)
- : json_file_path_(json_file_path), options_(options), last_read_size_(0U) {}
+ const base::FilePath& json_file_path)
+ : json_file_path_(json_file_path),
+ allow_trailing_comma_(false),
+ last_read_size_(0U) {
+}
JSONFileValueDeserializer::~JSONFileValueDeserializer() {
}
@@ -112,6 +114,7 @@ std::unique_ptr<base::Value> JSONFileValueDeserializer::Deserialize(
return NULL;
}
- JSONStringValueDeserializer deserializer(json_string, options_);
+ JSONStringValueDeserializer deserializer(json_string);
+ deserializer.set_allow_trailing_comma(allow_trailing_comma_);
return deserializer.Deserialize(error_code, error_str);
}
diff --git a/base/json/json_file_value_serializer.h b/base/json/json_file_value_serializer.h
index a93950a608..67d2342b4c 100644
--- a/base/json/json_file_value_serializer.h
+++ b/base/json/json_file_value_serializer.h
@@ -48,9 +48,8 @@ class BASE_EXPORT JSONFileValueSerializer : public base::ValueSerializer {
class BASE_EXPORT JSONFileValueDeserializer : public base::ValueDeserializer {
public:
// |json_file_path_| is the path of a file that will be source of the
- // deserialization. |options| is a bitmask of JSONParserOptions.
- explicit JSONFileValueDeserializer(const base::FilePath& json_file_path,
- int options = 0);
+ // deserialization.
+ explicit JSONFileValueDeserializer(const base::FilePath& json_file_path);
~JSONFileValueDeserializer() override;
@@ -83,6 +82,10 @@ class BASE_EXPORT JSONFileValueDeserializer : public base::ValueDeserializer {
// be a JsonFileError.
static const char* GetErrorMessageForCode(int error_code);
+ void set_allow_trailing_comma(bool new_value) {
+ allow_trailing_comma_ = new_value;
+ }
+
// Returns the size (in bytes) of JSON string read from disk in the last
// successful |Deserialize()| call.
size_t get_last_read_size() const { return last_read_size_; }
@@ -93,7 +96,7 @@ class BASE_EXPORT JSONFileValueDeserializer : public base::ValueDeserializer {
int ReadFileToString(std::string* json_string);
const base::FilePath json_file_path_;
- const int options_;
+ bool allow_trailing_comma_;
size_t last_read_size_;
DISALLOW_IMPLICIT_CONSTRUCTORS(JSONFileValueDeserializer);
diff --git a/base/json/json_parser.cc b/base/json/json_parser.cc
index c6f6409df6..d97eccc96c 100644
--- a/base/json/json_parser.cc
+++ b/base/json/json_parser.cc
@@ -24,12 +24,149 @@ namespace internal {
namespace {
-// Chosen to support 99.9% of documents found in the wild late 2016.
-// http://crbug.com/673263
-const int kStackMaxDepth = 200;
+const int kStackMaxDepth = 100;
const int32_t kExtendedASCIIStart = 0x80;
+// DictionaryHiddenRootValue and ListHiddenRootValue are used in conjunction
+// with JSONStringValue as an optimization for reducing the number of string
+// copies. When this optimization is active, the parser uses a hidden root to
+// keep the original JSON input string live and creates JSONStringValue children
+// holding StringPiece references to the input string, avoiding about 2/3rds of
+// string memory copies. The real root value is Swap()ed into the new instance.
+class DictionaryHiddenRootValue : public DictionaryValue {
+ public:
+ DictionaryHiddenRootValue(std::unique_ptr<std::string> json,
+ std::unique_ptr<Value> root)
+ : json_(std::move(json)) {
+ DCHECK(root->IsType(Value::TYPE_DICTIONARY));
+ DictionaryValue::Swap(static_cast<DictionaryValue*>(root.get()));
+ }
+
+ void Swap(DictionaryValue* other) override {
+ DVLOG(1) << "Swap()ing a DictionaryValue inefficiently.";
+
+ // First deep copy to convert JSONStringValue to std::string and swap that
+ // copy with |other|, which contains the new contents of |this|.
+ std::unique_ptr<DictionaryValue> copy(CreateDeepCopy());
+ copy->Swap(other);
+
+ // Then erase the contents of the current dictionary and swap in the
+ // new contents, originally from |other|.
+ Clear();
+ json_.reset();
+ DictionaryValue::Swap(copy.get());
+ }
+
+ // Not overriding DictionaryValue::Remove because it just calls through to
+ // the method below.
+
+ bool RemoveWithoutPathExpansion(const std::string& key,
+ std::unique_ptr<Value>* out) override {
+ // If the caller won't take ownership of the removed value, just call up.
+ if (!out)
+ return DictionaryValue::RemoveWithoutPathExpansion(key, out);
+
+ DVLOG(1) << "Remove()ing from a DictionaryValue inefficiently.";
+
+ // Otherwise, remove the value while its still "owned" by this and copy it
+ // to convert any JSONStringValues to std::string.
+ std::unique_ptr<Value> out_owned;
+ if (!DictionaryValue::RemoveWithoutPathExpansion(key, &out_owned))
+ return false;
+
+ *out = out_owned->CreateDeepCopy();
+
+ return true;
+ }
+
+ private:
+ std::unique_ptr<std::string> json_;
+
+ DISALLOW_COPY_AND_ASSIGN(DictionaryHiddenRootValue);
+};
+
+class ListHiddenRootValue : public ListValue {
+ public:
+ ListHiddenRootValue(std::unique_ptr<std::string> json,
+ std::unique_ptr<Value> root)
+ : json_(std::move(json)) {
+ DCHECK(root->IsType(Value::TYPE_LIST));
+ ListValue::Swap(static_cast<ListValue*>(root.get()));
+ }
+
+ void Swap(ListValue* other) override {
+ DVLOG(1) << "Swap()ing a ListValue inefficiently.";
+
+ // First deep copy to convert JSONStringValue to std::string and swap that
+ // copy with |other|, which contains the new contents of |this|.
+ std::unique_ptr<ListValue> copy(CreateDeepCopy());
+ copy->Swap(other);
+
+ // Then erase the contents of the current list and swap in the new contents,
+ // originally from |other|.
+ Clear();
+ json_.reset();
+ ListValue::Swap(copy.get());
+ }
+
+ bool Remove(size_t index, std::unique_ptr<Value>* out) override {
+ // If the caller won't take ownership of the removed value, just call up.
+ if (!out)
+ return ListValue::Remove(index, out);
+
+ DVLOG(1) << "Remove()ing from a ListValue inefficiently.";
+
+ // Otherwise, remove the value while its still "owned" by this and copy it
+ // to convert any JSONStringValues to std::string.
+ std::unique_ptr<Value> out_owned;
+ if (!ListValue::Remove(index, &out_owned))
+ return false;
+
+ *out = out_owned->CreateDeepCopy();
+
+ return true;
+ }
+
+ private:
+ std::unique_ptr<std::string> json_;
+
+ DISALLOW_COPY_AND_ASSIGN(ListHiddenRootValue);
+};
+
+// A variant on StringValue that uses StringPiece instead of copying the string
+// into the Value. This can only be stored in a child of hidden root (above),
+// otherwise the referenced string will not be guaranteed to outlive it.
+class JSONStringValue : public Value {
+ public:
+ explicit JSONStringValue(StringPiece piece)
+ : Value(TYPE_STRING), string_piece_(piece) {}
+
+ // Overridden from Value:
+ bool GetAsString(std::string* out_value) const override {
+ string_piece_.CopyToString(out_value);
+ return true;
+ }
+ bool GetAsString(string16* out_value) const override {
+ *out_value = UTF8ToUTF16(string_piece_);
+ return true;
+ }
+ Value* DeepCopy() const override {
+ return new StringValue(string_piece_.as_string());
+ }
+ bool Equals(const Value* other) const override {
+ std::string other_string;
+ return other->IsType(TYPE_STRING) && other->GetAsString(&other_string) &&
+ StringPiece(other_string) == string_piece_;
+ }
+
+ private:
+ // The location in the original input stream.
+ StringPiece string_piece_;
+
+ DISALLOW_COPY_AND_ASSIGN(JSONStringValue);
+};
+
// Simple class that checks for maximum recursion/"stack overflow."
class StackMarker {
public:
@@ -53,9 +190,6 @@ class StackMarker {
} // namespace
-// This is U+FFFD.
-const char kUnicodeReplacementString[] = "\xEF\xBF\xBD";
-
JSONParser::JSONParser(int options)
: options_(options),
start_pos_(nullptr),
@@ -74,7 +208,16 @@ JSONParser::~JSONParser() {
}
std::unique_ptr<Value> JSONParser::Parse(StringPiece input) {
- start_pos_ = input.data();
+ std::unique_ptr<std::string> input_copy;
+ // If the children of a JSON root can be detached, then hidden roots cannot
+ // be used, so do not bother copying the input because StringPiece will not
+ // be used anywhere.
+ if (!(options_ & JSON_DETACHABLE_CHILDREN)) {
+ input_copy = MakeUnique<std::string>(input.as_string());
+ start_pos_ = input_copy->data();
+ } else {
+ start_pos_ = input.data();
+ }
pos_ = start_pos_;
end_pos_ = start_pos_ + input.length();
index_ = 0;
@@ -108,6 +251,26 @@ std::unique_ptr<Value> JSONParser::Parse(StringPiece input) {
}
}
+ // Dictionaries and lists can contain JSONStringValues, so wrap them in a
+ // hidden root.
+ if (!(options_ & JSON_DETACHABLE_CHILDREN)) {
+ if (root->IsType(Value::TYPE_DICTIONARY)) {
+ return MakeUnique<DictionaryHiddenRootValue>(std::move(input_copy),
+ std::move(root));
+ }
+ if (root->IsType(Value::TYPE_LIST)) {
+ return MakeUnique<ListHiddenRootValue>(std::move(input_copy),
+ std::move(root));
+ }
+ if (root->IsType(Value::TYPE_STRING)) {
+ // A string type could be a JSONStringValue, but because there's no
+ // corresponding HiddenRootValue, the memory will be lost. Deep copy to
+ // preserve it.
+ return root->CreateDeepCopy();
+ }
+ }
+
+ // All other values can be returned directly.
return root;
}
@@ -133,62 +296,58 @@ int JSONParser::error_column() const {
JSONParser::StringBuilder::StringBuilder() : StringBuilder(nullptr) {}
JSONParser::StringBuilder::StringBuilder(const char* pos)
- : pos_(pos), length_(0), has_string_(false) {}
+ : pos_(pos),
+ length_(0),
+ string_(nullptr) {
+}
-JSONParser::StringBuilder::~StringBuilder() {
- if (has_string_)
- string_.Destroy();
+void JSONParser::StringBuilder::Swap(StringBuilder* other) {
+ std::swap(other->string_, string_);
+ std::swap(other->pos_, pos_);
+ std::swap(other->length_, length_);
}
-void JSONParser::StringBuilder::operator=(StringBuilder&& other) {
- pos_ = other.pos_;
- length_ = other.length_;
- has_string_ = other.has_string_;
- if (has_string_)
- string_.InitFromMove(std::move(other.string_));
+JSONParser::StringBuilder::~StringBuilder() {
+ delete string_;
}
void JSONParser::StringBuilder::Append(const char& c) {
DCHECK_GE(c, 0);
DCHECK_LT(static_cast<unsigned char>(c), 128);
- if (has_string_)
+ if (string_)
string_->push_back(c);
else
++length_;
}
-void JSONParser::StringBuilder::AppendString(const char* str, size_t len) {
- DCHECK(has_string_);
- string_->append(str, len);
+void JSONParser::StringBuilder::AppendString(const std::string& str) {
+ DCHECK(string_);
+ string_->append(str);
}
void JSONParser::StringBuilder::Convert() {
- if (has_string_)
+ if (string_)
return;
+ string_ = new std::string(pos_, length_);
+}
- has_string_ = true;
- string_.Init(pos_, length_);
+bool JSONParser::StringBuilder::CanBeStringPiece() const {
+ return !string_;
}
StringPiece JSONParser::StringBuilder::AsStringPiece() {
- if (has_string_)
- return StringPiece(*string_);
+ if (string_)
+ return StringPiece();
return StringPiece(pos_, length_);
}
const std::string& JSONParser::StringBuilder::AsString() {
- if (!has_string_)
+ if (!string_)
Convert();
return *string_;
}
-std::string JSONParser::StringBuilder::DestructiveAsString() {
- if (has_string_)
- return std::move(*string_);
- return std::string(pos_, length_);
-}
-
// JSONParser private //////////////////////////////////////////////////////////
inline bool JSONParser::CanConsume(int length) {
@@ -308,11 +467,11 @@ bool JSONParser::EatComment() {
return false;
}
-std::unique_ptr<Value> JSONParser::ParseNextToken() {
+Value* JSONParser::ParseNextToken() {
return ParseToken(GetNextToken());
}
-std::unique_ptr<Value> JSONParser::ParseToken(Token token) {
+Value* JSONParser::ParseToken(Token token) {
switch (token) {
case T_OBJECT_BEGIN:
return ConsumeDictionary();
@@ -332,7 +491,7 @@ std::unique_ptr<Value> JSONParser::ParseToken(Token token) {
}
}
-std::unique_ptr<Value> JSONParser::ConsumeDictionary() {
+Value* JSONParser::ConsumeDictionary() {
if (*pos_ != '{') {
ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1);
return nullptr;
@@ -370,13 +529,13 @@ std::unique_ptr<Value> JSONParser::ConsumeDictionary() {
// The next token is the value. Ownership transfers to |dict|.
NextChar();
- std::unique_ptr<Value> value = ParseNextToken();
+ Value* value = ParseNextToken();
if (!value) {
// ReportError from deeper level.
return nullptr;
}
- dict->SetWithoutPathExpansion(key.AsStringPiece(), std::move(value));
+ dict->SetWithoutPathExpansion(key.AsString(), value);
NextChar();
token = GetNextToken();
@@ -393,10 +552,10 @@ std::unique_ptr<Value> JSONParser::ConsumeDictionary() {
}
}
- return std::move(dict);
+ return dict.release();
}
-std::unique_ptr<Value> JSONParser::ConsumeList() {
+Value* JSONParser::ConsumeList() {
if (*pos_ != '[') {
ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1);
return nullptr;
@@ -413,13 +572,13 @@ std::unique_ptr<Value> JSONParser::ConsumeList() {
NextChar();
Token token = GetNextToken();
while (token != T_ARRAY_END) {
- std::unique_ptr<Value> item = ParseToken(token);
+ Value* item = ParseToken(token);
if (!item) {
// ReportError from deeper level.
return nullptr;
}
- list->Append(std::move(item));
+ list->Append(item);
NextChar();
token = GetNextToken();
@@ -436,15 +595,22 @@ std::unique_ptr<Value> JSONParser::ConsumeList() {
}
}
- return std::move(list);
+ return list.release();
}
-std::unique_ptr<Value> JSONParser::ConsumeString() {
+Value* JSONParser::ConsumeString() {
StringBuilder string;
if (!ConsumeStringRaw(&string))
return nullptr;
- return base::MakeUnique<Value>(string.DestructiveAsString());
+ // Create the Value representation, using a hidden root, if configured
+ // to do so, and if the string can be represented by StringPiece.
+ if (string.CanBeStringPiece() && !(options_ & JSON_DETACHABLE_CHILDREN))
+ return new JSONStringValue(string.AsStringPiece());
+
+ if (string.CanBeStringPiece())
+ string.Convert();
+ return new StringValue(string.AsString());
}
bool JSONParser::ConsumeStringRaw(StringBuilder* out) {
@@ -462,24 +628,16 @@ bool JSONParser::ConsumeStringRaw(StringBuilder* out) {
int32_t next_char = 0;
while (CanConsume(1)) {
- int start_index = index_;
pos_ = start_pos_ + index_; // CBU8_NEXT is postcrement.
CBU8_NEXT(start_pos_, index_, length, next_char);
if (next_char < 0 || !IsValidCharacter(next_char)) {
- if ((options_ & JSON_REPLACE_INVALID_CHARACTERS) == 0) {
- ReportError(JSONReader::JSON_UNSUPPORTED_ENCODING, 1);
- return false;
- }
- CBU8_NEXT(start_pos_, start_index, length, next_char);
- string.Convert();
- string.AppendString(kUnicodeReplacementString,
- arraysize(kUnicodeReplacementString) - 1);
- continue;
+ ReportError(JSONReader::JSON_UNSUPPORTED_ENCODING, 1);
+ return false;
}
if (next_char == '"') {
--index_; // Rewind by one because of CBU8_NEXT.
- *out = std::move(string);
+ out->Swap(&string);
return true;
}
@@ -512,8 +670,7 @@ bool JSONParser::ConsumeStringRaw(StringBuilder* out) {
}
int hex_digit = 0;
- if (!HexStringToInt(StringPiece(NextChar(), 2), &hex_digit) ||
- !IsValidCharacter(hex_digit)) {
+ if (!HexStringToInt(StringPiece(NextChar(), 2), &hex_digit)) {
ReportError(JSONReader::JSON_INVALID_ESCAPE, -1);
return false;
}
@@ -541,7 +698,7 @@ bool JSONParser::ConsumeStringRaw(StringBuilder* out) {
return false;
}
- string.AppendString(utf8_units.data(), utf8_units.length());
+ string.AppendString(utf8_units);
break;
}
case '"':
@@ -666,11 +823,11 @@ void JSONParser::DecodeUTF8(const int32_t& point, StringBuilder* dest) {
dest->Convert();
// CBU8_APPEND_UNSAFE can overwrite up to 4 bytes, so utf8_units may not be
// zero terminated at this point. |offset| contains the correct length.
- dest->AppendString(utf8_units, offset);
+ dest->AppendString(std::string(utf8_units, offset));
}
}
-std::unique_ptr<Value> JSONParser::ConsumeNumber() {
+Value* JSONParser::ConsumeNumber() {
const char* num_start = pos_;
const int start_index = index_;
int end_index = start_index;
@@ -685,7 +842,11 @@ std::unique_ptr<Value> JSONParser::ConsumeNumber() {
end_index = index_;
// The optional fraction part.
- if (CanConsume(1) && *pos_ == '.') {
+ if (*pos_ == '.') {
+ if (!CanConsume(1)) {
+ ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
+ return nullptr;
+ }
NextChar();
if (!ReadInt(true)) {
ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
@@ -695,15 +856,10 @@ std::unique_ptr<Value> JSONParser::ConsumeNumber() {
}
// Optional exponent part.
- if (CanConsume(1) && (*pos_ == 'e' || *pos_ == 'E')) {
+ if (*pos_ == 'e' || *pos_ == 'E') {
NextChar();
- if (!CanConsume(1)) {
- ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
- return nullptr;
- }
- if (*pos_ == '-' || *pos_ == '+') {
+ if (*pos_ == '-' || *pos_ == '+')
NextChar();
- }
if (!ReadInt(true)) {
ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
return nullptr;
@@ -736,30 +892,25 @@ std::unique_ptr<Value> JSONParser::ConsumeNumber() {
int num_int;
if (StringToInt(num_string, &num_int))
- return base::MakeUnique<Value>(num_int);
+ return new FundamentalValue(num_int);
double num_double;
if (StringToDouble(num_string.as_string(), &num_double) &&
std::isfinite(num_double)) {
- return base::MakeUnique<Value>(num_double);
+ return new FundamentalValue(num_double);
}
return nullptr;
}
bool JSONParser::ReadInt(bool allow_leading_zeros) {
- size_t len = 0;
- char first = 0;
-
- while (CanConsume(1)) {
- if (!IsAsciiDigit(*pos_))
- break;
-
- if (len == 0)
- first = *pos_;
+ char first = *pos_;
+ int len = 0;
+ char c = first;
+ while (CanConsume(1) && IsAsciiDigit(c)) {
+ c = *NextChar();
++len;
- NextChar();
}
if (len == 0)
@@ -771,7 +922,7 @@ bool JSONParser::ReadInt(bool allow_leading_zeros) {
return true;
}
-std::unique_ptr<Value> JSONParser::ConsumeLiteral() {
+Value* JSONParser::ConsumeLiteral() {
switch (*pos_) {
case 't': {
const char kTrueLiteral[] = "true";
@@ -782,7 +933,7 @@ std::unique_ptr<Value> JSONParser::ConsumeLiteral() {
return nullptr;
}
NextNChars(kTrueLen - 1);
- return base::MakeUnique<Value>(true);
+ return new FundamentalValue(true);
}
case 'f': {
const char kFalseLiteral[] = "false";
@@ -793,7 +944,7 @@ std::unique_ptr<Value> JSONParser::ConsumeLiteral() {
return nullptr;
}
NextNChars(kFalseLen - 1);
- return base::MakeUnique<Value>(false);
+ return new FundamentalValue(false);
}
case 'n': {
const char kNullLiteral[] = "null";
@@ -804,7 +955,7 @@ std::unique_ptr<Value> JSONParser::ConsumeLiteral() {
return nullptr;
}
NextNChars(kNullLen - 1);
- return Value::CreateNullValue();
+ return Value::CreateNullValue().release();
}
default:
ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1);
diff --git a/base/json/json_parser.h b/base/json/json_parser.h
index 4f26458363..7539fa99ca 100644
--- a/base/json/json_parser.h
+++ b/base/json/json_parser.h
@@ -16,7 +16,6 @@
#include "base/gtest_prod_util.h"
#include "base/json/json_reader.h"
#include "base/macros.h"
-#include "base/memory/manual_constructor.h"
#include "base/strings/string_piece.h"
namespace base {
@@ -31,7 +30,7 @@ class JSONParserTest;
// to be used directly; it encapsulates logic that need not be exposed publicly.
//
// This parser guarantees O(n) time through the input string. It also optimizes
-// base::Value by using StringPiece where possible when returning Value
+// base::StringValue by using StringPiece where possible when returning Value
// objects by using "hidden roots," discussed in the implementation.
//
// Iteration happens on the byte level, with the functions CanConsume and
@@ -94,7 +93,7 @@ class BASE_EXPORT JSONParser {
// This class centralizes that logic.
class StringBuilder {
public:
- // Empty constructor. Used for creating a builder with which to assign to.
+ // Empty constructor. Used for creating a builder with which to Swap().
StringBuilder();
// |pos| is the beginning of an input string, excluding the |"|.
@@ -102,7 +101,8 @@ class BASE_EXPORT JSONParser {
~StringBuilder();
- void operator=(StringBuilder&& other);
+ // Swaps the contents of |other| with this.
+ void Swap(StringBuilder* other);
// Either increases the |length_| of the string or copies the character if
// the StringBuilder has been converted. |c| must be in the basic ASCII
@@ -111,24 +111,23 @@ class BASE_EXPORT JSONParser {
void Append(const char& c);
// Appends a string to the std::string. Must be Convert()ed to use.
- void AppendString(const char* str, size_t len);
+ void AppendString(const std::string& str);
// Converts the builder from its default StringPiece to a full std::string,
// performing a copy. Once a builder is converted, it cannot be made a
// StringPiece again.
void Convert();
- // Returns the builder as a StringPiece.
+ // Returns whether the builder can be converted to a StringPiece.
+ bool CanBeStringPiece() const;
+
+ // Returns the StringPiece representation. Returns an empty piece if it
+ // cannot be converted.
StringPiece AsStringPiece();
// Returns the builder as a std::string.
const std::string& AsString();
- // Returns the builder as a string, invalidating all state. This allows
- // the internal string buffer representation to be destructively moved
- // in cases where the builder will not be needed any more.
- std::string DestructiveAsString();
-
private:
// The beginning of the input string.
const char* pos_;
@@ -136,10 +135,9 @@ class BASE_EXPORT JSONParser {
// Number of bytes in |pos_| that make up the string being built.
size_t length_;
- // The copied string representation. Will be uninitialized until Convert()
- // is called, which will set has_string_ to true.
- bool has_string_;
- base::ManualConstructor<std::string> string_;
+ // The copied string representation. NULL until Convert() is called.
+ // Strong. std::unique_ptr<T> has too much of an overhead here.
+ std::string* string_;
};
// Quick check that the stream has capacity to consume |length| more bytes.
@@ -163,27 +161,28 @@ class BASE_EXPORT JSONParser {
// currently wound to a '/'.
bool EatComment();
- // Calls GetNextToken() and then ParseToken().
- std::unique_ptr<Value> ParseNextToken();
+ // Calls GetNextToken() and then ParseToken(). Caller owns the result.
+ Value* ParseNextToken();
// Takes a token that represents the start of a Value ("a structural token"
- // in RFC terms) and consumes it, returning the result as a Value.
- std::unique_ptr<Value> ParseToken(Token token);
+ // in RFC terms) and consumes it, returning the result as an object the
+ // caller owns.
+ Value* ParseToken(Token token);
// Assuming that the parser is currently wound to '{', this parses a JSON
// object into a DictionaryValue.
- std::unique_ptr<Value> ConsumeDictionary();
+ Value* ConsumeDictionary();
// Assuming that the parser is wound to '[', this parses a JSON list into a
- // std::unique_ptr<ListValue>.
- std::unique_ptr<Value> ConsumeList();
+ // ListValue.
+ Value* ConsumeList();
// Calls through ConsumeStringRaw and wraps it in a value.
- std::unique_ptr<Value> ConsumeString();
+ Value* ConsumeString();
// Assuming that the parser is wound to a double quote, this parses a string,
// decoding any escape sequences and converts UTF-16 to UTF-8. Returns true on
- // success and places result into |out|. Returns false on failure with
+ // success and Swap()s the result into |out|. Returns false on failure with
// error information set.
bool ConsumeStringRaw(StringBuilder* out);
// Helper function for ConsumeStringRaw() that consumes the next four or 10
@@ -199,14 +198,14 @@ class BASE_EXPORT JSONParser {
// Assuming that the parser is wound to the start of a valid JSON number,
// this parses and converts it to either an int or double value.
- std::unique_ptr<Value> ConsumeNumber();
+ Value* ConsumeNumber();
// Helper that reads characters that are ints. Returns true if a number was
// read and false on error.
bool ReadInt(bool allow_leading_zeros);
// Consumes the literal values of |true|, |false|, and |null|, assuming the
// parser is wound to the first character of any of those.
- std::unique_ptr<Value> ConsumeLiteral();
+ Value* ConsumeLiteral();
// Compares two string buffers of a given length.
static bool StringsAreEqual(const char* left, const char* right, size_t len);
@@ -259,14 +258,10 @@ class BASE_EXPORT JSONParser {
FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeLiterals);
FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeNumbers);
FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ErrorMessages);
- FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ReplaceInvalidCharacters);
DISALLOW_COPY_AND_ASSIGN(JSONParser);
};
-// Used when decoding and an invalid utf-8 sequence is encountered.
-BASE_EXPORT extern const char kUnicodeReplacementString[];
-
} // namespace internal
} // namespace base
diff --git a/base/json/json_parser_unittest.cc b/base/json/json_parser_unittest.cc
index e3f635b76f..30255ca461 100644
--- a/base/json/json_parser_unittest.cc
+++ b/base/json/json_parser_unittest.cc
@@ -9,8 +9,6 @@
#include <memory>
#include "base/json/json_reader.h"
-#include "base/memory/ptr_util.h"
-#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -19,9 +17,8 @@ namespace internal {
class JSONParserTest : public testing::Test {
public:
- JSONParser* NewTestParser(const std::string& input,
- int options = JSON_PARSE_RFC) {
- JSONParser* parser = new JSONParser(options);
+ JSONParser* NewTestParser(const std::string& input) {
+ JSONParser* parser = new JSONParser(JSON_PARSE_RFC);
parser->start_pos_ = input.data();
parser->pos_ = parser->start_pos_;
parser->end_pos_ = parser->start_pos_ + input.length();
@@ -108,7 +105,7 @@ TEST_F(JSONParserTest, ConsumeLiterals) {
// Literal |false|.
input = "false,|";
parser.reset(NewTestParser(input));
- value = parser->ConsumeLiteral();
+ value.reset(parser->ConsumeLiteral());
EXPECT_EQ('e', *parser->pos_);
TestLastThree(parser.get());
@@ -120,13 +117,13 @@ TEST_F(JSONParserTest, ConsumeLiterals) {
// Literal |null|.
input = "null,|";
parser.reset(NewTestParser(input));
- value = parser->ConsumeLiteral();
+ value.reset(parser->ConsumeLiteral());
EXPECT_EQ('l', *parser->pos_);
TestLastThree(parser.get());
ASSERT_TRUE(value.get());
- EXPECT_TRUE(value->IsType(Value::Type::NONE));
+ EXPECT_TRUE(value->IsType(Value::TYPE_NULL));
}
TEST_F(JSONParserTest, ConsumeNumbers) {
@@ -146,7 +143,7 @@ TEST_F(JSONParserTest, ConsumeNumbers) {
// Negative integer.
input = "-1234,|";
parser.reset(NewTestParser(input));
- value = parser->ConsumeNumber();
+ value.reset(parser->ConsumeNumber());
EXPECT_EQ('4', *parser->pos_);
TestLastThree(parser.get());
@@ -158,7 +155,7 @@ TEST_F(JSONParserTest, ConsumeNumbers) {
// Double.
input = "12.34,|";
parser.reset(NewTestParser(input));
- value = parser->ConsumeNumber();
+ value.reset(parser->ConsumeNumber());
EXPECT_EQ('4', *parser->pos_);
TestLastThree(parser.get());
@@ -171,7 +168,7 @@ TEST_F(JSONParserTest, ConsumeNumbers) {
// Scientific.
input = "42e3,|";
parser.reset(NewTestParser(input));
- value = parser->ConsumeNumber();
+ value.reset(parser->ConsumeNumber());
EXPECT_EQ('3', *parser->pos_);
TestLastThree(parser.get());
@@ -183,7 +180,7 @@ TEST_F(JSONParserTest, ConsumeNumbers) {
// Negative scientific.
input = "314159e-5,|";
parser.reset(NewTestParser(input));
- value = parser->ConsumeNumber();
+ value.reset(parser->ConsumeNumber());
EXPECT_EQ('5', *parser->pos_);
TestLastThree(parser.get());
@@ -195,7 +192,7 @@ TEST_F(JSONParserTest, ConsumeNumbers) {
// Positive scientific.
input = "0.42e+3,|";
parser.reset(NewTestParser(input));
- value = parser->ConsumeNumber();
+ value.reset(parser->ConsumeNumber());
EXPECT_EQ('3', *parser->pos_);
TestLastThree(parser.get());
@@ -246,14 +243,14 @@ TEST_F(JSONParserTest, ErrorMessages) {
EXPECT_EQ(JSONReader::JSON_UNEXPECTED_DATA_AFTER_ROOT, error_code);
std::string nested_json;
- for (int i = 0; i < 201; ++i) {
+ for (int i = 0; i < 101; ++i) {
nested_json.insert(nested_json.begin(), '[');
nested_json.append(1, ']');
}
root = JSONReader::ReadAndReturnError(nested_json, JSON_PARSE_RFC,
&error_code, &error_message);
EXPECT_FALSE(root.get());
- EXPECT_EQ(JSONParser::FormatErrorMessage(1, 200, JSONReader::kTooMuchNesting),
+ EXPECT_EQ(JSONParser::FormatErrorMessage(1, 100, JSONReader::kTooMuchNesting),
error_message);
EXPECT_EQ(JSONReader::JSON_TOO_MUCH_NESTING, error_code);
@@ -326,69 +323,5 @@ TEST_F(JSONParserTest, DecodeUnicodeNonCharacter) {
EXPECT_FALSE(JSONReader::Read("[\"\\ud83f\\udffe\"]"));
}
-TEST_F(JSONParserTest, DecodeNegativeEscapeSequence) {
- EXPECT_FALSE(JSONReader::Read("[\"\\x-A\"]"));
- EXPECT_FALSE(JSONReader::Read("[\"\\u-00A\"]"));
-}
-
-// Verifies invalid utf-8 characters are replaced.
-TEST_F(JSONParserTest, ReplaceInvalidCharacters) {
- const std::string bogus_char = "ó¿¿¿";
- const std::string quoted_bogus_char = "\"" + bogus_char + "\"";
- std::unique_ptr<JSONParser> parser(
- NewTestParser(quoted_bogus_char, JSON_REPLACE_INVALID_CHARACTERS));
- std::unique_ptr<Value> value(parser->ConsumeString());
- ASSERT_TRUE(value.get());
- std::string str;
- EXPECT_TRUE(value->GetAsString(&str));
- EXPECT_EQ(kUnicodeReplacementString, str);
-}
-
-TEST_F(JSONParserTest, ParseNumberErrors) {
- const struct {
- const char* input;
- bool parse_success;
- double value;
- } kCases[] = {
- // clang-format off
- {"1", true, 1},
- {"2.", false, 0},
- {"42", true, 42},
- {"6e", false, 0},
- {"43e2", true, 4300},
- {"43e-", false, 0},
- {"9e-3", true, 0.009},
- {"2e+", false, 0},
- {"2e+2", true, 200},
- // clang-format on
- };
-
- for (unsigned int i = 0; i < arraysize(kCases); ++i) {
- auto test_case = kCases[i];
- SCOPED_TRACE(StringPrintf("case %u: \"%s\"", i, test_case.input));
-
- // MSan will do a better job detecting over-read errors if the input is
- // not nul-terminated on the heap.
- size_t str_len = strlen(test_case.input);
- auto non_nul_termianted = MakeUnique<char[]>(str_len);
- memcpy(non_nul_termianted.get(), test_case.input, str_len);
-
- StringPiece string_piece(non_nul_termianted.get(), str_len);
- std::unique_ptr<Value> result = JSONReader::Read(string_piece);
- if (test_case.parse_success) {
- EXPECT_TRUE(result);
- } else {
- EXPECT_FALSE(result);
- }
-
- if (!result)
- continue;
-
- double double_value = 0;
- EXPECT_TRUE(result->GetAsDouble(&double_value));
- EXPECT_EQ(test_case.value, double_value);
- }
-}
-
} // namespace internal
} // namespace base
diff --git a/base/json/json_reader.h b/base/json/json_reader.h
index a39b37adeb..a954821a28 100644
--- a/base/json/json_reader.h
+++ b/base/json/json_reader.h
@@ -55,11 +55,6 @@ enum JSONParserOptions {
// if the child is Remove()d from root, it would result in use-after-free
// unless it is DeepCopy()ed or this option is used.
JSON_DETACHABLE_CHILDREN = 1 << 1,
-
- // If set the parser replaces invalid characters with the Unicode replacement
- // character (U+FFFD). If not set, invalid characters trigger a hard error and
- // parsing fails.
- JSON_REPLACE_INVALID_CHARACTERS = 1 << 2,
};
class BASE_EXPORT JSONReader {
diff --git a/base/json/json_reader_unittest.cc b/base/json/json_reader_unittest.cc
index 1344de6391..84732c4d75 100644
--- a/base/json/json_reader_unittest.cc
+++ b/base/json/json_reader_unittest.cc
@@ -29,7 +29,7 @@ TEST(JSONReaderTest, Reading) {
// some whitespace checking
std::unique_ptr<Value> root = JSONReader().ReadToValue(" null ");
ASSERT_TRUE(root);
- EXPECT_TRUE(root->IsType(Value::Type::NONE));
+ EXPECT_TRUE(root->IsType(Value::TYPE_NULL));
}
{
@@ -41,23 +41,23 @@ TEST(JSONReaderTest, Reading) {
// Simple bool
std::unique_ptr<Value> root = JSONReader().ReadToValue("true ");
ASSERT_TRUE(root);
- EXPECT_TRUE(root->IsType(Value::Type::BOOLEAN));
+ EXPECT_TRUE(root->IsType(Value::TYPE_BOOLEAN));
}
{
// Embedded comment
std::unique_ptr<Value> root = JSONReader().ReadToValue("/* comment */null");
ASSERT_TRUE(root);
- EXPECT_TRUE(root->IsType(Value::Type::NONE));
+ EXPECT_TRUE(root->IsType(Value::TYPE_NULL));
root = JSONReader().ReadToValue("40 /* comment */");
ASSERT_TRUE(root);
- EXPECT_TRUE(root->IsType(Value::Type::INTEGER));
+ EXPECT_TRUE(root->IsType(Value::TYPE_INTEGER));
root = JSONReader().ReadToValue("true // comment");
ASSERT_TRUE(root);
- EXPECT_TRUE(root->IsType(Value::Type::BOOLEAN));
+ EXPECT_TRUE(root->IsType(Value::TYPE_BOOLEAN));
root = JSONReader().ReadToValue("/* comment */\"sample string\"");
ASSERT_TRUE(root);
- EXPECT_TRUE(root->IsType(Value::Type::STRING));
+ EXPECT_TRUE(root->IsType(Value::TYPE_STRING));
std::string value;
EXPECT_TRUE(root->GetAsString(&value));
EXPECT_EQ("sample string", value);
@@ -75,7 +75,7 @@ TEST(JSONReaderTest, Reading) {
EXPECT_EQ(3u, list->GetSize());
root = JSONReader().ReadToValue("/* comment **/42");
ASSERT_TRUE(root);
- EXPECT_TRUE(root->IsType(Value::Type::INTEGER));
+ EXPECT_TRUE(root->IsType(Value::TYPE_INTEGER));
EXPECT_TRUE(root->GetAsInteger(&int_val));
EXPECT_EQ(42, int_val);
root = JSONReader().ReadToValue(
@@ -83,7 +83,7 @@ TEST(JSONReaderTest, Reading) {
"// */ 43\n"
"44");
ASSERT_TRUE(root);
- EXPECT_TRUE(root->IsType(Value::Type::INTEGER));
+ EXPECT_TRUE(root->IsType(Value::TYPE_INTEGER));
EXPECT_TRUE(root->GetAsInteger(&int_val));
EXPECT_EQ(44, int_val);
}
@@ -92,7 +92,7 @@ TEST(JSONReaderTest, Reading) {
// Test number formats
std::unique_ptr<Value> root = JSONReader().ReadToValue("43");
ASSERT_TRUE(root);
- EXPECT_TRUE(root->IsType(Value::Type::INTEGER));
+ EXPECT_TRUE(root->IsType(Value::TYPE_INTEGER));
int int_val = 0;
EXPECT_TRUE(root->GetAsInteger(&int_val));
EXPECT_EQ(43, int_val);
@@ -110,7 +110,7 @@ TEST(JSONReaderTest, Reading) {
// clause).
std::unique_ptr<Value> root = JSONReader().ReadToValue("0");
ASSERT_TRUE(root);
- EXPECT_TRUE(root->IsType(Value::Type::INTEGER));
+ EXPECT_TRUE(root->IsType(Value::TYPE_INTEGER));
int int_val = 1;
EXPECT_TRUE(root->GetAsInteger(&int_val));
EXPECT_EQ(0, int_val);
@@ -122,13 +122,13 @@ TEST(JSONReaderTest, Reading) {
std::unique_ptr<Value> root = JSONReader().ReadToValue("2147483648");
ASSERT_TRUE(root);
double double_val;
- EXPECT_TRUE(root->IsType(Value::Type::DOUBLE));
+ EXPECT_TRUE(root->IsType(Value::TYPE_DOUBLE));
double_val = 0.0;
EXPECT_TRUE(root->GetAsDouble(&double_val));
EXPECT_DOUBLE_EQ(2147483648.0, double_val);
root = JSONReader().ReadToValue("-2147483649");
ASSERT_TRUE(root);
- EXPECT_TRUE(root->IsType(Value::Type::DOUBLE));
+ EXPECT_TRUE(root->IsType(Value::TYPE_DOUBLE));
double_val = 0.0;
EXPECT_TRUE(root->GetAsDouble(&double_val));
EXPECT_DOUBLE_EQ(-2147483649.0, double_val);
@@ -138,42 +138,42 @@ TEST(JSONReaderTest, Reading) {
// Parse a double
std::unique_ptr<Value> root = JSONReader().ReadToValue("43.1");
ASSERT_TRUE(root);
- EXPECT_TRUE(root->IsType(Value::Type::DOUBLE));
+ EXPECT_TRUE(root->IsType(Value::TYPE_DOUBLE));
double double_val = 0.0;
EXPECT_TRUE(root->GetAsDouble(&double_val));
EXPECT_DOUBLE_EQ(43.1, double_val);
root = JSONReader().ReadToValue("4.3e-1");
ASSERT_TRUE(root);
- EXPECT_TRUE(root->IsType(Value::Type::DOUBLE));
+ EXPECT_TRUE(root->IsType(Value::TYPE_DOUBLE));
double_val = 0.0;
EXPECT_TRUE(root->GetAsDouble(&double_val));
EXPECT_DOUBLE_EQ(.43, double_val);
root = JSONReader().ReadToValue("2.1e0");
ASSERT_TRUE(root);
- EXPECT_TRUE(root->IsType(Value::Type::DOUBLE));
+ EXPECT_TRUE(root->IsType(Value::TYPE_DOUBLE));
double_val = 0.0;
EXPECT_TRUE(root->GetAsDouble(&double_val));
EXPECT_DOUBLE_EQ(2.1, double_val);
root = JSONReader().ReadToValue("2.1e+0001");
ASSERT_TRUE(root);
- EXPECT_TRUE(root->IsType(Value::Type::DOUBLE));
+ EXPECT_TRUE(root->IsType(Value::TYPE_DOUBLE));
double_val = 0.0;
EXPECT_TRUE(root->GetAsDouble(&double_val));
EXPECT_DOUBLE_EQ(21.0, double_val);
root = JSONReader().ReadToValue("0.01");
ASSERT_TRUE(root);
- EXPECT_TRUE(root->IsType(Value::Type::DOUBLE));
+ EXPECT_TRUE(root->IsType(Value::TYPE_DOUBLE));
double_val = 0.0;
EXPECT_TRUE(root->GetAsDouble(&double_val));
EXPECT_DOUBLE_EQ(0.01, double_val);
root = JSONReader().ReadToValue("1.00");
ASSERT_TRUE(root);
- EXPECT_TRUE(root->IsType(Value::Type::DOUBLE));
+ EXPECT_TRUE(root->IsType(Value::TYPE_DOUBLE));
double_val = 0.0;
EXPECT_TRUE(root->GetAsDouble(&double_val));
EXPECT_DOUBLE_EQ(1.0, double_val);
@@ -213,7 +213,7 @@ TEST(JSONReaderTest, Reading) {
// Test string parser
std::unique_ptr<Value> root = JSONReader().ReadToValue("\"hello world\"");
ASSERT_TRUE(root);
- EXPECT_TRUE(root->IsType(Value::Type::STRING));
+ EXPECT_TRUE(root->IsType(Value::TYPE_STRING));
std::string str_val;
EXPECT_TRUE(root->GetAsString(&str_val));
EXPECT_EQ("hello world", str_val);
@@ -223,7 +223,7 @@ TEST(JSONReaderTest, Reading) {
// Empty string
std::unique_ptr<Value> root = JSONReader().ReadToValue("\"\"");
ASSERT_TRUE(root);
- EXPECT_TRUE(root->IsType(Value::Type::STRING));
+ EXPECT_TRUE(root->IsType(Value::TYPE_STRING));
std::string str_val;
EXPECT_TRUE(root->GetAsString(&str_val));
EXPECT_EQ("", str_val);
@@ -234,7 +234,7 @@ TEST(JSONReaderTest, Reading) {
std::unique_ptr<Value> root =
JSONReader().ReadToValue("\" \\\"\\\\\\/\\b\\f\\n\\r\\t\\v\"");
ASSERT_TRUE(root);
- EXPECT_TRUE(root->IsType(Value::Type::STRING));
+ EXPECT_TRUE(root->IsType(Value::TYPE_STRING));
std::string str_val;
EXPECT_TRUE(root->GetAsString(&str_val));
EXPECT_EQ(" \"\\/\b\f\n\r\t\v", str_val);
@@ -245,7 +245,7 @@ TEST(JSONReaderTest, Reading) {
std::unique_ptr<Value> root =
JSONReader().ReadToValue("\"\\x41\\x00\\u1234\"");
ASSERT_TRUE(root);
- EXPECT_TRUE(root->IsType(Value::Type::STRING));
+ EXPECT_TRUE(root->IsType(Value::TYPE_STRING));
std::string str_val;
EXPECT_TRUE(root->GetAsString(&str_val));
EXPECT_EQ(std::wstring(L"A\0\x1234", 3), UTF8ToWide(str_val));
@@ -319,7 +319,7 @@ TEST(JSONReaderTest, Reading) {
EXPECT_EQ(1U, list->GetSize());
Value* tmp_value = nullptr;
ASSERT_TRUE(list->Get(0, &tmp_value));
- EXPECT_TRUE(tmp_value->IsType(Value::Type::BOOLEAN));
+ EXPECT_TRUE(tmp_value->IsType(Value::TYPE_BOOLEAN));
bool bool_value = false;
EXPECT_TRUE(tmp_value->GetAsBoolean(&bool_value));
EXPECT_TRUE(bool_value);
@@ -348,7 +348,7 @@ TEST(JSONReaderTest, Reading) {
EXPECT_DOUBLE_EQ(9.87654321, double_val);
Value* null_val = nullptr;
ASSERT_TRUE(dict_val->Get("null", &null_val));
- EXPECT_TRUE(null_val->IsType(Value::Type::NONE));
+ EXPECT_TRUE(null_val->IsType(Value::TYPE_NULL));
std::string str_val;
EXPECT_TRUE(dict_val->GetString("S", &str_val));
EXPECT_EQ("str", str_val);
@@ -486,7 +486,7 @@ TEST(JSONReaderTest, Reading) {
std::unique_ptr<Value> root =
JSONReader().ReadToValue("\"\xe7\xbd\x91\xe9\xa1\xb5\"");
ASSERT_TRUE(root);
- EXPECT_TRUE(root->IsType(Value::Type::STRING));
+ EXPECT_TRUE(root->IsType(Value::TYPE_STRING));
std::string str_val;
EXPECT_TRUE(root->GetAsString(&str_val));
EXPECT_EQ(L"\x7f51\x9875", UTF8ToWide(str_val));
@@ -510,7 +510,7 @@ TEST(JSONReaderTest, Reading) {
// Test utf16 encoded strings.
std::unique_ptr<Value> root = JSONReader().ReadToValue("\"\\u20ac3,14\"");
ASSERT_TRUE(root);
- EXPECT_TRUE(root->IsType(Value::Type::STRING));
+ EXPECT_TRUE(root->IsType(Value::TYPE_STRING));
std::string str_val;
EXPECT_TRUE(root->GetAsString(&str_val));
EXPECT_EQ(
@@ -520,7 +520,7 @@ TEST(JSONReaderTest, Reading) {
root = JSONReader().ReadToValue("\"\\ud83d\\udca9\\ud83d\\udc6c\"");
ASSERT_TRUE(root);
- EXPECT_TRUE(root->IsType(Value::Type::STRING));
+ EXPECT_TRUE(root->IsType(Value::TYPE_STRING));
str_val.clear();
EXPECT_TRUE(root->GetAsString(&str_val));
EXPECT_EQ("\xf0\x9f\x92\xa9\xf0\x9f\x91\xac", str_val);
@@ -548,7 +548,7 @@ TEST(JSONReaderTest, Reading) {
{
// Test literal root objects.
std::unique_ptr<Value> root = JSONReader::Read("null");
- EXPECT_TRUE(root->IsType(Value::Type::NONE));
+ EXPECT_TRUE(root->IsType(Value::TYPE_NULL));
root = JSONReader::Read("true");
ASSERT_TRUE(root);
@@ -583,7 +583,7 @@ TEST(JSONReaderTest, ReadFromFile) {
JSONReader reader;
std::unique_ptr<Value> root(reader.ReadToValue(input));
ASSERT_TRUE(root) << reader.GetErrorMessage();
- EXPECT_TRUE(root->IsType(Value::Type::DICTIONARY));
+ EXPECT_TRUE(root->IsType(Value::TYPE_DICTIONARY));
}
#endif // !__ANDROID__ && !__ANDROID_HOST__
diff --git a/base/json/json_string_value_serializer.cc b/base/json/json_string_value_serializer.cc
index 2e46ab387a..cd786db9e7 100644
--- a/base/json/json_string_value_serializer.cc
+++ b/base/json/json_string_value_serializer.cc
@@ -41,15 +41,18 @@ bool JSONStringValueSerializer::SerializeInternal(const Value& root,
}
JSONStringValueDeserializer::JSONStringValueDeserializer(
- const base::StringPiece& json_string,
- int options)
- : json_string_(json_string), options_(options) {}
+ const base::StringPiece& json_string)
+ : json_string_(json_string),
+ allow_trailing_comma_(false) {
+}
JSONStringValueDeserializer::~JSONStringValueDeserializer() {}
std::unique_ptr<Value> JSONStringValueDeserializer::Deserialize(
int* error_code,
std::string* error_str) {
- return base::JSONReader::ReadAndReturnError(json_string_, options_,
- error_code, error_str);
+ return base::JSONReader::ReadAndReturnError(
+ json_string_, allow_trailing_comma_ ? base::JSON_ALLOW_TRAILING_COMMAS
+ : base::JSON_PARSE_RFC,
+ error_code, error_str);
}
diff --git a/base/json/json_string_value_serializer.h b/base/json/json_string_value_serializer.h
index 55a53e207f..a97da23920 100644
--- a/base/json/json_string_value_serializer.h
+++ b/base/json/json_string_value_serializer.h
@@ -47,10 +47,8 @@ class BASE_EXPORT JSONStringValueSerializer : public base::ValueSerializer {
class BASE_EXPORT JSONStringValueDeserializer : public base::ValueDeserializer {
public:
// This retains a reference to the contents of |json_string|, so the data
- // must outlive the JSONStringValueDeserializer. |options| is a bitmask of
- // JSONParserOptions.
- explicit JSONStringValueDeserializer(const base::StringPiece& json_string,
- int options = 0);
+ // must outlive the JSONStringValueDeserializer.
+ explicit JSONStringValueDeserializer(const base::StringPiece& json_string);
~JSONStringValueDeserializer() override;
@@ -64,10 +62,15 @@ class BASE_EXPORT JSONStringValueDeserializer : public base::ValueDeserializer {
std::unique_ptr<base::Value> Deserialize(int* error_code,
std::string* error_message) override;
+ void set_allow_trailing_comma(bool new_value) {
+ allow_trailing_comma_ = new_value;
+ }
+
private:
// Data is owned by the caller of the constructor.
base::StringPiece json_string_;
- const int options_;
+ // If true, deserialization will allow trailing commas.
+ bool allow_trailing_comma_;
DISALLOW_COPY_AND_ASSIGN(JSONStringValueDeserializer);
};
diff --git a/base/json/json_value_converter.h b/base/json/json_value_converter.h
index 68ebfa23de..4cca034f33 100644
--- a/base/json/json_value_converter.h
+++ b/base/json/json_value_converter.h
@@ -14,7 +14,8 @@
#include "base/base_export.h"
#include "base/logging.h"
#include "base/macros.h"
-#include "base/memory/ptr_util.h"
+#include "base/memory/scoped_vector.h"
+#include "base/stl_util.h"
#include "base/strings/string16.h"
#include "base/strings/string_piece.h"
#include "base/values.h"
@@ -65,9 +66,9 @@
// }
// };
//
-// For repeated field, we just assume std::vector<std::unique_ptr<ElementType>>
-// for its container and you can put RegisterRepeatedInt or some other types.
-// Use RegisterRepeatedMessage for nested repeated fields.
+// For repeated field, we just assume ScopedVector for its container
+// and you can put RegisterRepeatedInt or some other types. Use
+// RegisterRepeatedMessage for nested repeated fields.
//
// Sometimes JSON format uses string representations for other types such
// like enum, timestamp, or URL. You can use RegisterCustomField method
@@ -199,7 +200,7 @@ class ValueFieldConverter : public ValueConverter<FieldType> {
public:
typedef bool(*ConvertFunc)(const base::Value* value, FieldType* field);
- explicit ValueFieldConverter(ConvertFunc convert_func)
+ ValueFieldConverter(ConvertFunc convert_func)
: convert_func_(convert_func) {}
bool Convert(const base::Value& value, FieldType* field) const override {
@@ -217,7 +218,7 @@ class CustomFieldConverter : public ValueConverter<FieldType> {
public:
typedef bool(*ConvertFunc)(const StringPiece& value, FieldType* field);
- explicit CustomFieldConverter(ConvertFunc convert_func)
+ CustomFieldConverter(ConvertFunc convert_func)
: convert_func_(convert_func) {}
bool Convert(const base::Value& value, FieldType* field) const override {
@@ -247,13 +248,12 @@ class NestedValueConverter : public ValueConverter<NestedType> {
};
template <typename Element>
-class RepeatedValueConverter
- : public ValueConverter<std::vector<std::unique_ptr<Element>>> {
+class RepeatedValueConverter : public ValueConverter<ScopedVector<Element> > {
public:
RepeatedValueConverter() {}
bool Convert(const base::Value& value,
- std::vector<std::unique_ptr<Element>>* field) const override {
+ ScopedVector<Element>* field) const override {
const base::ListValue* list = NULL;
if (!value.GetAsList(&list)) {
// The field is not a list.
@@ -268,7 +268,7 @@ class RepeatedValueConverter
std::unique_ptr<Element> e(new Element);
if (basic_converter_.Convert(*element, e.get())) {
- field->push_back(std::move(e));
+ field->push_back(e.release());
} else {
DVLOG(1) << "failure at " << i << "-th element";
return false;
@@ -284,12 +284,12 @@ class RepeatedValueConverter
template <typename NestedType>
class RepeatedMessageConverter
- : public ValueConverter<std::vector<std::unique_ptr<NestedType>>> {
+ : public ValueConverter<ScopedVector<NestedType> > {
public:
RepeatedMessageConverter() {}
bool Convert(const base::Value& value,
- std::vector<std::unique_ptr<NestedType>>* field) const override {
+ ScopedVector<NestedType>* field) const override {
const base::ListValue* list = NULL;
if (!value.GetAsList(&list))
return false;
@@ -302,7 +302,7 @@ class RepeatedMessageConverter
std::unique_ptr<NestedType> nested(new NestedType);
if (converter_.Convert(*element, nested.get())) {
- field->push_back(std::move(nested));
+ field->push_back(nested.release());
} else {
DVLOG(1) << "failure at " << i << "-th element";
return false;
@@ -318,15 +318,15 @@ class RepeatedMessageConverter
template <typename NestedType>
class RepeatedCustomValueConverter
- : public ValueConverter<std::vector<std::unique_ptr<NestedType>>> {
+ : public ValueConverter<ScopedVector<NestedType> > {
public:
typedef bool(*ConvertFunc)(const base::Value* value, NestedType* field);
- explicit RepeatedCustomValueConverter(ConvertFunc convert_func)
+ RepeatedCustomValueConverter(ConvertFunc convert_func)
: convert_func_(convert_func) {}
bool Convert(const base::Value& value,
- std::vector<std::unique_ptr<NestedType>>* field) const override {
+ ScopedVector<NestedType>* field) const override {
const base::ListValue* list = NULL;
if (!value.GetAsList(&list))
return false;
@@ -339,7 +339,7 @@ class RepeatedCustomValueConverter
std::unique_ptr<NestedType> nested(new NestedType);
if ((*convert_func_)(element, nested.get())) {
- field->push_back(std::move(nested));
+ field->push_back(nested.release());
} else {
DVLOG(1) << "failure at " << i << "-th element";
return false;
@@ -365,42 +365,41 @@ class JSONValueConverter {
void RegisterIntField(const std::string& field_name,
int StructType::* field) {
- fields_.push_back(MakeUnique<internal::FieldConverter<StructType, int>>(
+ fields_.push_back(new internal::FieldConverter<StructType, int>(
field_name, field, new internal::BasicValueConverter<int>));
}
void RegisterStringField(const std::string& field_name,
std::string StructType::* field) {
- fields_.push_back(
- MakeUnique<internal::FieldConverter<StructType, std::string>>(
- field_name, field, new internal::BasicValueConverter<std::string>));
+ fields_.push_back(new internal::FieldConverter<StructType, std::string>(
+ field_name, field, new internal::BasicValueConverter<std::string>));
}
void RegisterStringField(const std::string& field_name,
string16 StructType::* field) {
- fields_.push_back(
- MakeUnique<internal::FieldConverter<StructType, string16>>(
- field_name, field, new internal::BasicValueConverter<string16>));
+ fields_.push_back(new internal::FieldConverter<StructType, string16>(
+ field_name, field, new internal::BasicValueConverter<string16>));
}
void RegisterBoolField(const std::string& field_name,
bool StructType::* field) {
- fields_.push_back(MakeUnique<internal::FieldConverter<StructType, bool>>(
+ fields_.push_back(new internal::FieldConverter<StructType, bool>(
field_name, field, new internal::BasicValueConverter<bool>));
}
void RegisterDoubleField(const std::string& field_name,
double StructType::* field) {
- fields_.push_back(MakeUnique<internal::FieldConverter<StructType, double>>(
+ fields_.push_back(new internal::FieldConverter<StructType, double>(
field_name, field, new internal::BasicValueConverter<double>));
}
template <class NestedType>
void RegisterNestedField(
const std::string& field_name, NestedType StructType::* field) {
- fields_.push_back(
- MakeUnique<internal::FieldConverter<StructType, NestedType>>(
- field_name, field, new internal::NestedValueConverter<NestedType>));
+ fields_.push_back(new internal::FieldConverter<StructType, NestedType>(
+ field_name,
+ field,
+ new internal::NestedValueConverter<NestedType>));
}
template <typename FieldType>
@@ -408,10 +407,10 @@ class JSONValueConverter {
const std::string& field_name,
FieldType StructType::* field,
bool (*convert_func)(const StringPiece&, FieldType*)) {
- fields_.push_back(
- MakeUnique<internal::FieldConverter<StructType, FieldType>>(
- field_name, field,
- new internal::CustomFieldConverter<FieldType>(convert_func)));
+ fields_.push_back(new internal::FieldConverter<StructType, FieldType>(
+ field_name,
+ field,
+ new internal::CustomFieldConverter<FieldType>(convert_func)));
}
template <typename FieldType>
@@ -419,76 +418,71 @@ class JSONValueConverter {
const std::string& field_name,
FieldType StructType::* field,
bool (*convert_func)(const base::Value*, FieldType*)) {
- fields_.push_back(
- MakeUnique<internal::FieldConverter<StructType, FieldType>>(
- field_name, field,
- new internal::ValueFieldConverter<FieldType>(convert_func)));
+ fields_.push_back(new internal::FieldConverter<StructType, FieldType>(
+ field_name,
+ field,
+ new internal::ValueFieldConverter<FieldType>(convert_func)));
}
- void RegisterRepeatedInt(
- const std::string& field_name,
- std::vector<std::unique_ptr<int>> StructType::*field) {
+ void RegisterRepeatedInt(const std::string& field_name,
+ ScopedVector<int> StructType::* field) {
fields_.push_back(
- MakeUnique<internal::FieldConverter<StructType,
- std::vector<std::unique_ptr<int>>>>(
+ new internal::FieldConverter<StructType, ScopedVector<int> >(
field_name, field, new internal::RepeatedValueConverter<int>));
}
- void RegisterRepeatedString(
- const std::string& field_name,
- std::vector<std::unique_ptr<std::string>> StructType::*field) {
+ void RegisterRepeatedString(const std::string& field_name,
+ ScopedVector<std::string> StructType::* field) {
fields_.push_back(
- MakeUnique<internal::FieldConverter<
- StructType, std::vector<std::unique_ptr<std::string>>>>(
- field_name, field,
+ new internal::FieldConverter<StructType, ScopedVector<std::string> >(
+ field_name,
+ field,
new internal::RepeatedValueConverter<std::string>));
}
- void RegisterRepeatedString(
- const std::string& field_name,
- std::vector<std::unique_ptr<string16>> StructType::*field) {
- fields_.push_back(MakeUnique<internal::FieldConverter<
- StructType, std::vector<std::unique_ptr<string16>>>>(
- field_name, field, new internal::RepeatedValueConverter<string16>));
+ void RegisterRepeatedString(const std::string& field_name,
+ ScopedVector<string16> StructType::* field) {
+ fields_.push_back(
+ new internal::FieldConverter<StructType, ScopedVector<string16> >(
+ field_name,
+ field,
+ new internal::RepeatedValueConverter<string16>));
}
- void RegisterRepeatedDouble(
- const std::string& field_name,
- std::vector<std::unique_ptr<double>> StructType::*field) {
- fields_.push_back(MakeUnique<internal::FieldConverter<
- StructType, std::vector<std::unique_ptr<double>>>>(
- field_name, field, new internal::RepeatedValueConverter<double>));
+ void RegisterRepeatedDouble(const std::string& field_name,
+ ScopedVector<double> StructType::* field) {
+ fields_.push_back(
+ new internal::FieldConverter<StructType, ScopedVector<double> >(
+ field_name, field, new internal::RepeatedValueConverter<double>));
}
- void RegisterRepeatedBool(
- const std::string& field_name,
- std::vector<std::unique_ptr<bool>> StructType::*field) {
- fields_.push_back(MakeUnique<internal::FieldConverter<
- StructType, std::vector<std::unique_ptr<bool>>>>(
- field_name, field, new internal::RepeatedValueConverter<bool>));
+ void RegisterRepeatedBool(const std::string& field_name,
+ ScopedVector<bool> StructType::* field) {
+ fields_.push_back(
+ new internal::FieldConverter<StructType, ScopedVector<bool> >(
+ field_name, field, new internal::RepeatedValueConverter<bool>));
}
template <class NestedType>
void RegisterRepeatedCustomValue(
const std::string& field_name,
- std::vector<std::unique_ptr<NestedType>> StructType::*field,
+ ScopedVector<NestedType> StructType::* field,
bool (*convert_func)(const base::Value*, NestedType*)) {
fields_.push_back(
- MakeUnique<internal::FieldConverter<
- StructType, std::vector<std::unique_ptr<NestedType>>>>(
- field_name, field,
+ new internal::FieldConverter<StructType, ScopedVector<NestedType> >(
+ field_name,
+ field,
new internal::RepeatedCustomValueConverter<NestedType>(
convert_func)));
}
template <class NestedType>
- void RegisterRepeatedMessage(
- const std::string& field_name,
- std::vector<std::unique_ptr<NestedType>> StructType::*field) {
+ void RegisterRepeatedMessage(const std::string& field_name,
+ ScopedVector<NestedType> StructType::* field) {
fields_.push_back(
- MakeUnique<internal::FieldConverter<
- StructType, std::vector<std::unique_ptr<NestedType>>>>(
- field_name, field,
+ new internal::FieldConverter<StructType, ScopedVector<NestedType> >(
+ field_name,
+ field,
new internal::RepeatedMessageConverter<NestedType>));
}
@@ -497,9 +491,9 @@ class JSONValueConverter {
if (!value.GetAsDictionary(&dictionary_value))
return false;
- for (size_t i = 0; i < fields_.size(); ++i) {
+ for(size_t i = 0; i < fields_.size(); ++i) {
const internal::FieldConverterBase<StructType>* field_converter =
- fields_[i].get();
+ fields_[i];
const base::Value* field = NULL;
if (dictionary_value->Get(field_converter->field_path(), &field)) {
if (!field_converter->ConvertField(*field, output)) {
@@ -512,8 +506,7 @@ class JSONValueConverter {
}
private:
- std::vector<std::unique_ptr<internal::FieldConverterBase<StructType>>>
- fields_;
+ ScopedVector<internal::FieldConverterBase<StructType> > fields_;
DISALLOW_COPY_AND_ASSIGN(JSONValueConverter);
};
diff --git a/base/json/json_value_converter_unittest.cc b/base/json/json_value_converter_unittest.cc
index 6a603d3a92..56ade24ac3 100644
--- a/base/json/json_value_converter_unittest.cc
+++ b/base/json/json_value_converter_unittest.cc
@@ -9,6 +9,7 @@
#include <vector>
#include "base/json/json_reader.h"
+#include "base/memory/scoped_vector.h"
#include "base/strings/string_piece.h"
#include "base/values.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -26,8 +27,8 @@ struct SimpleMessage {
bool baz;
bool bstruct;
SimpleEnum simple_enum;
- std::vector<std::unique_ptr<int>> ints;
- std::vector<std::unique_ptr<std::string>> string_values;
+ ScopedVector<int> ints;
+ ScopedVector<std::string> string_values;
SimpleMessage() : foo(0), baz(false), bstruct(false), simple_enum(FOO) {}
static bool ParseSimpleEnum(const StringPiece& value, SimpleEnum* field) {
@@ -79,7 +80,7 @@ struct SimpleMessage {
struct NestedMessage {
double foo;
SimpleMessage child;
- std::vector<std::unique_ptr<SimpleMessage>> children;
+ ScopedVector<SimpleMessage> children;
NestedMessage() : foo(0) {}
@@ -162,7 +163,7 @@ TEST(JSONValueConverterTest, ParseNestedMessage) {
EXPECT_EQ("value_2", *message.child.string_values[1]);
EXPECT_EQ(2, static_cast<int>(message.children.size()));
- const SimpleMessage* first_child = message.children[0].get();
+ const SimpleMessage* first_child = message.children[0];
ASSERT_TRUE(first_child);
EXPECT_EQ(2, first_child->foo);
EXPECT_EQ("foobar", first_child->bar);
@@ -171,7 +172,7 @@ TEST(JSONValueConverterTest, ParseNestedMessage) {
ASSERT_EQ(1U, first_child->string_values.size());
EXPECT_EQ("value_1", *first_child->string_values[0]);
- const SimpleMessage* second_child = message.children[1].get();
+ const SimpleMessage* second_child = message.children[1];
ASSERT_TRUE(second_child);
EXPECT_EQ(3, second_child->foo);
EXPECT_EQ("barbaz", second_child->bar);
diff --git a/base/json/json_value_serializer_unittest.cc b/base/json/json_value_serializer_unittest.cc
index 1d58c61e04..0c079b7623 100644
--- a/base/json/json_value_serializer_unittest.cc
+++ b/base/json/json_value_serializer_unittest.cc
@@ -78,10 +78,11 @@ void CheckJSONIsStillTheSame(const Value& value) {
}
void ValidateJsonList(const std::string& json) {
- std::unique_ptr<ListValue> list = ListValue::From(JSONReader::Read(json));
- ASSERT_TRUE(list);
+ std::unique_ptr<Value> root = JSONReader::Read(json);
+ ASSERT_TRUE(root.get() && root->IsType(Value::TYPE_LIST));
+ ListValue* list = static_cast<ListValue*>(root.get());
ASSERT_EQ(1U, list->GetSize());
- Value* elt = nullptr;
+ Value* elt = NULL;
ASSERT_TRUE(list->Get(0, &elt));
int value = 0;
ASSERT_TRUE(elt && elt->GetAsInteger(&value));
@@ -97,7 +98,7 @@ TEST(JSONValueDeserializerTest, ReadProperJSONFromString) {
std::string error_message;
std::unique_ptr<Value> value =
str_deserializer.Deserialize(&error_code, &error_message);
- ASSERT_TRUE(value);
+ ASSERT_TRUE(value.get());
ASSERT_EQ(0, error_code);
ASSERT_TRUE(error_message.empty());
// Verify if the same JSON is still there.
@@ -108,7 +109,7 @@ TEST(JSONValueDeserializerTest, ReadProperJSONFromString) {
TEST(JSONValueDeserializerTest, ReadProperJSONFromStringPiece) {
// Create a StringPiece for the substring of kProperJSONPadded that matches
// kProperJSON.
- StringPiece proper_json(kProperJSONPadded);
+ base::StringPiece proper_json(kProperJSONPadded);
proper_json = proper_json.substr(5, proper_json.length() - 10);
JSONStringValueDeserializer str_deserializer(proper_json);
@@ -116,7 +117,7 @@ TEST(JSONValueDeserializerTest, ReadProperJSONFromStringPiece) {
std::string error_message;
std::unique_ptr<Value> value =
str_deserializer.Deserialize(&error_code, &error_message);
- ASSERT_TRUE(value);
+ ASSERT_TRUE(value.get());
ASSERT_EQ(0, error_code);
ASSERT_TRUE(error_message.empty());
// Verify if the same JSON is still there.
@@ -133,14 +134,13 @@ TEST(JSONValueDeserializerTest, ReadJSONWithTrailingCommasFromString) {
std::string error_message;
std::unique_ptr<Value> value =
str_deserializer.Deserialize(&error_code, &error_message);
- ASSERT_FALSE(value);
+ ASSERT_FALSE(value.get());
ASSERT_NE(0, error_code);
ASSERT_FALSE(error_message.empty());
- // Repeat with commas allowed.
- JSONStringValueDeserializer str_deserializer2(kProperJSONWithCommas,
- JSON_ALLOW_TRAILING_COMMAS);
- value = str_deserializer2.Deserialize(&error_code, &error_message);
- ASSERT_TRUE(value);
+ // Now the flag is set and it must pass.
+ str_deserializer.set_allow_trailing_comma(true);
+ value = str_deserializer.Deserialize(&error_code, &error_message);
+ ASSERT_TRUE(value.get());
ASSERT_EQ(JSONReader::JSON_TRAILING_COMMA, error_code);
// Verify if the same JSON is still there.
CheckJSONIsStillTheSame(*value);
@@ -151,7 +151,7 @@ TEST(JSONValueDeserializerTest, ReadProperJSONFromFile) {
ScopedTempDir tempdir;
ASSERT_TRUE(tempdir.CreateUniqueTempDir());
// Write it down in the file.
- FilePath temp_file(tempdir.GetPath().AppendASCII("test.json"));
+ FilePath temp_file(tempdir.path().AppendASCII("test.json"));
ASSERT_EQ(static_cast<int>(strlen(kProperJSON)),
WriteFile(temp_file, kProperJSON, strlen(kProperJSON)));
@@ -162,7 +162,7 @@ TEST(JSONValueDeserializerTest, ReadProperJSONFromFile) {
std::string error_message;
std::unique_ptr<Value> value =
file_deserializer.Deserialize(&error_code, &error_message);
- ASSERT_TRUE(value);
+ ASSERT_TRUE(value.get());
ASSERT_EQ(0, error_code);
ASSERT_TRUE(error_message.empty());
// Verify if the same JSON is still there.
@@ -175,7 +175,7 @@ TEST(JSONValueDeserializerTest, ReadJSONWithCommasFromFile) {
ScopedTempDir tempdir;
ASSERT_TRUE(tempdir.CreateUniqueTempDir());
// Write it down in the file.
- FilePath temp_file(tempdir.GetPath().AppendASCII("test.json"));
+ FilePath temp_file(tempdir.path().AppendASCII("test.json"));
ASSERT_EQ(static_cast<int>(strlen(kProperJSONWithCommas)),
WriteFile(temp_file, kProperJSONWithCommas,
strlen(kProperJSONWithCommas)));
@@ -187,31 +187,31 @@ TEST(JSONValueDeserializerTest, ReadJSONWithCommasFromFile) {
std::string error_message;
std::unique_ptr<Value> value =
file_deserializer.Deserialize(&error_code, &error_message);
- ASSERT_FALSE(value);
+ ASSERT_FALSE(value.get());
ASSERT_NE(0, error_code);
ASSERT_FALSE(error_message.empty());
- // Repeat with commas allowed.
- JSONFileValueDeserializer file_deserializer2(temp_file,
- JSON_ALLOW_TRAILING_COMMAS);
- value = file_deserializer2.Deserialize(&error_code, &error_message);
- ASSERT_TRUE(value);
+ // Now the flag is set and it must pass.
+ file_deserializer.set_allow_trailing_comma(true);
+ value = file_deserializer.Deserialize(&error_code, &error_message);
+ ASSERT_TRUE(value.get());
ASSERT_EQ(JSONReader::JSON_TRAILING_COMMA, error_code);
// Verify if the same JSON is still there.
CheckJSONIsStillTheSame(*value);
}
TEST(JSONValueDeserializerTest, AllowTrailingComma) {
+ std::unique_ptr<Value> root;
+ std::unique_ptr<Value> root_expected;
static const char kTestWithCommas[] = "{\"key\": [true,],}";
static const char kTestNoCommas[] = "{\"key\": [true]}";
- JSONStringValueDeserializer deserializer(kTestWithCommas,
- JSON_ALLOW_TRAILING_COMMAS);
+ JSONStringValueDeserializer deserializer(kTestWithCommas);
+ deserializer.set_allow_trailing_comma(true);
JSONStringValueDeserializer deserializer_expected(kTestNoCommas);
- std::unique_ptr<Value> root = deserializer.Deserialize(nullptr, nullptr);
- ASSERT_TRUE(root);
- std::unique_ptr<Value> root_expected;
- root_expected = deserializer_expected.Deserialize(nullptr, nullptr);
- ASSERT_TRUE(root_expected);
+ root = deserializer.Deserialize(NULL, NULL);
+ ASSERT_TRUE(root.get());
+ root_expected = deserializer_expected.Deserialize(NULL, NULL);
+ ASSERT_TRUE(root_expected.get());
ASSERT_TRUE(root->Equals(root_expected.get()));
}
@@ -219,14 +219,16 @@ TEST(JSONValueSerializerTest, Roundtrip) {
static const char kOriginalSerialization[] =
"{\"bool\":true,\"double\":3.14,\"int\":42,\"list\":[1,2],\"null\":null}";
JSONStringValueDeserializer deserializer(kOriginalSerialization);
- std::unique_ptr<DictionaryValue> root_dict =
- DictionaryValue::From(deserializer.Deserialize(nullptr, nullptr));
- ASSERT_TRUE(root_dict);
+ std::unique_ptr<Value> root = deserializer.Deserialize(NULL, NULL);
+ ASSERT_TRUE(root.get());
+ ASSERT_TRUE(root->IsType(Value::TYPE_DICTIONARY));
+
+ DictionaryValue* root_dict = static_cast<DictionaryValue*>(root.get());
- Value* null_value = nullptr;
+ Value* null_value = NULL;
ASSERT_TRUE(root_dict->Get("null", &null_value));
ASSERT_TRUE(null_value);
- ASSERT_TRUE(null_value->IsType(Value::Type::NONE));
+ ASSERT_TRUE(null_value->IsType(Value::TYPE_NULL));
bool bool_value = false;
ASSERT_TRUE(root_dict->GetBoolean("bool", &bool_value));
@@ -327,9 +329,8 @@ TEST(JSONValueSerializerTest, UnicodeStrings) {
// escaped ascii text -> json
JSONStringValueDeserializer deserializer(kExpected);
- std::unique_ptr<Value> deserial_root =
- deserializer.Deserialize(nullptr, nullptr);
- ASSERT_TRUE(deserial_root);
+ std::unique_ptr<Value> deserial_root = deserializer.Deserialize(NULL, NULL);
+ ASSERT_TRUE(deserial_root.get());
DictionaryValue* dict_root =
static_cast<DictionaryValue*>(deserial_root.get());
string16 web_value;
@@ -352,9 +353,8 @@ TEST(JSONValueSerializerTest, HexStrings) {
// escaped ascii text -> json
JSONStringValueDeserializer deserializer(kExpected);
- std::unique_ptr<Value> deserial_root =
- deserializer.Deserialize(nullptr, nullptr);
- ASSERT_TRUE(deserial_root);
+ std::unique_ptr<Value> deserial_root = deserializer.Deserialize(NULL, NULL);
+ ASSERT_TRUE(deserial_root.get());
DictionaryValue* dict_root =
static_cast<DictionaryValue*>(deserial_root.get());
string16 test_value;
@@ -364,8 +364,8 @@ TEST(JSONValueSerializerTest, HexStrings) {
// Test converting escaped regular chars
static const char kEscapedChars[] = "{\"test\":\"\\u0067\\u006f\"}";
JSONStringValueDeserializer deserializer2(kEscapedChars);
- deserial_root = deserializer2.Deserialize(nullptr, nullptr);
- ASSERT_TRUE(deserial_root);
+ deserial_root = deserializer2.Deserialize(NULL, NULL);
+ ASSERT_TRUE(deserial_root.get());
dict_root = static_cast<DictionaryValue*>(deserial_root.get());
ASSERT_TRUE(dict_root->GetString("test", &test_value));
ASSERT_EQ(ASCIIToUTF16("go"), test_value);
@@ -380,48 +380,54 @@ TEST(JSONValueSerializerTest, JSONReaderComments) {
ValidateJsonList("[ 1 //// ,2\r\n ]");
// It's ok to have a comment in a string.
- std::unique_ptr<ListValue> list =
- ListValue::From(JSONReader::Read("[\"// ok\\n /* foo */ \"]"));
- ASSERT_TRUE(list);
+ std::unique_ptr<Value> root = JSONReader::Read("[\"// ok\\n /* foo */ \"]");
+ ASSERT_TRUE(root.get() && root->IsType(Value::TYPE_LIST));
+ ListValue* list = static_cast<ListValue*>(root.get());
ASSERT_EQ(1U, list->GetSize());
- Value* elt = nullptr;
+ Value* elt = NULL;
ASSERT_TRUE(list->Get(0, &elt));
std::string value;
ASSERT_TRUE(elt && elt->GetAsString(&value));
ASSERT_EQ("// ok\n /* foo */ ", value);
// You can't nest comments.
- ASSERT_FALSE(JSONReader::Read("/* /* inner */ outer */ [ 1 ]"));
+ root = JSONReader::Read("/* /* inner */ outer */ [ 1 ]");
+ ASSERT_FALSE(root.get());
// Not a open comment token.
- ASSERT_FALSE(JSONReader::Read("/ * * / [1]"));
+ root = JSONReader::Read("/ * * / [1]");
+ ASSERT_FALSE(root.get());
}
#if !defined(__ANDROID__) && !defined(__ANDROID_HOST__)
-
class JSONFileValueSerializerTest : public testing::Test {
protected:
void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }
- ScopedTempDir temp_dir_;
+ base::ScopedTempDir temp_dir_;
};
TEST_F(JSONFileValueSerializerTest, Roundtrip) {
- FilePath original_file_path;
+ base::FilePath original_file_path;
ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &original_file_path));
- original_file_path = original_file_path.AppendASCII("serializer_test.json");
+ original_file_path =
+ original_file_path.Append(FILE_PATH_LITERAL("serializer_test.json"));
ASSERT_TRUE(PathExists(original_file_path));
JSONFileValueDeserializer deserializer(original_file_path);
- std::unique_ptr<DictionaryValue> root_dict =
- DictionaryValue::From(deserializer.Deserialize(nullptr, nullptr));
- ASSERT_TRUE(root_dict);
+ std::unique_ptr<Value> root;
+ root = deserializer.Deserialize(NULL, NULL);
- Value* null_value = nullptr;
+ ASSERT_TRUE(root.get());
+ ASSERT_TRUE(root->IsType(Value::TYPE_DICTIONARY));
+
+ DictionaryValue* root_dict = static_cast<DictionaryValue*>(root.get());
+
+ Value* null_value = NULL;
ASSERT_TRUE(root_dict->Get("null", &null_value));
ASSERT_TRUE(null_value);
- ASSERT_TRUE(null_value->IsType(Value::Type::NONE));
+ ASSERT_TRUE(null_value->IsType(Value::TYPE_NULL));
bool bool_value = false;
ASSERT_TRUE(root_dict->GetBoolean("bool", &bool_value));
@@ -436,34 +442,35 @@ TEST_F(JSONFileValueSerializerTest, Roundtrip) {
ASSERT_EQ("hello", string_value);
// Now try writing.
- const FilePath written_file_path =
- temp_dir_.GetPath().AppendASCII("test_output.js");
+ const base::FilePath written_file_path =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("test_output.js"));
ASSERT_FALSE(PathExists(written_file_path));
JSONFileValueSerializer serializer(written_file_path);
- ASSERT_TRUE(serializer.Serialize(*root_dict));
+ ASSERT_TRUE(serializer.Serialize(*root));
ASSERT_TRUE(PathExists(written_file_path));
// Now compare file contents.
EXPECT_TRUE(TextContentsEqual(original_file_path, written_file_path));
- EXPECT_TRUE(DeleteFile(written_file_path, false));
+ EXPECT_TRUE(base::DeleteFile(written_file_path, false));
}
TEST_F(JSONFileValueSerializerTest, RoundtripNested) {
- FilePath original_file_path;
+ base::FilePath original_file_path;
ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &original_file_path));
- original_file_path =
- original_file_path.AppendASCII("serializer_nested_test.json");
+ original_file_path = original_file_path.Append(
+ FILE_PATH_LITERAL("serializer_nested_test.json"));
ASSERT_TRUE(PathExists(original_file_path));
JSONFileValueDeserializer deserializer(original_file_path);
- std::unique_ptr<Value> root = deserializer.Deserialize(nullptr, nullptr);
- ASSERT_TRUE(root);
+ std::unique_ptr<Value> root;
+ root = deserializer.Deserialize(NULL, NULL);
+ ASSERT_TRUE(root.get());
// Now try writing.
- FilePath written_file_path =
- temp_dir_.GetPath().AppendASCII("test_output.json");
+ base::FilePath written_file_path = temp_dir_.path().Append(
+ FILE_PATH_LITERAL("test_output.json"));
ASSERT_FALSE(PathExists(written_file_path));
JSONFileValueSerializer serializer(written_file_path);
@@ -472,18 +479,19 @@ TEST_F(JSONFileValueSerializerTest, RoundtripNested) {
// Now compare file contents.
EXPECT_TRUE(TextContentsEqual(original_file_path, written_file_path));
- EXPECT_TRUE(DeleteFile(written_file_path, false));
+ EXPECT_TRUE(base::DeleteFile(written_file_path, false));
}
TEST_F(JSONFileValueSerializerTest, NoWhitespace) {
- FilePath source_file_path;
+ base::FilePath source_file_path;
ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &source_file_path));
- source_file_path =
- source_file_path.AppendASCII("serializer_test_nowhitespace.json");
+ source_file_path = source_file_path.Append(
+ FILE_PATH_LITERAL("serializer_test_nowhitespace.json"));
ASSERT_TRUE(PathExists(source_file_path));
JSONFileValueDeserializer deserializer(source_file_path);
- std::unique_ptr<Value> root = deserializer.Deserialize(nullptr, nullptr);
- ASSERT_TRUE(root);
+ std::unique_ptr<Value> root;
+ root = deserializer.Deserialize(NULL, NULL);
+ ASSERT_TRUE(root.get());
}
#endif // !__ANDROID__ && !__ANDROID_HOST__
diff --git a/base/json/json_writer.cc b/base/json/json_writer.cc
index 07b9d5091c..0b658eed59 100644
--- a/base/json/json_writer.cc
+++ b/base/json/json_writer.cc
@@ -57,12 +57,12 @@ JSONWriter::JSONWriter(int options, std::string* json)
bool JSONWriter::BuildJSONString(const Value& node, size_t depth) {
switch (node.GetType()) {
- case Value::Type::NONE: {
+ case Value::TYPE_NULL: {
json_string_->append("null");
return true;
}
- case Value::Type::BOOLEAN: {
+ case Value::TYPE_BOOLEAN: {
bool value;
bool result = node.GetAsBoolean(&value);
DCHECK(result);
@@ -70,7 +70,7 @@ bool JSONWriter::BuildJSONString(const Value& node, size_t depth) {
return result;
}
- case Value::Type::INTEGER: {
+ case Value::TYPE_INTEGER: {
int value;
bool result = node.GetAsInteger(&value);
DCHECK(result);
@@ -78,7 +78,7 @@ bool JSONWriter::BuildJSONString(const Value& node, size_t depth) {
return result;
}
- case Value::Type::DOUBLE: {
+ case Value::TYPE_DOUBLE: {
double value;
bool result = node.GetAsDouble(&value);
DCHECK(result);
@@ -110,7 +110,7 @@ bool JSONWriter::BuildJSONString(const Value& node, size_t depth) {
return result;
}
- case Value::Type::STRING: {
+ case Value::TYPE_STRING: {
std::string value;
bool result = node.GetAsString(&value);
DCHECK(result);
@@ -118,7 +118,7 @@ bool JSONWriter::BuildJSONString(const Value& node, size_t depth) {
return result;
}
- case Value::Type::LIST: {
+ case Value::TYPE_LIST: {
json_string_->push_back('[');
if (pretty_print_)
json_string_->push_back(' ');
@@ -128,7 +128,7 @@ bool JSONWriter::BuildJSONString(const Value& node, size_t depth) {
bool result = node.GetAsList(&list);
DCHECK(result);
for (const auto& value : *list) {
- if (omit_binary_values_ && value->GetType() == Value::Type::BINARY)
+ if (omit_binary_values_ && value->GetType() == Value::TYPE_BINARY)
continue;
if (first_value_has_been_output) {
@@ -149,7 +149,7 @@ bool JSONWriter::BuildJSONString(const Value& node, size_t depth) {
return result;
}
- case Value::Type::DICTIONARY: {
+ case Value::TYPE_DICTIONARY: {
json_string_->push_back('{');
if (pretty_print_)
json_string_->append(kPrettyPrintLineEnding);
@@ -161,7 +161,7 @@ bool JSONWriter::BuildJSONString(const Value& node, size_t depth) {
for (DictionaryValue::Iterator itr(*dict); !itr.IsAtEnd();
itr.Advance()) {
if (omit_binary_values_ &&
- itr.value().GetType() == Value::Type::BINARY) {
+ itr.value().GetType() == Value::TYPE_BINARY) {
continue;
}
@@ -194,7 +194,7 @@ bool JSONWriter::BuildJSONString(const Value& node, size_t depth) {
return result;
}
- case Value::Type::BINARY:
+ case Value::TYPE_BINARY:
// Successful only if we're allowed to omit it.
DLOG_IF(ERROR, !omit_binary_values_) << "Cannot serialize binary value.";
return omit_binary_values_;
diff --git a/base/json/json_writer.h b/base/json/json_writer.h
index 57cb8c16a2..ef43341409 100644
--- a/base/json/json_writer.h
+++ b/base/json/json_writer.h
@@ -37,8 +37,6 @@ class BASE_EXPORT JSONWriter {
};
// Given a root node, generates a JSON string and puts it into |json|.
- // The output string is overwritten and not appended.
- //
// 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.
diff --git a/base/json/json_writer_unittest.cc b/base/json/json_writer_unittest.cc
index 6cb236fdc1..233ac5e867 100644
--- a/base/json/json_writer_unittest.cc
+++ b/base/json/json_writer_unittest.cc
@@ -27,27 +27,27 @@ TEST(JSONWriterTest, BasicTypes) {
EXPECT_EQ("[]", output_js);
// Test integer values.
- EXPECT_TRUE(JSONWriter::Write(Value(42), &output_js));
+ EXPECT_TRUE(JSONWriter::Write(FundamentalValue(42), &output_js));
EXPECT_EQ("42", output_js);
// Test boolean values.
- EXPECT_TRUE(JSONWriter::Write(Value(true), &output_js));
+ EXPECT_TRUE(JSONWriter::Write(FundamentalValue(true), &output_js));
EXPECT_EQ("true", output_js);
// Test Real values should always have a decimal or an 'e'.
- EXPECT_TRUE(JSONWriter::Write(Value(1.0), &output_js));
+ EXPECT_TRUE(JSONWriter::Write(FundamentalValue(1.0), &output_js));
EXPECT_EQ("1.0", output_js);
// Test Real values in the the range (-1, 1) must have leading zeros
- EXPECT_TRUE(JSONWriter::Write(Value(0.2), &output_js));
+ EXPECT_TRUE(JSONWriter::Write(FundamentalValue(0.2), &output_js));
EXPECT_EQ("0.2", output_js);
// Test Real values in the the range (-1, 1) must have leading zeros
- EXPECT_TRUE(JSONWriter::Write(Value(-0.8), &output_js));
+ EXPECT_TRUE(JSONWriter::Write(FundamentalValue(-0.8), &output_js));
EXPECT_EQ("-0.8", output_js);
// Test String values.
- EXPECT_TRUE(JSONWriter::Write(Value("foo"), &output_js));
+ EXPECT_TRUE(JSONWriter::Write(StringValue("foo"), &output_js));
EXPECT_EQ("\"foo\"", output_js);
}
@@ -61,7 +61,7 @@ TEST(JSONWriterTest, NestedTypes) {
std::unique_ptr<DictionaryValue> inner_dict(new DictionaryValue());
inner_dict->SetInteger("inner int", 10);
list->Append(std::move(inner_dict));
- list->Append(MakeUnique<ListValue>());
+ list->Append(WrapUnique(new ListValue()));
list->AppendBoolean(true);
root_dict.Set("list", std::move(list));
@@ -119,9 +119,9 @@ TEST(JSONWriterTest, BinaryValues) {
ListValue binary_list;
binary_list.Append(BinaryValue::CreateWithCopiedBuffer("asdf", 4));
- binary_list.Append(MakeUnique<Value>(5));
+ binary_list.Append(WrapUnique(new FundamentalValue(5)));
binary_list.Append(BinaryValue::CreateWithCopiedBuffer("asdf", 4));
- binary_list.Append(MakeUnique<Value>(2));
+ binary_list.Append(WrapUnique(new FundamentalValue(2)));
binary_list.Append(BinaryValue::CreateWithCopiedBuffer("asdf", 4));
EXPECT_FALSE(JSONWriter::Write(binary_list, &output_js));
EXPECT_TRUE(JSONWriter::WriteWithOptions(
@@ -144,7 +144,7 @@ TEST(JSONWriterTest, DoublesAsInts) {
std::string output_js;
// Test allowing a double with no fractional part to be written as an integer.
- Value double_value(1e10);
+ FundamentalValue double_value(1e10);
EXPECT_TRUE(JSONWriter::WriteWithOptions(
double_value, JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION,
&output_js));
diff --git a/base/lazy_instance.h b/base/lazy_instance.h
index 5481f905cc..ac970c55c1 100644
--- a/base/lazy_instance.h
+++ b/base/lazy_instance.h
@@ -24,11 +24,11 @@
// requires that Type be a complete type so we can determine the size.
//
// Example usage:
-// static LazyInstance<MyClass>::Leaky inst = LAZY_INSTANCE_INITIALIZER;
+// static LazyInstance<MyClass> my_instance = LAZY_INSTANCE_INITIALIZER;
// void SomeMethod() {
-// inst.Get().SomeMethod(); // MyClass::SomeMethod()
+// my_instance.Get().SomeMethod(); // MyClass::SomeMethod()
//
-// MyClass* ptr = inst.Pointer();
+// MyClass* ptr = my_instance.Pointer();
// ptr->DoDoDo(); // MyClass::DoDoDo
// }
@@ -57,15 +57,22 @@
namespace base {
template <typename Type>
-struct LazyInstanceTraitsBase {
+struct DefaultLazyInstanceTraits {
+ static const bool kRegisterOnExit = true;
+#ifndef NDEBUG
+ static const bool kAllowedToAccessOnNonjoinableThread = false;
+#endif
+
static Type* New(void* instance) {
- DCHECK_EQ(reinterpret_cast<uintptr_t>(instance) & (ALIGNOF(Type) - 1), 0u);
+ DCHECK_EQ(reinterpret_cast<uintptr_t>(instance) & (ALIGNOF(Type) - 1), 0u)
+ << ": Bad boy, the buffer passed to placement new is not aligned!\n"
+ "This may break some stuff like SSE-based optimizations assuming the "
+ "<Type> objects are word aligned.";
// Use placement new to initialize our instance in our preallocated space.
// The parenthesis is very important here to force POD type initialization.
return new (instance) Type();
}
-
- static void CallDestructor(Type* instance) {
+ static void Delete(Type* instance) {
// Explicitly call the destructor.
instance->~Type();
}
@@ -75,25 +82,6 @@ struct LazyInstanceTraitsBase {
// can implement the more complicated pieces out of line in the .cc file.
namespace internal {
-// This traits class causes destruction the contained Type at process exit via
-// AtExitManager. This is probably generally not what you want. Instead, prefer
-// Leaky below.
-template <typename Type>
-struct DestructorAtExitLazyInstanceTraits {
- static const bool kRegisterOnExit = true;
-#if DCHECK_IS_ON()
- static const bool kAllowedToAccessOnNonjoinableThread = false;
-#endif
-
- static Type* New(void* instance) {
- return LazyInstanceTraitsBase<Type>::New(instance);
- }
-
- static void Delete(Type* instance) {
- LazyInstanceTraitsBase<Type>::CallDestructor(instance);
- }
-};
-
// Use LazyInstance<T>::Leaky for a less-verbose call-site typedef; e.g.:
// base::LazyInstance<T>::Leaky my_leaky_lazy_instance;
// instead of:
@@ -105,22 +93,19 @@ struct DestructorAtExitLazyInstanceTraits {
template <typename Type>
struct LeakyLazyInstanceTraits {
static const bool kRegisterOnExit = false;
-#if DCHECK_IS_ON()
+#ifndef NDEBUG
static const bool kAllowedToAccessOnNonjoinableThread = true;
#endif
static Type* New(void* instance) {
ANNOTATE_SCOPED_MEMORY_LEAK;
- return LazyInstanceTraitsBase<Type>::New(instance);
+ return DefaultLazyInstanceTraits<Type>::New(instance);
}
static void Delete(Type*) {}
};
-template <typename Type>
-struct ErrorMustSelectLazyOrDestructorAtExitForLazyInstance {};
-
// Our AtomicWord doubles as a spinlock, where a value of
-// kLazyInstanceStateCreating means the spinlock is being held for creation.
+// kBeingCreatedMarker means the spinlock is being held for creation.
static const subtle::AtomicWord kLazyInstanceStateCreating = 1;
// Check if instance needs to be created. If so return true otherwise
@@ -137,10 +122,7 @@ BASE_EXPORT void CompleteLazyInstance(subtle::AtomicWord* state,
} // namespace internal
-template <
- typename Type,
- typename Traits =
- internal::ErrorMustSelectLazyOrDestructorAtExitForLazyInstance<Type>>
+template <typename Type, typename Traits = DefaultLazyInstanceTraits<Type> >
class LazyInstance {
public:
// Do not define a destructor, as doing so makes LazyInstance a
@@ -152,16 +134,14 @@ class LazyInstance {
// Convenience typedef to avoid having to repeat Type for leaky lazy
// instances.
- typedef LazyInstance<Type, internal::LeakyLazyInstanceTraits<Type>> Leaky;
- typedef LazyInstance<Type, internal::DestructorAtExitLazyInstanceTraits<Type>>
- DestructorAtExit;
+ typedef LazyInstance<Type, internal::LeakyLazyInstanceTraits<Type> > Leaky;
Type& Get() {
return *Pointer();
}
Type* Pointer() {
-#if DCHECK_IS_ON()
+#ifndef NDEBUG
// Avoid making TLS lookup on release builds.
if (!Traits::kAllowedToAccessOnNonjoinableThread)
ThreadRestrictions::AssertSingletonAllowed();
diff --git a/base/lazy_instance_unittest.cc b/base/lazy_instance_unittest.cc
index 0aa4659465..8947b1291f 100644
--- a/base/lazy_instance_unittest.cc
+++ b/base/lazy_instance_unittest.cc
@@ -45,8 +45,7 @@ int SlowConstructor::constructed = 0;
class SlowDelegate : public base::DelegateSimpleThread::Delegate {
public:
- explicit SlowDelegate(
- base::LazyInstance<SlowConstructor>::DestructorAtExit* lazy)
+ explicit SlowDelegate(base::LazyInstance<SlowConstructor>* lazy)
: lazy_(lazy) {}
void Run() override {
@@ -55,13 +54,13 @@ class SlowDelegate : public base::DelegateSimpleThread::Delegate {
}
private:
- base::LazyInstance<SlowConstructor>::DestructorAtExit* lazy_;
+ base::LazyInstance<SlowConstructor>* lazy_;
};
} // namespace
-static base::LazyInstance<ConstructAndDestructLogger>::DestructorAtExit
- lazy_logger = LAZY_INSTANCE_INITIALIZER;
+static base::LazyInstance<ConstructAndDestructLogger> lazy_logger =
+ LAZY_INSTANCE_INITIALIZER;
TEST(LazyInstanceTest, Basic) {
{
@@ -82,7 +81,7 @@ TEST(LazyInstanceTest, Basic) {
EXPECT_EQ(4, destructed_seq_.GetNext());
}
-static base::LazyInstance<SlowConstructor>::DestructorAtExit lazy_slow =
+static base::LazyInstance<SlowConstructor> lazy_slow =
LAZY_INSTANCE_INITIALIZER;
TEST(LazyInstanceTest, ConstructorThreadSafety) {
@@ -127,8 +126,7 @@ TEST(LazyInstanceTest, LeakyLazyInstance) {
bool deleted1 = false;
{
base::ShadowingAtExitManager shadow;
- static base::LazyInstance<DeleteLogger>::DestructorAtExit test =
- LAZY_INSTANCE_INITIALIZER;
+ static base::LazyInstance<DeleteLogger> test = LAZY_INSTANCE_INITIALIZER;
test.Get().SetDeletedPtr(&deleted1);
}
EXPECT_TRUE(deleted1);
@@ -166,12 +164,9 @@ TEST(LazyInstanceTest, Alignment) {
// Create some static instances with increasing sizes and alignment
// requirements. By ordering this way, the linker will need to do some work to
// ensure proper alignment of the static data.
- static LazyInstance<AlignedData<4>>::DestructorAtExit align4 =
- LAZY_INSTANCE_INITIALIZER;
- static LazyInstance<AlignedData<32>>::DestructorAtExit align32 =
- LAZY_INSTANCE_INITIALIZER;
- static LazyInstance<AlignedData<4096>>::DestructorAtExit align4096 =
- LAZY_INSTANCE_INITIALIZER;
+ static LazyInstance<AlignedData<4> > align4 = LAZY_INSTANCE_INITIALIZER;
+ static LazyInstance<AlignedData<32> > align32 = LAZY_INSTANCE_INITIALIZER;
+ static LazyInstance<AlignedData<4096> > align4096 = LAZY_INSTANCE_INITIALIZER;
EXPECT_ALIGNED(align4.Pointer(), 4);
EXPECT_ALIGNED(align32.Pointer(), 32);
diff --git a/base/location.h b/base/location.h
index dd78515ce2..21e270c5a9 100644
--- a/base/location.h
+++ b/base/location.h
@@ -97,7 +97,7 @@ struct BASE_EXPORT LocationSnapshot {
BASE_EXPORT const void* GetProgramCounter();
// Define a macro to record the current source location.
-#define FROM_HERE FROM_HERE_WITH_EXPLICIT_FUNCTION(__func__)
+#define FROM_HERE FROM_HERE_WITH_EXPLICIT_FUNCTION(__FUNCTION__)
#define FROM_HERE_WITH_EXPLICIT_FUNCTION(function_name) \
::tracked_objects::Location(function_name, \
diff --git a/base/logging.cc b/base/logging.cc
index a8736badd3..381e9eea0f 100644
--- a/base/logging.cc
+++ b/base/logging.cc
@@ -7,12 +7,12 @@
#include <limits.h>
#include <stdint.h>
-#include "base/debug/activity_tracker.h"
#include "base/macros.h"
#include "build/build_config.h"
#if defined(OS_WIN)
#include <io.h>
+#include <windows.h>
typedef HANDLE FileHandle;
typedef HANDLE MutexHandle;
// Windows warns on using write(). It prefers _write().
@@ -346,11 +346,6 @@ void CloseLogFileUnlocked() {
} // namespace
-// This is never instantiated, it's just used for EAT_STREAM_PARAMETERS to have
-// an object of the correct type on the LHS of the unused part of the ternary
-// operator.
-std::ostream* g_swallow_stream;
-
LoggingSettings::LoggingSettings()
: logging_dest(LOG_DEFAULT),
log_file(nullptr),
@@ -742,12 +737,6 @@ LogMessage::~LogMessage() {
}
if (severity_ == LOG_FATAL) {
- // Write the log message to the global activity tracker, if running.
- base::debug::GlobalActivityTracker* tracker =
- base::debug::GlobalActivityTracker::Get();
- if (tracker)
- tracker->RecordLogMessage(str_newline);
-
// Ensure the first characters of the string are on the stack so they
// are contained in minidumps for diagnostic purposes.
char str_stack[1024];
@@ -791,13 +780,18 @@ void LogMessage::Init(const char* file, int line) {
if (g_log_thread_id)
stream_ << base::PlatformThread::CurrentId() << ':';
if (g_log_timestamp) {
-#if defined(OS_POSIX)
- timeval tv;
- gettimeofday(&tv, nullptr);
- time_t t = tv.tv_sec;
+ time_t t = time(nullptr);
+#if defined(__ANDROID__) || defined(ANDROID)
struct tm local_time;
- memset(&local_time, 0, sizeof(local_time));
+ memset(&local_time, 0, sizeof(local_time));
+#else
+ struct tm local_time = {0};
+#endif
+#ifdef _MSC_VER
+ localtime_s(&local_time, &t);
+#else
localtime_r(&t, &local_time);
+#endif
struct tm* tm_time = &local_time;
stream_ << std::setfill('0')
<< std::setw(2) << 1 + tm_time->tm_mon
@@ -806,23 +800,7 @@ void LogMessage::Init(const char* file, int line) {
<< std::setw(2) << tm_time->tm_hour
<< std::setw(2) << tm_time->tm_min
<< std::setw(2) << tm_time->tm_sec
- << '.'
- << std::setw(6) << tv.tv_usec
<< ':';
-#elif defined(OS_WIN)
- SYSTEMTIME local_time;
- GetLocalTime(&local_time);
- stream_ << std::setfill('0')
- << std::setw(2) << local_time.wMonth
- << std::setw(2) << local_time.wDay
- << '/'
- << std::setw(2) << local_time.wHour
- << std::setw(2) << local_time.wMinute
- << std::setw(2) << local_time.wSecond
- << '.'
- << std::setw(3) << local_time.wMilliseconds
- << ':';
-#endif
}
if (g_log_tickcount)
stream_ << TickCount() << ':';
diff --git a/base/logging.h b/base/logging.h
index 7ca018e227..2bfc972601 100644
--- a/base/logging.h
+++ b/base/logging.h
@@ -15,7 +15,6 @@
#include <utility>
#include "base/base_export.h"
-#include "base/compiler_specific.h"
#include "base/debug/debugger.h"
#include "base/macros.h"
#include "base/template_util.h"
@@ -141,6 +140,34 @@
// There is the special severity of DFATAL, which logs FATAL in debug mode,
// ERROR in normal mode.
+// Note that "The behavior of a C++ program is undefined if it adds declarations
+// or definitions to namespace std or to a namespace within namespace std unless
+// otherwise specified." --C++11[namespace.std]
+//
+// We've checked that this particular definition has the intended behavior on
+// our implementations, but it's prone to breaking in the future, and please
+// don't imitate this in your own definitions without checking with some
+// standard library experts.
+namespace std {
+// These functions are provided as a convenience for logging, which is where we
+// use streams (it is against Google style to use streams in other places). It
+// is designed to allow you to emit non-ASCII Unicode strings to the log file,
+// which is normally ASCII. It is relatively slow, so try not to use it for
+// common cases. Non-ASCII characters will be converted to UTF-8 by these
+// operators.
+BASE_EXPORT std::ostream& operator<<(std::ostream& out, const wchar_t* wstr);
+inline std::ostream& operator<<(std::ostream& out, const std::wstring& wstr) {
+ return out << wstr.c_str();
+}
+
+template<typename T>
+typename std::enable_if<std::is_enum<T>::value, std::ostream&>::type operator<<(
+ std::ostream& out, T value) {
+ return out << static_cast<typename std::underlying_type<T>::type>(value);
+}
+
+} // namespace std
+
namespace logging {
// TODO(avi): do we want to do a unification of character types here?
@@ -310,16 +337,15 @@ const LogSeverity LOG_DFATAL = LOG_FATAL;
// by LOG() and LOG_IF, etc. Since these are used all over our code, it's
// better to have compact code for these operations.
#define COMPACT_GOOGLE_LOG_EX_INFO(ClassName, ...) \
- ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_INFO, ##__VA_ARGS__)
-#define COMPACT_GOOGLE_LOG_EX_WARNING(ClassName, ...) \
- ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_WARNING, \
- ##__VA_ARGS__)
+ logging::ClassName(__FILE__, __LINE__, logging::LOG_INFO , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_WARNING(ClassName, ...) \
+ logging::ClassName(__FILE__, __LINE__, logging::LOG_WARNING , ##__VA_ARGS__)
#define COMPACT_GOOGLE_LOG_EX_ERROR(ClassName, ...) \
- ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_ERROR, ##__VA_ARGS__)
+ logging::ClassName(__FILE__, __LINE__, logging::LOG_ERROR , ##__VA_ARGS__)
#define COMPACT_GOOGLE_LOG_EX_FATAL(ClassName, ...) \
- ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_FATAL, ##__VA_ARGS__)
+ logging::ClassName(__FILE__, __LINE__, logging::LOG_FATAL , ##__VA_ARGS__)
#define COMPACT_GOOGLE_LOG_EX_DFATAL(ClassName, ...) \
- ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_DFATAL, ##__VA_ARGS__)
+ logging::ClassName(__FILE__, __LINE__, logging::LOG_DFATAL , ##__VA_ARGS__)
#define COMPACT_GOOGLE_LOG_INFO \
COMPACT_GOOGLE_LOG_EX_INFO(LogMessage)
@@ -380,7 +406,7 @@ const LogSeverity LOG_0 = LOG_ERROR;
// The VLOG macros log with negative verbosities.
#define VLOG_STREAM(verbose_level) \
- ::logging::LogMessage(__FILE__, __LINE__, -verbose_level).stream()
+ logging::LogMessage(__FILE__, __LINE__, -verbose_level).stream()
#define VLOG(verbose_level) \
LAZY_STREAM(VLOG_STREAM(verbose_level), VLOG_IS_ON(verbose_level))
@@ -391,11 +417,11 @@ const LogSeverity LOG_0 = LOG_ERROR;
#if defined (OS_WIN)
#define VPLOG_STREAM(verbose_level) \
- ::logging::Win32ErrorLogMessage(__FILE__, __LINE__, -verbose_level, \
+ logging::Win32ErrorLogMessage(__FILE__, __LINE__, -verbose_level, \
::logging::GetLastSystemErrorCode()).stream()
#elif defined(OS_POSIX)
#define VPLOG_STREAM(verbose_level) \
- ::logging::ErrnoLogMessage(__FILE__, __LINE__, -verbose_level, \
+ logging::ErrnoLogMessage(__FILE__, __LINE__, -verbose_level, \
::logging::GetLastSystemErrorCode()).stream()
#endif
@@ -408,7 +434,7 @@ const LogSeverity LOG_0 = LOG_ERROR;
// TODO(akalin): Add more VLOG variants, e.g. VPLOG.
-#define LOG_ASSERT(condition) \
+#define LOG_ASSERT(condition) \
LOG_IF(FATAL, !(condition)) << "Assert failed: " #condition ". "
#if defined(OS_WIN)
@@ -427,23 +453,9 @@ const LogSeverity LOG_0 = LOG_ERROR;
#define PLOG_IF(severity, condition) \
LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity) && (condition))
-BASE_EXPORT extern std::ostream* g_swallow_stream;
-
-// Note that g_swallow_stream is used instead of an arbitrary LOG() stream to
-// avoid the creation of an object with a non-trivial destructor (LogMessage).
-// On MSVC x86 (checked on 2015 Update 3), this causes a few additional
-// pointless instructions to be emitted even at full optimization level, even
-// though the : arm of the ternary operator is clearly never executed. Using a
-// simpler object to be &'d with Voidify() avoids these extra instructions.
-// Using a simpler POD object with a templated operator<< also works to avoid
-// these instructions. However, this causes warnings on statically defined
-// implementations of operator<<(std::ostream, ...) in some .cc files, because
-// they become defined-but-unreferenced functions. A reinterpret_cast of 0 to an
-// ostream* also is not suitable, because some compilers warn of undefined
-// behavior.
-#define EAT_STREAM_PARAMETERS \
- true ? (void)0 \
- : ::logging::LogMessageVoidify() & (*::logging::g_swallow_stream)
+// The actual stream used isn't important.
+#define EAT_STREAM_PARAMETERS \
+ true ? (void) 0 : ::logging::LogMessageVoidify() & LOG_STREAM(FATAL)
// Captures the result of a CHECK_EQ (for example) and facilitates testing as a
// boolean.
@@ -460,84 +472,6 @@ class CheckOpResult {
std::string* message_;
};
-// Crashes in the fastest possible way with no attempt at logging.
-// There are different constraints to satisfy here, see http://crbug.com/664209
-// for more context:
-// - The trap instructions, and hence the PC value at crash time, have to be
-// distinct and not get folded into the same opcode by the compiler.
-// On Linux/Android this is tricky because GCC still folds identical
-// asm volatile blocks. The workaround is generating distinct opcodes for
-// each CHECK using the __COUNTER__ macro.
-// - The debug info for the trap instruction has to be attributed to the source
-// line that has the CHECK(), to make crash reports actionable. This rules
-// out the ability of using a inline function, at least as long as clang
-// doesn't support attribute(artificial).
-// - Failed CHECKs should produce a signal that is distinguishable from an
-// invalid memory access, to improve the actionability of crash reports.
-// - The compiler should treat the CHECK as no-return instructions, so that the
-// trap code can be efficiently packed in the prologue of the function and
-// doesn't interfere with the main execution flow.
-// - When debugging, developers shouldn't be able to accidentally step over a
-// CHECK. This is achieved by putting opcodes that will cause a non
-// continuable exception after the actual trap instruction.
-// - Don't cause too much binary bloat.
-#if defined(COMPILER_GCC)
-
-#if defined(ARCH_CPU_X86_FAMILY) && !defined(OS_NACL)
-// int 3 will generate a SIGTRAP.
-#define TRAP_SEQUENCE() \
- asm volatile( \
- "int3; ud2; push %0;" ::"i"(static_cast<unsigned char>(__COUNTER__)))
-
-#elif defined(ARCH_CPU_ARMEL) && !defined(OS_NACL)
-// bkpt will generate a SIGBUS when running on armv7 and a SIGTRAP when running
-// as a 32 bit userspace app on arm64. There doesn't seem to be any way to
-// cause a SIGTRAP from userspace without using a syscall (which would be a
-// problem for sandboxing).
-#define TRAP_SEQUENCE() \
- asm volatile("bkpt #0; udf %0;" ::"i"(__COUNTER__ % 256))
-
-#elif defined(ARCH_CPU_ARM64) && !defined(OS_NACL)
-// This will always generate a SIGTRAP on arm64.
-#define TRAP_SEQUENCE() \
- asm volatile("brk #0; hlt %0;" ::"i"(__COUNTER__ % 65536))
-
-#else
-// Crash report accuracy will not be guaranteed on other architectures, but at
-// least this will crash as expected.
-#define TRAP_SEQUENCE() __builtin_trap()
-#endif // ARCH_CPU_*
-
-#define IMMEDIATE_CRASH() \
- ({ \
- TRAP_SEQUENCE(); \
- __builtin_unreachable(); \
- })
-
-#elif defined(COMPILER_MSVC)
-
-// Clang is cleverer about coalescing int3s, so we need to add a unique-ish
-// instruction following the __debugbreak() to have it emit distinct locations
-// for CHECKs rather than collapsing them all together. It would be nice to use
-// a short intrinsic to do this (and perhaps have only one implementation for
-// both clang and MSVC), however clang-cl currently does not support intrinsics.
-// On the flip side, MSVC x64 doesn't support inline asm. So, we have to have
-// two implementations. Normally clang-cl's version will be 5 bytes (1 for
-// `int3`, 2 for `ud2`, 2 for `push byte imm`, however, TODO(scottmg):
-// https://crbug.com/694670 clang-cl doesn't currently support %'ing
-// __COUNTER__, so eventually it will emit the dword form of push.
-// TODO(scottmg): Reinvestigate a short sequence that will work on both
-// compilers once clang supports more intrinsics. See https://crbug.com/693713.
-#if defined(__clang__)
-#define IMMEDIATE_CRASH() ({__asm int 3 __asm ud2 __asm push __COUNTER__})
-#else
-#define IMMEDIATE_CRASH() __debugbreak()
-#endif // __clang__
-
-#else
-#error Port
-#endif
-
// CHECK dies with a fatal error if condition is not true. It is *not*
// controlled by NDEBUG, so the check will be executed regardless of
// compilation mode.
@@ -547,14 +481,20 @@ class CheckOpResult {
#if defined(OFFICIAL_BUILD) && defined(NDEBUG)
-// Make all CHECK functions discard their log strings to reduce code bloat, and
-// improve performance, for official release builds.
-//
+// Make all CHECK functions discard their log strings to reduce code
+// bloat, and improve performance, for official release builds.
+
+#if defined(COMPILER_GCC) || __clang__
+#define LOGGING_CRASH() __builtin_trap()
+#else
+#define LOGGING_CRASH() ((void)(*(volatile char*)0 = 0))
+#endif
+
// This is not calling BreakDebugger since this is called frequently, and
// calling an out-of-line function instead of a noreturn inline macro prevents
// compiler optimizations.
-#define CHECK(condition) \
- UNLIKELY(!(condition)) ? IMMEDIATE_CRASH() : EAT_STREAM_PARAMETERS
+#define CHECK(condition) \
+ !(condition) ? LOGGING_CRASH() : EAT_STREAM_PARAMETERS
#define PCHECK(condition) CHECK(condition)
@@ -570,26 +510,26 @@ class CheckOpResult {
// __analysis_assume gets confused on some conditions:
// http://randomascii.wordpress.com/2011/09/13/analyze-for-visual-studio-the-ugly-part-5/
-#define CHECK(condition) \
- __analysis_assume(!!(condition)), \
- LAZY_STREAM(LOG_STREAM(FATAL), false) \
- << "Check failed: " #condition ". "
+#define CHECK(condition) \
+ __analysis_assume(!!(condition)), \
+ LAZY_STREAM(LOG_STREAM(FATAL), false) \
+ << "Check failed: " #condition ". "
-#define PCHECK(condition) \
- __analysis_assume(!!(condition)), \
- LAZY_STREAM(PLOG_STREAM(FATAL), false) \
- << "Check failed: " #condition ". "
+#define PCHECK(condition) \
+ __analysis_assume(!!(condition)), \
+ LAZY_STREAM(PLOG_STREAM(FATAL), false) \
+ << "Check failed: " #condition ". "
#else // _PREFAST_
// Do as much work as possible out of line to reduce inline code size.
-#define CHECK(condition) \
- LAZY_STREAM(::logging::LogMessage(__FILE__, __LINE__, #condition).stream(), \
+#define CHECK(condition) \
+ LAZY_STREAM(logging::LogMessage(__FILE__, __LINE__, #condition).stream(), \
!(condition))
#define PCHECK(condition) \
LAZY_STREAM(PLOG_STREAM(FATAL), !(condition)) \
- << "Check failed: " #condition ". "
+ << "Check failed: " #condition ". "
#endif // _PREFAST_
@@ -601,12 +541,12 @@ class CheckOpResult {
// CHECK_EQ(2, a);
#define CHECK_OP(name, op, val1, val2) \
switch (0) case 0: default: \
- if (::logging::CheckOpResult true_if_passed = \
- ::logging::Check##name##Impl((val1), (val2), \
- #val1 " " #op " " #val2)) \
+ if (logging::CheckOpResult true_if_passed = \
+ logging::Check##name##Impl((val1), (val2), \
+ #val1 " " #op " " #val2)) \
; \
else \
- ::logging::LogMessage(__FILE__, __LINE__, true_if_passed.message()).stream()
+ logging::LogMessage(__FILE__, __LINE__, true_if_passed.message()).stream()
#endif // !(OFFICIAL_BUILD && NDEBUG)
@@ -614,26 +554,12 @@ class CheckOpResult {
// it uses the definition for operator<<, with a few special cases below.
template <typename T>
inline typename std::enable_if<
- base::internal::SupportsOstreamOperator<const T&>::value &&
- !std::is_function<typename std::remove_pointer<T>::type>::value,
+ base::internal::SupportsOstreamOperator<const T&>::value,
void>::type
MakeCheckOpValueString(std::ostream* os, const T& v) {
(*os) << v;
}
-// Provide an overload for functions and function pointers. Function pointers
-// don't implicitly convert to void* but do implicitly convert to bool, so
-// without this function pointers are always printed as 1 or 0. (MSVC isn't
-// standards-conforming here and converts function pointers to regular
-// pointers, so this is a no-op for MSVC.)
-template <typename T>
-inline typename std::enable_if<
- std::is_function<typename std::remove_pointer<T>::type>::value,
- void>::type
-MakeCheckOpValueString(std::ostream* os, const T& v) {
- (*os) << reinterpret_cast<const void*>(v);
-}
-
// We need overloads for enums that don't support operator<<.
// (i.e. scoped enums where no operator<< overload was declared).
template <typename T>
@@ -685,20 +611,16 @@ std::string* MakeCheckOpString<std::string, std::string>(
// The (int, int) specialization works around the issue that the compiler
// will not instantiate the template version of the function on values of
// unnamed enum type - see comment below.
-#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 (v1 op v2) \
- return NULL; \
- else \
- return ::logging::MakeCheckOpString(v1, v2, names); \
- } \
+#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 (v1 op v2) return NULL; \
+ else return MakeCheckOpString(v1, v2, names); \
+ } \
inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \
- if (v1 op v2) \
- return NULL; \
- else \
- return ::logging::MakeCheckOpString(v1, v2, names); \
+ if (v1 op v2) return NULL; \
+ else return MakeCheckOpString(v1, v2, names); \
}
DEFINE_CHECK_OP_IMPL(EQ, ==)
DEFINE_CHECK_OP_IMPL(NE, !=)
@@ -716,6 +638,12 @@ DEFINE_CHECK_OP_IMPL(GT, > )
#define CHECK_GT(val1, val2) CHECK_OP(GT, > , val1, val2)
#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+#define ENABLE_DLOG 0
+#else
+#define ENABLE_DLOG 1
+#endif
+
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
#define DCHECK_IS_ON() 0
#else
#define DCHECK_IS_ON() 1
@@ -723,7 +651,7 @@ DEFINE_CHECK_OP_IMPL(GT, > )
// Definitions for DLOG et al.
-#if DCHECK_IS_ON()
+#if ENABLE_DLOG
#define DLOG_IS_ON(severity) LOG_IS_ON(severity)
#define DLOG_IF(severity, condition) LOG_IF(severity, condition)
@@ -732,11 +660,12 @@ DEFINE_CHECK_OP_IMPL(GT, > )
#define DVLOG_IF(verboselevel, condition) VLOG_IF(verboselevel, condition)
#define DVPLOG_IF(verboselevel, condition) VPLOG_IF(verboselevel, condition)
-#else // DCHECK_IS_ON()
+#else // ENABLE_DLOG
-// If !DCHECK_IS_ON(), we want to avoid emitting any references to |condition|
-// (which may reference a variable defined only if DCHECK_IS_ON()).
-// Contrast this with DCHECK et al., which has different behavior.
+// If ENABLE_DLOG is off, we want to avoid emitting any references to
+// |condition| (which may reference a variable defined only if NDEBUG
+// is not defined). Contrast this with DCHECK et al., which has
+// different behavior.
#define DLOG_IS_ON(severity) false
#define DLOG_IF(severity, condition) EAT_STREAM_PARAMETERS
@@ -745,7 +674,19 @@ DEFINE_CHECK_OP_IMPL(GT, > )
#define DVLOG_IF(verboselevel, condition) EAT_STREAM_PARAMETERS
#define DVPLOG_IF(verboselevel, condition) EAT_STREAM_PARAMETERS
-#endif // DCHECK_IS_ON()
+#endif // ENABLE_DLOG
+
+// DEBUG_MODE is for uses like
+// if (DEBUG_MODE) foo.CheckThatFoo();
+// instead of
+// #ifndef NDEBUG
+// foo.CheckThatFoo();
+// #endif
+//
+// We tie its state to ENABLE_DLOG.
+enum { DEBUG_MODE = ENABLE_DLOG };
+
+#undef ENABLE_DLOG
#define DLOG(severity) \
LAZY_STREAM(LOG_STREAM(severity), DLOG_IS_ON(severity))
@@ -780,63 +721,31 @@ const LogSeverity LOG_DCHECK = LOG_INFO;
// whether DCHECKs are enabled; this is so that we don't get unused
// variable warnings if the only use of a variable is in a DCHECK.
// This behavior is different from DLOG_IF et al.
-//
-// Note that the definition of the DCHECK macros depends on whether or not
-// DCHECK_IS_ON() is true. When DCHECK_IS_ON() is false, the macros use
-// EAT_STREAM_PARAMETERS to avoid expressions that would create temporaries.
#if defined(_PREFAST_) && defined(OS_WIN)
// See comments on the previous use of __analysis_assume.
-#define DCHECK(condition) \
- __analysis_assume(!!(condition)), \
- LAZY_STREAM(LOG_STREAM(DCHECK), false) \
- << "Check failed: " #condition ". "
+#define DCHECK(condition) \
+ __analysis_assume(!!(condition)), \
+ LAZY_STREAM(LOG_STREAM(DCHECK), false) \
+ << "Check failed: " #condition ". "
-#define DPCHECK(condition) \
- __analysis_assume(!!(condition)), \
- LAZY_STREAM(PLOG_STREAM(DCHECK), false) \
- << "Check failed: " #condition ". "
-
-#elif defined(__clang_analyzer__)
-
-// Keeps the static analyzer from proceeding along the current codepath,
-// otherwise false positive errors may be generated by null pointer checks.
-inline constexpr bool AnalyzerNoReturn() __attribute__((analyzer_noreturn)) {
- return false;
-}
+#define DPCHECK(condition) \
+ __analysis_assume(!!(condition)), \
+ LAZY_STREAM(PLOG_STREAM(DCHECK), false) \
+ << "Check failed: " #condition ". "
-#define DCHECK(condition) \
- LAZY_STREAM( \
- LOG_STREAM(DCHECK), \
- DCHECK_IS_ON() ? (logging::AnalyzerNoReturn() || !(condition)) : false) \
- << "Check failed: " #condition ". "
+#else // _PREFAST_
-#define DPCHECK(condition) \
- LAZY_STREAM( \
- PLOG_STREAM(DCHECK), \
- DCHECK_IS_ON() ? (logging::AnalyzerNoReturn() || !(condition)) : false) \
+#define DCHECK(condition) \
+ LAZY_STREAM(LOG_STREAM(DCHECK), DCHECK_IS_ON() ? !(condition) : false) \
<< "Check failed: " #condition ". "
-#else
-
-#if DCHECK_IS_ON()
-
-#define DCHECK(condition) \
- LAZY_STREAM(LOG_STREAM(DCHECK), !(condition)) \
+#define DPCHECK(condition) \
+ LAZY_STREAM(PLOG_STREAM(DCHECK), DCHECK_IS_ON() ? !(condition) : false) \
<< "Check failed: " #condition ". "
-#define DPCHECK(condition) \
- LAZY_STREAM(PLOG_STREAM(DCHECK), !(condition)) \
- << "Check failed: " #condition ". "
-
-#else // DCHECK_IS_ON()
-
-#define DCHECK(condition) EAT_STREAM_PARAMETERS << !(condition)
-#define DPCHECK(condition) EAT_STREAM_PARAMETERS << !(condition)
-#endif // DCHECK_IS_ON()
-
-#endif
+#endif // _PREFAST_
// Helper macro for binary operators.
// Don't use this macro directly in your code, use DCHECK_EQ et al below.
@@ -844,37 +753,16 @@ inline constexpr bool AnalyzerNoReturn() __attribute__((analyzer_noreturn)) {
// macro is used in an 'if' clause such as:
// if (a == 1)
// DCHECK_EQ(2, a);
-#if DCHECK_IS_ON()
-
-#define DCHECK_OP(name, op, val1, val2) \
- switch (0) case 0: default: \
- if (::logging::CheckOpResult true_if_passed = \
- DCHECK_IS_ON() ? \
- ::logging::Check##name##Impl((val1), (val2), \
- #val1 " " #op " " #val2) : nullptr) \
- ; \
- else \
- ::logging::LogMessage(__FILE__, __LINE__, ::logging::LOG_DCHECK, \
- true_if_passed.message()).stream()
-
-#else // DCHECK_IS_ON()
-
-// When DCHECKs aren't enabled, DCHECK_OP still needs to reference operator<<
-// overloads for |val1| and |val2| to avoid potential compiler warnings about
-// unused functions. For the same reason, it also compares |val1| and |val2|
-// using |op|.
-//
-// Note that the contract of DCHECK_EQ, etc is that arguments are only evaluated
-// once. Even though |val1| and |val2| appear twice in this version of the macro
-// expansion, this is OK, since the expression is never actually evaluated.
-#define DCHECK_OP(name, op, val1, val2) \
- EAT_STREAM_PARAMETERS << (::logging::MakeCheckOpValueString( \
- ::logging::g_swallow_stream, val1), \
- ::logging::MakeCheckOpValueString( \
- ::logging::g_swallow_stream, val2), \
- (val1)op(val2))
-
-#endif // DCHECK_IS_ON()
+#define DCHECK_OP(name, op, val1, val2) \
+ switch (0) case 0: default: \
+ if (logging::CheckOpResult true_if_passed = \
+ DCHECK_IS_ON() ? \
+ logging::Check##name##Impl((val1), (val2), \
+ #val1 " " #op " " #val2) : nullptr) \
+ ; \
+ else \
+ logging::LogMessage(__FILE__, __LINE__, ::logging::LOG_DCHECK, \
+ true_if_passed.message()).stream()
// Equality/Inequality checks - compare two values, and log a
// LOG_DCHECK message including the two values when the result is not
@@ -882,7 +770,7 @@ inline constexpr bool AnalyzerNoReturn() __attribute__((analyzer_noreturn)) {
// defined.
//
// You may append to the error message like so:
-// DCHECK_NE(1, 2) << "The world must be ending!";
+// DCHECK_NE(1, 2) << ": The world must be ending!";
//
// We are very careful to ensure that each argument is evaluated exactly
// once, and that anything which is legal to pass as a function argument is
@@ -946,9 +834,6 @@ class BASE_EXPORT LogMessage {
std::ostream& stream() { return stream_; }
- LogSeverity severity() { return severity_; }
- std::string str() { return stream_.str(); }
-
private:
void Init(const char* file, int line);
@@ -1056,14 +941,12 @@ BASE_EXPORT void CloseLogFile();
// Async signal safe logging mechanism.
BASE_EXPORT void RawLog(int level, const char* message);
-#define RAW_LOG(level, message) \
- ::logging::RawLog(::logging::LOG_##level, message)
+#define RAW_LOG(level, message) logging::RawLog(logging::LOG_ ## level, message)
-#define RAW_CHECK(condition) \
- do { \
- if (!(condition)) \
- ::logging::RawLog(::logging::LOG_FATAL, \
- "Check failed: " #condition "\n"); \
+#define RAW_CHECK(condition) \
+ do { \
+ if (!(condition)) \
+ logging::RawLog(logging::LOG_FATAL, "Check failed: " #condition "\n"); \
} while (0)
#if defined(OS_WIN)
@@ -1076,27 +959,6 @@ BASE_EXPORT std::wstring GetLogFileFullPath();
} // namespace logging
-// Note that "The behavior of a C++ program is undefined if it adds declarations
-// or definitions to namespace std or to a namespace within namespace std unless
-// otherwise specified." --C++11[namespace.std]
-//
-// We've checked that this particular definition has the intended behavior on
-// our implementations, but it's prone to breaking in the future, and please
-// don't imitate this in your own definitions without checking with some
-// standard library experts.
-namespace std {
-// These functions are provided as a convenience for logging, which is where we
-// use streams (it is against Google style to use streams in other places). It
-// is designed to allow you to emit non-ASCII Unicode strings to the log file,
-// which is normally ASCII. It is relatively slow, so try not to use it for
-// common cases. Non-ASCII characters will be converted to UTF-8 by these
-// operators.
-BASE_EXPORT std::ostream& operator<<(std::ostream& out, const wchar_t* wstr);
-inline std::ostream& operator<<(std::ostream& out, const std::wstring& wstr) {
- return out << wstr.c_str();
-}
-} // namespace std
-
// The NOTIMPLEMENTED() macro annotates codepaths which have
// not been implemented yet.
//
diff --git a/base/logging_unittest.cc b/base/logging_unittest.cc
index 04f349cab6..8a20c54fb4 100644
--- a/base/logging_unittest.cc
+++ b/base/logging_unittest.cc
@@ -9,21 +9,6 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
-#if defined(OS_POSIX)
-#include <signal.h>
-#include <unistd.h>
-#include "base/posix/eintr_wrapper.h"
-#endif // OS_POSIX
-
-#if defined(OS_LINUX) || defined(OS_ANDROID)
-#include <ucontext.h>
-#endif
-
-#if defined(OS_WIN)
-#include <excpt.h>
-#include <windows.h>
-#endif // OS_WIN
-
namespace logging {
namespace {
@@ -69,14 +54,16 @@ class MockLogSource {
TEST_F(LoggingTest, BasicLogging) {
MockLogSource mock_log_source;
- EXPECT_CALL(mock_log_source, Log())
- .Times(DCHECK_IS_ON() ? 16 : 8)
- .WillRepeatedly(Return("log message"));
+ EXPECT_CALL(mock_log_source, Log()).Times(DEBUG_MODE ? 16 : 8).
+ WillRepeatedly(Return("log message"));
SetMinLogLevel(LOG_INFO);
EXPECT_TRUE(LOG_IS_ON(INFO));
- EXPECT_TRUE((DCHECK_IS_ON() != 0) == DLOG_IS_ON(INFO));
+ // As of g++-4.5, the first argument to EXPECT_EQ cannot be a
+ // constant expression.
+ const bool kIsDebugMode = (DEBUG_MODE != 0);
+ EXPECT_TRUE(kIsDebugMode == DLOG_IS_ON(INFO));
EXPECT_TRUE(VLOG_IS_ON(0));
LOG(INFO) << mock_log_source.Log();
@@ -203,154 +190,6 @@ TEST_F(LoggingTest, CheckStreamsAreLazy) {
#endif
-#if defined(OFFICIAL_BUILD) && defined(OS_WIN)
-NOINLINE void CheckContainingFunc(int death_location) {
- CHECK(death_location != 1);
- CHECK(death_location != 2);
- CHECK(death_location != 3);
-}
-
-int GetCheckExceptionData(EXCEPTION_POINTERS* p, DWORD* code, void** addr) {
- *code = p->ExceptionRecord->ExceptionCode;
- *addr = p->ExceptionRecord->ExceptionAddress;
- return EXCEPTION_EXECUTE_HANDLER;
-}
-
-TEST_F(LoggingTest, CheckCausesDistinctBreakpoints) {
- DWORD code1 = 0;
- DWORD code2 = 0;
- DWORD code3 = 0;
- void* addr1 = nullptr;
- void* addr2 = nullptr;
- void* addr3 = nullptr;
-
- // Record the exception code and addresses.
- __try {
- CheckContainingFunc(1);
- } __except (
- GetCheckExceptionData(GetExceptionInformation(), &code1, &addr1)) {
- }
-
- __try {
- CheckContainingFunc(2);
- } __except (
- GetCheckExceptionData(GetExceptionInformation(), &code2, &addr2)) {
- }
-
- __try {
- CheckContainingFunc(3);
- } __except (
- GetCheckExceptionData(GetExceptionInformation(), &code3, &addr3)) {
- }
-
- // Ensure that the exception codes are correct (in particular, breakpoints,
- // not access violations).
- EXPECT_EQ(STATUS_BREAKPOINT, code1);
- EXPECT_EQ(STATUS_BREAKPOINT, code2);
- EXPECT_EQ(STATUS_BREAKPOINT, code3);
-
- // Ensure that none of the CHECKs are colocated.
- EXPECT_NE(addr1, addr2);
- EXPECT_NE(addr1, addr3);
- EXPECT_NE(addr2, addr3);
-}
-
-#elif defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_IOS) && \
- (defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY))
-
-int g_child_crash_pipe;
-
-void CheckCrashTestSighandler(int, siginfo_t* info, void* context_ptr) {
- // Conversely to what clearly stated in "man 2 sigaction", some Linux kernels
- // do NOT populate the |info->si_addr| in the case of a SIGTRAP. Hence we
- // need the arch-specific boilerplate below, which is inspired by breakpad.
- // At the same time, on OSX, ucontext.h is deprecated but si_addr works fine.
- uintptr_t crash_addr = 0;
-#if defined(OS_MACOSX)
- crash_addr = reinterpret_cast<uintptr_t>(info->si_addr);
-#else // OS_POSIX && !OS_MACOSX
- struct ucontext* context = reinterpret_cast<struct ucontext*>(context_ptr);
-#if defined(ARCH_CPU_X86)
- crash_addr = static_cast<uintptr_t>(context->uc_mcontext.gregs[REG_EIP]);
-#elif defined(ARCH_CPU_X86_64)
- crash_addr = static_cast<uintptr_t>(context->uc_mcontext.gregs[REG_RIP]);
-#elif defined(ARCH_CPU_ARMEL)
- crash_addr = static_cast<uintptr_t>(context->uc_mcontext.arm_pc);
-#elif defined(ARCH_CPU_ARM64)
- crash_addr = static_cast<uintptr_t>(context->uc_mcontext.pc);
-#endif // ARCH_*
-#endif // OS_POSIX && !OS_MACOSX
- HANDLE_EINTR(write(g_child_crash_pipe, &crash_addr, sizeof(uintptr_t)));
- _exit(0);
-}
-
-// CHECK causes a direct crash (without jumping to another function) only in
-// official builds. Unfortunately, continuous test coverage on official builds
-// is lower. DO_CHECK here falls back on a home-brewed implementation in
-// non-official builds, to catch regressions earlier in the CQ.
-#if defined(OFFICIAL_BUILD)
-#define DO_CHECK CHECK
-#else
-#define DO_CHECK(cond) \
- if (!(cond)) \
- IMMEDIATE_CRASH()
-#endif
-
-void CrashChildMain(int death_location) {
- struct sigaction act = {};
- act.sa_sigaction = CheckCrashTestSighandler;
- act.sa_flags = SA_SIGINFO;
- ASSERT_EQ(0, sigaction(SIGTRAP, &act, NULL));
- ASSERT_EQ(0, sigaction(SIGBUS, &act, NULL));
- ASSERT_EQ(0, sigaction(SIGILL, &act, NULL));
- DO_CHECK(death_location != 1);
- DO_CHECK(death_location != 2);
- printf("\n");
- DO_CHECK(death_location != 3);
-
- // Should never reach this point.
- const uintptr_t failed = 0;
- HANDLE_EINTR(write(g_child_crash_pipe, &failed, sizeof(uintptr_t)));
-};
-
-void SpawnChildAndCrash(int death_location, uintptr_t* child_crash_addr) {
- int pipefd[2];
- ASSERT_EQ(0, pipe(pipefd));
-
- int pid = fork();
- ASSERT_GE(pid, 0);
-
- if (pid == 0) { // child process.
- close(pipefd[0]); // Close reader (parent) end.
- g_child_crash_pipe = pipefd[1];
- CrashChildMain(death_location);
- FAIL() << "The child process was supposed to crash. It didn't.";
- }
-
- close(pipefd[1]); // Close writer (child) end.
- DCHECK(child_crash_addr);
- int res = HANDLE_EINTR(read(pipefd[0], child_crash_addr, sizeof(uintptr_t)));
- ASSERT_EQ(static_cast<int>(sizeof(uintptr_t)), res);
-}
-
-TEST_F(LoggingTest, CheckCausesDistinctBreakpoints) {
- uintptr_t child_crash_addr_1 = 0;
- uintptr_t child_crash_addr_2 = 0;
- uintptr_t child_crash_addr_3 = 0;
-
- SpawnChildAndCrash(1, &child_crash_addr_1);
- SpawnChildAndCrash(2, &child_crash_addr_2);
- SpawnChildAndCrash(3, &child_crash_addr_3);
-
- ASSERT_NE(0u, child_crash_addr_1);
- ASSERT_NE(0u, child_crash_addr_2);
- ASSERT_NE(0u, child_crash_addr_3);
- ASSERT_NE(child_crash_addr_1, child_crash_addr_2);
- ASSERT_NE(child_crash_addr_1, child_crash_addr_3);
- ASSERT_NE(child_crash_addr_2, child_crash_addr_3);
-}
-#endif // OS_POSIX
-
TEST_F(LoggingTest, DebugLoggingReleaseBehavior) {
#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
int debug_only_variable = 1;
@@ -378,14 +217,6 @@ TEST_F(LoggingTest, DcheckStreamsAreLazy) {
#endif
}
-void DcheckEmptyFunction1() {
- // Provide a body so that Release builds do not cause the compiler to
- // optimize DcheckEmptyFunction1 and DcheckEmptyFunction2 as a single
- // function, which breaks the Dcheck tests below.
- LOG(INFO) << "DcheckEmptyFunction1";
-}
-void DcheckEmptyFunction2() {}
-
TEST_F(LoggingTest, Dcheck) {
#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
// Release build.
@@ -427,31 +258,6 @@ TEST_F(LoggingTest, Dcheck) {
EXPECT_EQ(0, log_sink_call_count);
DCHECK_EQ(Animal::DOG, Animal::CAT);
EXPECT_EQ(DCHECK_IS_ON() ? 1 : 0, log_sink_call_count);
-
- // Test DCHECK on functions and function pointers.
- log_sink_call_count = 0;
- struct MemberFunctions {
- void MemberFunction1() {
- // See the comment in DcheckEmptyFunction1().
- LOG(INFO) << "Do not merge with MemberFunction2.";
- }
- void MemberFunction2() {}
- };
- void (MemberFunctions::*mp1)() = &MemberFunctions::MemberFunction1;
- void (MemberFunctions::*mp2)() = &MemberFunctions::MemberFunction2;
- void (*fp1)() = DcheckEmptyFunction1;
- void (*fp2)() = DcheckEmptyFunction2;
- void (*fp3)() = DcheckEmptyFunction1;
- DCHECK_EQ(fp1, fp3);
- EXPECT_EQ(0, log_sink_call_count);
- DCHECK_EQ(mp1, &MemberFunctions::MemberFunction1);
- EXPECT_EQ(0, log_sink_call_count);
- DCHECK_EQ(mp2, &MemberFunctions::MemberFunction2);
- EXPECT_EQ(0, log_sink_call_count);
- DCHECK_EQ(fp1, fp2);
- EXPECT_EQ(DCHECK_IS_ON() ? 1 : 0, log_sink_call_count);
- DCHECK_EQ(mp2, &MemberFunctions::MemberFunction1);
- EXPECT_EQ(DCHECK_IS_ON() ? 2 : 0, log_sink_call_count);
}
TEST_F(LoggingTest, DcheckReleaseBehavior) {
diff --git a/base/mac/bind_objc_block.h b/base/mac/bind_objc_block.h
index 9a481ed987..2434d444f5 100644
--- a/base/mac/bind_objc_block.h
+++ b/base/mac/bind_objc_block.h
@@ -9,7 +9,6 @@
#include "base/bind.h"
#include "base/callback_forward.h"
-#include "base/compiler_specific.h"
#include "base/mac/scoped_block.h"
// BindBlock builds a callback from an Objective-C block. Example usages:
@@ -28,13 +27,6 @@
// seven total arguments, and the bound block itself is used as one of these
// arguments, so functionally the templates are limited to binding blocks with
// zero through six arguments.
-//
-// For code compiled with ARC (automatic reference counting), use BindBlockArc.
-// This is because the method has a different implementation (to avoid over-
-// retaining the block) and need to have a different name not to break the ODR
-// (one definition rule). Another subtle difference is that the implementation
-// will call a different version of ScopedBlock constructor thus the linker must
-// not merge both functions.
namespace base {
@@ -49,8 +41,6 @@ R RunBlock(base::mac::ScopedBlock<R(^)(Args...)> block, Args... args) {
} // namespace internal
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-
// Construct a callback from an objective-C block with up to six arguments (see
// note above).
template<typename R, typename... Args>
@@ -62,18 +52,6 @@ base::Callback<R(Args...)> BindBlock(R(^block)(Args...)) {
block)));
}
-#else
-
-// Construct a callback from an objective-C block with up to six arguments (see
-// note above).
-template <typename R, typename... Args>
-base::Callback<R(Args...)> BindBlockArc(R (^block)(Args...)) {
- return base::Bind(&base::internal::RunBlock<R, Args...>,
- base::mac::ScopedBlock<R (^)(Args...)>(block));
-}
-
-#endif
-
} // namespace base
#endif // BASE_MAC_BIND_OBJC_BLOCK_H_
diff --git a/base/mac/bundle_locations.h b/base/mac/bundle_locations.h
index 5cc44ba966..276290b0e6 100644
--- a/base/mac/bundle_locations.h
+++ b/base/mac/bundle_locations.h
@@ -12,6 +12,7 @@
#import <Foundation/Foundation.h>
#else // __OBJC__
class NSBundle;
+class NSString;
#endif // __OBJC__
namespace base {
diff --git a/base/mac/foundation_util.h b/base/mac/foundation_util.h
index 69b61280c3..ee23a17fb1 100644
--- a/base/mac/foundation_util.h
+++ b/base/mac/foundation_util.h
@@ -55,12 +55,6 @@ typedef unsigned int NSSearchPathDomainMask;
typedef struct OpaqueSecTrustRef* SecACLRef;
typedef struct OpaqueSecTrustedApplicationRef* SecTrustedApplicationRef;
-#if defined(OS_IOS)
-typedef struct CF_BRIDGED_TYPE(id) __SecPolicy* SecPolicyRef;
-#else
-typedef struct OpaqueSecPolicyRef* SecPolicyRef;
-#endif
-
namespace base {
class FilePath;
@@ -147,8 +141,6 @@ TYPE_NAME_FOR_CF_TYPE_DECL(CGColor);
TYPE_NAME_FOR_CF_TYPE_DECL(CTFont);
TYPE_NAME_FOR_CF_TYPE_DECL(CTRun);
-TYPE_NAME_FOR_CF_TYPE_DECL(SecPolicy);
-
#undef TYPE_NAME_FOR_CF_TYPE_DECL
// Retain/release calls for memory management in C++.
@@ -309,7 +301,6 @@ CF_CAST_DECL(CTFontDescriptor);
CF_CAST_DECL(CTRun);
CF_CAST_DECL(SecACL);
-CF_CAST_DECL(SecPolicy);
CF_CAST_DECL(SecTrustedApplication);
#undef CF_CAST_DECL
diff --git a/base/mac/foundation_util.mm b/base/mac/foundation_util.mm
index eb8284ee58..4f6fa60afd 100644
--- a/base/mac/foundation_util.mm
+++ b/base/mac/foundation_util.mm
@@ -213,10 +213,6 @@ TYPE_NAME_FOR_CF_TYPE_DEFN(CGColor);
TYPE_NAME_FOR_CF_TYPE_DEFN(CTFont);
TYPE_NAME_FOR_CF_TYPE_DEFN(CTRun);
-#if !defined(OS_IOS)
-TYPE_NAME_FOR_CF_TYPE_DEFN(SecPolicy);
-#endif
-
#undef TYPE_NAME_FOR_CF_TYPE_DEFN
void NSObjectRetain(void* obj) {
@@ -412,7 +408,6 @@ CFCastStrict<CTFontRef>(const CFTypeRef& cf_val) {
#if !defined(OS_IOS)
CF_CAST_DEFN(SecACL);
-CF_CAST_DEFN(SecPolicy);
CF_CAST_DEFN(SecTrustedApplication);
#endif
diff --git a/base/mac/mac_util.h b/base/mac/mac_util.h
index 67d1880849..84948f7ce8 100644
--- a/base/mac/mac_util.h
+++ b/base/mac/mac_util.h
@@ -5,11 +5,11 @@
#ifndef BASE_MAC_MAC_UTIL_H_
#define BASE_MAC_MAC_UTIL_H_
+#include <AvailabilityMacros.h>
+#include <Carbon/Carbon.h>
#include <stdint.h>
#include <string>
-#import <CoreGraphics/CoreGraphics.h>
-
#include "base/base_export.h"
namespace base {
@@ -31,6 +31,9 @@ enum FullScreenMode {
kFullScreenModeNormal = 10,
};
+BASE_EXPORT std::string PathFromFSRef(const FSRef& ref);
+BASE_EXPORT bool FSRefFromPath(const std::string& path, FSRef* ref);
+
// Returns an sRGB color space. The return value is a static value; do not
// release it!
BASE_EXPORT CGColorSpaceRef GetSRGBColorSpace();
@@ -63,6 +66,11 @@ BASE_EXPORT void ReleaseFullScreen(FullScreenMode mode);
BASE_EXPORT void SwitchFullScreenModes(FullScreenMode from_mode,
FullScreenMode to_mode);
+// Returns true if this process is in the foreground, meaning that it's the
+// frontmost process, the one whose menu bar is shown at the top of the main
+// display.
+BASE_EXPORT bool AmIForeground();
+
// Excludes the file given by |file_path| from being backed up by Time Machine.
BASE_EXPORT bool SetFileBackupExclusion(const FilePath& file_path);
@@ -100,63 +108,85 @@ BASE_EXPORT bool WasLaunchedAsHiddenLoginItem();
// an error, or true otherwise.
BASE_EXPORT bool RemoveQuarantineAttribute(const FilePath& file_path);
-namespace internal {
-
-// Returns the system's Mac OS X minor version. This is the |y| value
-// in 10.y or 10.y.z.
-BASE_EXPORT int MacOSXMinorVersion();
-
-} // namespace internal
-
// Run-time OS version checks. Use these instead of
-// base::SysInfo::OperatingSystemVersionNumbers. Prefer the "AtLeast" and
-// "AtMost" variants to those that check for a specific version, unless you
+// base::SysInfo::OperatingSystemVersionNumbers. Prefer the "OrEarlier" and
+// "OrLater" variants to those that check for a specific version, unless you
// know for sure that you need to check for a specific version.
-#define DEFINE_IS_OS_FUNCS(V, TEST_DEPLOYMENT_TARGET) \
- inline bool IsOS10_##V() { \
- TEST_DEPLOYMENT_TARGET(>, V, false) \
- return internal::MacOSXMinorVersion() == V; \
- } \
- inline bool IsAtLeastOS10_##V() { \
- TEST_DEPLOYMENT_TARGET(>=, V, true) \
- return internal::MacOSXMinorVersion() >= V; \
- } \
- inline bool IsAtMostOS10_##V() { \
- TEST_DEPLOYMENT_TARGET(>, V, false) \
- return internal::MacOSXMinorVersion() <= V; \
- }
-
-#define TEST_DEPLOYMENT_TARGET(OP, V, RET) \
- if (MAC_OS_X_VERSION_MIN_REQUIRED OP MAC_OS_X_VERSION_10_##V) \
- return RET;
-#define IGNORE_DEPLOYMENT_TARGET(OP, V, RET)
-
-DEFINE_IS_OS_FUNCS(9, TEST_DEPLOYMENT_TARGET)
-DEFINE_IS_OS_FUNCS(10, TEST_DEPLOYMENT_TARGET)
-
-#ifdef MAC_OS_X_VERSION_10_11
-DEFINE_IS_OS_FUNCS(11, TEST_DEPLOYMENT_TARGET)
-#else
-DEFINE_IS_OS_FUNCS(11, IGNORE_DEPLOYMENT_TARGET)
-#endif
+// Mavericks is OS X 10.9, Darwin 13.
+BASE_EXPORT bool IsOSMavericks();
-#ifdef MAC_OS_X_VERSION_10_12
-DEFINE_IS_OS_FUNCS(12, TEST_DEPLOYMENT_TARGET)
-#else
-DEFINE_IS_OS_FUNCS(12, IGNORE_DEPLOYMENT_TARGET)
-#endif
+// Yosemite is OS X 10.10, Darwin 14.
+BASE_EXPORT bool IsOSYosemite();
+BASE_EXPORT bool IsOSYosemiteOrEarlier();
+BASE_EXPORT bool IsOSYosemiteOrLater();
+
+// El Capitan is OS X 10.11, Darwin 15.
+BASE_EXPORT bool IsOSElCapitan();
+BASE_EXPORT bool IsOSElCapitanOrEarlier();
+BASE_EXPORT bool IsOSElCapitanOrLater();
-#undef IGNORE_DEPLOYMENT_TARGET
-#undef TEST_DEPLOYMENT_TARGET
-#undef DEFINE_IS_OS_FUNCS
+// Sierra is macOS 10.12, Darwin 16.
+BASE_EXPORT bool IsOSSierra();
+BASE_EXPORT bool IsOSSierraOrLater();
// This should be infrequently used. It only makes sense to use this to avoid
// codepaths that are very likely to break on future (unreleased, untested,
// unborn) OS releases, or to log when the OS is newer than any known version.
-inline bool IsOSLaterThan10_12_DontCallThis() {
- return !IsAtMostOS10_12();
-}
+BASE_EXPORT bool IsOSLaterThanSierra_DontCallThis();
+
+// Inline functions that are redundant due to version ranges being mutually-
+// exclusive.
+inline bool IsOSYosemiteOrEarlier() { return !IsOSElCapitanOrLater(); }
+inline bool IsOSElCapitanOrEarlier() { return !IsOSSierraOrLater(); }
+
+// When the deployment target is set, the code produced cannot run on earlier
+// OS releases. That enables some of the IsOS* family to be implemented as
+// constant-value inline functions. The MAC_OS_X_VERSION_MIN_REQUIRED macro
+// contains the value of the deployment target.
+
+#if defined(MAC_OS_X_VERSION_10_9) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_9
+#define BASE_MAC_MAC_UTIL_H_INLINED_GT_10_9
+inline bool IsOSMavericks() { return false; }
+#endif
+
+#if defined(MAC_OS_X_VERSION_10_10) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10
+#define BASE_MAC_MAC_UTIL_H_INLINED_GE_10_10
+inline bool IsOSYosemiteOrLater() { return true; }
+#endif
+
+#if defined(MAC_OS_X_VERSION_10_10) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_10
+#define BASE_MAC_MAC_UTIL_H_INLINED_GT_10_10
+inline bool IsOSYosemite() { return false; }
+#endif
+
+#if defined(MAC_OS_X_VERSION_10_11) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11
+#define BASE_MAC_MAC_UTIL_H_INLINED_GE_10_11
+inline bool IsOSElCapitanOrLater() { return true; }
+#endif
+
+#if defined(MAC_OS_X_VERSION_10_11) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_11
+#define BASE_MAC_MAC_UTIL_H_INLINED_GT_10_11
+inline bool IsOSElCapitan() { return false; }
+#endif
+
+#if defined(MAC_OS_X_VERSION_10_12) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12
+#define BASE_MAC_MAC_UTIL_H_INLINED_GE_10_12
+inline bool IsOSSierraOrLater() { return true; }
+#endif
+
+#if defined(MAC_OS_X_VERSION_10_12) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_12
+#define BASE_MAC_MAC_UTIL_H_INLINED_GT_10_12
+inline bool IsOSSierra() { return false; }
+inline bool IsOSLaterThanSierra_DontCallThis() { return true; }
+#endif
// Retrieve the system's model identifier string from the IOKit registry:
// for example, "MacPro4,1", "MacBookPro6,1". Returns empty string upon
diff --git a/base/mac/mach_port_broker.mm b/base/mac/mach_port_broker.mm
index 6d9fec5ab6..bd47017f15 100644
--- a/base/mac/mach_port_broker.mm
+++ b/base/mac/mach_port_broker.mm
@@ -154,7 +154,12 @@ void MachPortBroker::HandleRequest() {
// Use the kernel audit information to make sure this message is from
// a task that this process spawned. The kernel audit token contains the
// unspoofable pid of the task that sent the message.
- pid_t child_pid = audit_token_to_pid(msg.trailer.msgh_audit);
+ //
+ // TODO(rsesek): In the 10.7 SDK, there's audit_token_to_pid().
+ pid_t child_pid;
+ audit_token_to_au32(msg.trailer.msgh_audit,
+ NULL, NULL, NULL, NULL, NULL, &child_pid, NULL, NULL);
+
mach_port_t child_task_port = msg.child_task_port.name;
// Take the lock and update the broker information.
diff --git a/base/mac/scoped_authorizationref.h b/base/mac/scoped_authorizationref.h
index b83f8dfb35..03cde86140 100644
--- a/base/mac/scoped_authorizationref.h
+++ b/base/mac/scoped_authorizationref.h
@@ -11,7 +11,7 @@
#include "base/macros.h"
// ScopedAuthorizationRef maintains ownership of an AuthorizationRef. It is
-// patterned after the unique_ptr interface.
+// patterned after the scoped_ptr interface.
namespace base {
namespace mac {
diff --git a/base/mac/scoped_block.h b/base/mac/scoped_block.h
index 10ab4b4e88..8199677f15 100644
--- a/base/mac/scoped_block.h
+++ b/base/mac/scoped_block.h
@@ -36,33 +36,9 @@ struct ScopedBlockTraits {
// ScopedBlock<> is patterned after ScopedCFTypeRef<>, but uses Block_copy() and
// Block_release() instead of CFRetain() and CFRelease().
-template <typename B>
-class ScopedBlock : public ScopedTypeRef<B, internal::ScopedBlockTraits<B>> {
- public:
- using Traits = internal::ScopedBlockTraits<B>;
-#if !defined(__has_feature) || !__has_feature(objc_arc)
- explicit ScopedBlock(
- B block = Traits::InvalidValue(),
- base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME)
- : ScopedTypeRef<B, Traits>(block, policy) {}
-#else
- explicit ScopedBlock(B block = Traits::InvalidValue())
- : ScopedTypeRef<B, Traits>(block, base::scoped_policy::RETAIN) {}
-#endif
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
- void reset(B block = Traits::InvalidValue(),
- base::scoped_policy::OwnershipPolicy policy =
- base::scoped_policy::ASSUME) {
- ScopedTypeRef<B, Traits>::reset(block, policy);
- }
-#else
- void reset(B block = Traits::InvalidValue()) {
- ScopedTypeRef<B, Traits>::reset(block, base::scoped_policy::RETAIN);
- }
-#endif
-};
+template <typename B>
+using ScopedBlock = ScopedTypeRef<B, internal::ScopedBlockTraits<B>>;
} // namespace mac
} // namespace base
diff --git a/base/mac/scoped_nsobject.h b/base/mac/scoped_nsobject.h
index ecd8e78f9d..cc54aa0ca8 100644
--- a/base/mac/scoped_nsobject.h
+++ b/base/mac/scoped_nsobject.h
@@ -102,7 +102,7 @@ class scoped_nsprotocol
: ScopedTypeRef<NST, Traits>(that_as_subclass) {}
scoped_nsprotocol(scoped_nsprotocol<NST>&& that)
- : ScopedTypeRef<NST, Traits>(std::move(that)) {}
+ : ScopedTypeRef<NST, Traits>(that) {}
scoped_nsprotocol& operator=(const scoped_nsprotocol<NST>& that) {
ScopedTypeRef<NST, Traits>::operator=(that);
@@ -166,7 +166,7 @@ class scoped_nsobject : public scoped_nsprotocol<NST*> {
: scoped_nsprotocol<NST*>(that_as_subclass) {}
scoped_nsobject(scoped_nsobject<NST>&& that)
- : scoped_nsprotocol<NST*>(std::move(that)) {}
+ : scoped_nsprotocol<NST*>(that) {}
scoped_nsobject& operator=(const scoped_nsobject<NST>& that) {
scoped_nsprotocol<NST*>::operator=(that);
@@ -214,8 +214,7 @@ class scoped_nsobject<id> : public scoped_nsprotocol<id> {
explicit scoped_nsobject(const scoped_nsobject<NSR>& that_as_subclass)
: scoped_nsprotocol<id>(that_as_subclass) {}
- scoped_nsobject(scoped_nsobject<id>&& that)
- : scoped_nsprotocol<id>(std::move(that)) {}
+ scoped_nsobject(scoped_nsobject<id>&& that) : scoped_nsprotocol<id>(that) {}
scoped_nsobject& operator=(const scoped_nsobject<id>& that) {
scoped_nsprotocol<id>::operator=(that);
diff --git a/base/mac/sdk_forward_declarations.h b/base/mac/sdk_forward_declarations.h
index 306cd93678..e9a11f700c 100644
--- a/base/mac/sdk_forward_declarations.h
+++ b/base/mac/sdk_forward_declarations.h
@@ -3,7 +3,7 @@
// found in the LICENSE file.
// This file contains forward declarations for items in later SDKs than the
-// default one with which Chromium is built (currently 10.10).
+// default one with which Chromium is built (currently 10.6).
// If you call any function from this header, be sure to check at runtime for
// respondsToSelector: before calling these functions (else your code will crash
// on older OS X versions that chrome still supports).
@@ -14,18 +14,206 @@
#import <AppKit/AppKit.h>
#import <CoreBluetooth/CoreBluetooth.h>
#import <CoreWLAN/CoreWLAN.h>
-#import <IOBluetooth/IOBluetooth.h>
#import <ImageCaptureCore/ImageCaptureCore.h>
-#import <QuartzCore/QuartzCore.h>
+#import <IOBluetooth/IOBluetooth.h>
#include <stdint.h>
#include "base/base_export.h"
// ----------------------------------------------------------------------------
+// Either define or forward declare classes only available in OSX 10.7+.
+// ----------------------------------------------------------------------------
+
+#if !defined(MAC_OS_X_VERSION_10_7) || \
+ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
+
+@interface CWChannel : NSObject
+@end
+
+@interface CBPeripheral : NSObject
+@end
+
+@interface CBCentralManager : NSObject
+@end
+
+@interface CBUUID : NSObject
+@end
+
+#else
+
+@class CWChannel;
+@class CBPeripheral;
+@class CBCentralManager;
+@class CBUUID;
+
+#endif // MAC_OS_X_VERSION_10_7
+
+#if !defined(MAC_OS_X_VERSION_10_8) || \
+ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_8
+
+@interface NSUUID : NSObject
+@end
+
+#else
+
+@class NSUUID;
+
+#endif // MAC_OS_X_VERSION_10_8
+
+#if !defined(MAC_OS_X_VERSION_10_9) || \
+ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9
+
+// NSProgress is public API in 10.9, but a version of it exists and is usable
+// in 10.8.
+@interface NSProgress : NSObject
+@end
+
+@interface NSAppearance : NSObject
+@end
+
+#else
+
+@class NSProgress;
+@class NSAppearance;
+
+#endif // MAC_OS_X_VERSION_10_9
+
+// ----------------------------------------------------------------------------
// Define typedefs, enums, and protocols not available in the version of the
// OSX SDK being compiled against.
// ----------------------------------------------------------------------------
+#if !defined(MAC_OS_X_VERSION_10_7) || \
+ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
+
+enum {
+ NSEventPhaseNone = 0, // event not associated with a phase.
+ NSEventPhaseBegan = 0x1 << 0,
+ NSEventPhaseStationary = 0x1 << 1,
+ NSEventPhaseChanged = 0x1 << 2,
+ NSEventPhaseEnded = 0x1 << 3,
+ NSEventPhaseCancelled = 0x1 << 4
+};
+typedef NSUInteger NSEventPhase;
+
+enum {
+ NSFullScreenWindowMask = 1 << 14,
+};
+
+enum {
+ NSApplicationPresentationFullScreen = 1 << 10,
+};
+
+enum {
+ NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7,
+ NSWindowCollectionBehaviorFullScreenAuxiliary = 1 << 8,
+};
+
+enum {
+ NSEventSwipeTrackingLockDirection = 0x1 << 0,
+ NSEventSwipeTrackingClampGestureAmount = 0x1 << 1,
+};
+typedef NSUInteger NSEventSwipeTrackingOptions;
+
+enum {
+ NSWindowAnimationBehaviorDefault = 0,
+ NSWindowAnimationBehaviorNone = 2,
+ NSWindowAnimationBehaviorDocumentWindow = 3,
+ NSWindowAnimationBehaviorUtilityWindow = 4,
+ NSWindowAnimationBehaviorAlertPanel = 5
+};
+typedef NSInteger NSWindowAnimationBehavior;
+
+enum {
+ NSWindowDocumentVersionsButton = 6,
+ NSWindowFullScreenButton,
+};
+typedef NSUInteger NSWindowButton;
+
+enum CWChannelBand {
+ kCWChannelBandUnknown = 0,
+ kCWChannelBand2GHz = 1,
+ kCWChannelBand5GHz = 2,
+};
+
+enum {
+ kCWSecurityNone = 0,
+ kCWSecurityWEP = 1,
+ kCWSecurityWPAPersonal = 2,
+ kCWSecurityWPAPersonalMixed = 3,
+ kCWSecurityWPA2Personal = 4,
+ kCWSecurityPersonal = 5,
+ kCWSecurityDynamicWEP = 6,
+ kCWSecurityWPAEnterprise = 7,
+ kCWSecurityWPAEnterpriseMixed = 8,
+ kCWSecurityWPA2Enterprise = 9,
+ kCWSecurityEnterprise = 10,
+ kCWSecurityUnknown = NSIntegerMax,
+};
+
+typedef NSInteger CWSecurity;
+
+enum {
+ kBluetoothFeatureLESupportedController = (1 << 6L),
+};
+
+@protocol IOBluetoothDeviceInquiryDelegate
+- (void)deviceInquiryStarted:(IOBluetoothDeviceInquiry*)sender;
+- (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry*)sender
+ device:(IOBluetoothDevice*)device;
+- (void)deviceInquiryComplete:(IOBluetoothDeviceInquiry*)sender
+ error:(IOReturn)error
+ aborted:(BOOL)aborted;
+@end
+
+enum {
+ CBPeripheralStateDisconnected = 0,
+ CBPeripheralStateConnecting,
+ CBPeripheralStateConnected,
+};
+typedef NSInteger CBPeripheralState;
+
+enum {
+ CBCentralManagerStateUnknown = 0,
+ CBCentralManagerStateResetting,
+ CBCentralManagerStateUnsupported,
+ CBCentralManagerStateUnauthorized,
+ CBCentralManagerStatePoweredOff,
+ CBCentralManagerStatePoweredOn,
+};
+typedef NSInteger CBCentralManagerState;
+
+@protocol CBCentralManagerDelegate;
+
+@protocol CBCentralManagerDelegate<NSObject>
+- (void)centralManagerDidUpdateState:(CBCentralManager*)central;
+- (void)centralManager:(CBCentralManager*)central
+ didDiscoverPeripheral:(CBPeripheral*)peripheral
+ advertisementData:(NSDictionary*)advertisementData
+ RSSI:(NSNumber*)RSSI;
+@end
+
+#endif // MAC_OS_X_VERSION_10_7
+
+#if !defined(MAC_OS_X_VERSION_10_8) || \
+ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_8
+
+enum { NSEventPhaseMayBegin = 0x1 << 5 };
+
+#endif // MAC_OS_X_VERSION_10_8
+
+#if !defined(MAC_OS_X_VERSION_10_9) || \
+ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9
+
+enum {
+ NSWindowOcclusionStateVisible = 1UL << 1,
+};
+typedef NSUInteger NSWindowOcclusionState;
+
+enum { NSWorkspaceLaunchWithErrorPresentation = 0x00000040 };
+
+#endif // MAC_OS_X_VERSION_10_9
+
#if !defined(MAC_OS_X_VERSION_10_11) || \
MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_11
@@ -44,29 +232,7 @@ typedef NSInteger NSPressureBehavior;
- (instancetype)initWithPressureBehavior:(NSPressureBehavior)pressureBehavior;
@end
-enum {
- NSSpringLoadingHighlightNone = 0,
- NSSpringLoadingHighlightStandard,
- NSSpringLoadingHighlightEmphasized
-};
-typedef NSUInteger NSSpringLoadingHighlight;
-
-#endif // MAC_OS_X_VERSION_10_11
-
-#if !defined(MAC_OS_X_VERSION_10_12) || \
- MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
-
-// The protocol was formalized by the 10.12 SDK, but it was informally used
-// before.
-@protocol CAAnimationDelegate
-- (void)animationDidStart:(CAAnimation*)animation;
-- (void)animationDidStop:(CAAnimation*)animation finished:(BOOL)finished;
-@end
-
-@protocol CALayerDelegate
-@end
-
-#endif // MAC_OS_X_VERSION_10_12
+#endif // MAC_OS_X_VERSION_10_11
// ----------------------------------------------------------------------------
// Define NSStrings only available in newer versions of the OSX SDK to force
@@ -74,9 +240,27 @@ typedef NSUInteger NSSpringLoadingHighlight;
// ----------------------------------------------------------------------------
extern "C" {
+#if !defined(MAC_OS_X_VERSION_10_7) || \
+ MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7
+BASE_EXPORT extern NSString* const NSWindowWillEnterFullScreenNotification;
+BASE_EXPORT extern NSString* const NSWindowWillExitFullScreenNotification;
+BASE_EXPORT extern NSString* const NSWindowDidEnterFullScreenNotification;
+BASE_EXPORT extern NSString* const NSWindowDidExitFullScreenNotification;
+BASE_EXPORT extern NSString* const
+ NSWindowDidChangeBackingPropertiesNotification;
+BASE_EXPORT extern NSString* const CBAdvertisementDataServiceDataKey;
+BASE_EXPORT extern NSString* const CBAdvertisementDataServiceUUIDsKey;
+#endif // MAC_OS_X_VERSION_10_7
+
+#if !defined(MAC_OS_X_VERSION_10_9) || \
+ MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9
+BASE_EXPORT extern NSString* const NSWindowDidChangeOcclusionStateNotification;
+BASE_EXPORT extern NSString* const CBAdvertisementDataOverflowServiceUUIDsKey;
+BASE_EXPORT extern NSString* const CBAdvertisementDataIsConnectable;
+#endif // MAC_OS_X_VERSION_10_9
+
#if !defined(MAC_OS_X_VERSION_10_10) || \
MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
-BASE_EXPORT extern NSString* const CIDetectorTypeQRCode;
BASE_EXPORT extern NSString* const NSUserActivityTypeBrowsingWeb;
BASE_EXPORT extern NSString* const NSAppearanceNameVibrantDark;
BASE_EXPORT extern NSString* const NSAppearanceNameVibrantLight;
@@ -84,158 +268,250 @@ BASE_EXPORT extern NSString* const NSAppearanceNameVibrantLight;
} // extern "C"
// ----------------------------------------------------------------------------
-// If compiling against an older version of the OSX SDK, declare classes and
-// functions that are available in newer versions of the OSX SDK. If compiling
-// against a newer version of the OSX SDK, redeclare those same classes and
-// functions to suppress -Wpartial-availability warnings.
+// If compiling against an older version of the OSX SDK, declare functions that
+// are available in newer versions of the OSX SDK. If compiling against a newer
+// version of the OSX SDK, redeclare those same functions to suppress
+// -Wpartial-availability warnings.
// ----------------------------------------------------------------------------
-// Once Chrome no longer supports OSX 10.9, everything within this preprocessor
+// Once Chrome no longer supports OSX 10.6, everything within this preprocessor
// block can be removed.
-#if !defined(MAC_OS_X_VERSION_10_10) || \
- MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
+#if !defined(MAC_OS_X_VERSION_10_7) || \
+ MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7
+
+@interface NSEvent (LionSDK)
++ (BOOL)isSwipeTrackingFromScrollEventsEnabled;
+- (NSEventPhase)momentumPhase;
+- (NSEventPhase)phase;
+- (BOOL)hasPreciseScrollingDeltas;
+- (CGFloat)scrollingDeltaX;
+- (CGFloat)scrollingDeltaY;
+- (void)trackSwipeEventWithOptions:(NSEventSwipeTrackingOptions)options
+ dampenAmountThresholdMin:(CGFloat)minDampenThreshold
+ max:(CGFloat)maxDampenThreshold
+ usingHandler:(void (^)(CGFloat gestureAmount,
+ NSEventPhase phase,
+ BOOL isComplete,
+ BOOL* stop))trackingHandler;
+- (BOOL)isDirectionInvertedFromDevice;
+@end
-@interface NSUserActivity (YosemiteSDK)
-@property(readonly, copy) NSString* activityType;
-@property(copy) NSDictionary* userInfo;
-@property(copy) NSURL* webpageURL;
-- (instancetype)initWithActivityType:(NSString*)activityType;
-- (void)becomeCurrent;
-- (void)invalidate;
+@interface NSApplication (LionSDK)
+- (void)disableRelaunchOnLogin;
@end
-@interface CBUUID (YosemiteSDK)
-- (NSString*)UUIDString;
+@interface CALayer (LionSDK)
+- (CGFloat)contentsScale;
+- (void)setContentsScale:(CGFloat)contentsScale;
@end
-@interface NSViewController (YosemiteSDK)
-- (void)viewDidLoad;
+@interface NSScreen (LionSDK)
+- (CGFloat)backingScaleFactor;
+- (NSRect)convertRectToBacking:(NSRect)aRect;
@end
-@interface NSWindow (YosemiteSDK)
-- (void)setTitlebarAppearsTransparent:(BOOL)flag;
+@interface NSWindow (LionSDK)
+- (CGFloat)backingScaleFactor;
+- (NSWindowAnimationBehavior)animationBehavior;
+- (void)setAnimationBehavior:(NSWindowAnimationBehavior)newAnimationBehavior;
+- (void)toggleFullScreen:(id)sender;
+- (void)setRestorable:(BOOL)flag;
+- (NSRect)convertRectFromScreen:(NSRect)aRect;
+- (NSRect)convertRectToScreen:(NSRect)aRect;
@end
-@interface NSProcessInfo (YosemiteSDK)
-@property(readonly) NSOperatingSystemVersion operatingSystemVersion;
+@interface NSCursor (LionSDKDeclarations)
++ (NSCursor*)IBeamCursorForVerticalLayout;
@end
-@interface NSLayoutConstraint (YosemiteSDK)
-@property(getter=isActive) BOOL active;
-+ (void)activateConstraints:(NSArray*)constraints;
+@interface NSAnimationContext (LionSDK)
++ (void)runAnimationGroup:(void (^)(NSAnimationContext* context))changes
+ completionHandler:(void (^)(void))completionHandler;
+@property(copy) void (^completionHandler)(void);
@end
-@interface NSVisualEffectView (YosemiteSDK)
-- (void)setState:(NSVisualEffectState)state;
+@interface NSView (LionSDK)
+- (NSSize)convertSizeFromBacking:(NSSize)size;
+- (void)setWantsBestResolutionOpenGLSurface:(BOOL)flag;
+- (NSDraggingSession*)beginDraggingSessionWithItems:(NSArray*)items
+ event:(NSEvent*)event
+ source:
+ (id<NSDraggingSource>)source;
@end
-@class NSVisualEffectView;
+@interface NSObject (ICCameraDeviceDelegateLionSDK)
+- (void)deviceDidBecomeReadyWithCompleteContentCatalog:(ICDevice*)device;
+- (void)didDownloadFile:(ICCameraFile*)file
+ error:(NSError*)error
+ options:(NSDictionary*)options
+ contextInfo:(void*)contextInfo;
+@end
-@interface CIQRCodeFeature (YosemiteSDK)
-@property(readonly) CGRect bounds;
-@property(readonly) CGPoint topLeft;
-@property(readonly) CGPoint topRight;
-@property(readonly) CGPoint bottomLeft;
-@property(readonly) CGPoint bottomRight;
-@property(readonly, copy) NSString* messageString;
+@interface CWInterface (LionSDK)
+- (BOOL)associateToNetwork:(CWNetwork*)network
+ password:(NSString*)password
+ error:(NSError**)error;
+- (NSSet*)scanForNetworksWithName:(NSString*)networkName error:(NSError**)error;
@end
-@class CIQRCodeFeature;
+@interface CWChannel (LionSDK)
+@property(readonly) CWChannelBand channelBand;
+@end
-@interface NSView (YosemiteSDK)
-- (BOOL)isAccessibilitySelectorAllowed:(SEL)selector;
+@interface CWNetwork (LionSDK)
+@property(readonly) CWChannel* wlanChannel;
+@property(readonly) NSInteger rssiValue;
+- (BOOL)supportsSecurity:(CWSecurity)security;
@end
-#endif // MAC_OS_X_VERSION_10_10
+@interface IOBluetoothHostController (LionSDK)
+- (NSString*)nameAsString;
+- (BluetoothHCIPowerState)powerState;
+@end
-// Once Chrome no longer supports OSX 10.10.2, everything within this
-// preprocessor block can be removed.
-#if !defined(MAC_OS_X_VERSION_10_10_3) || \
- MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10_3
+@interface IOBluetoothL2CAPChannel (LionSDK)
+@property(readonly) BluetoothL2CAPMTU outgoingMTU;
+@end
-@interface NSEvent (Yosemite_3_SDK)
-@property(readonly) NSInteger stage;
+@interface IOBluetoothDevice (LionSDK)
+- (NSString*)addressString;
+- (unsigned int)classOfDevice;
+- (BluetoothConnectionHandle)connectionHandle;
+- (BluetoothHCIRSSIValue)rawRSSI;
+- (NSArray*)services;
+- (IOReturn)performSDPQuery:(id)target uuids:(NSArray*)uuids;
@end
-@interface NSView (Yosemite_3_SDK)
-- (void)setPressureConfiguration:(NSPressureConfiguration*)aConfiguration;
+@interface CBPeripheral (LionSDK)
+@property(readonly, nonatomic) CFUUIDRef UUID;
+@property(retain, readonly) NSString* name;
+@property(readonly) BOOL isConnected;
@end
-#endif // MAC_OS_X_VERSION_10_10
+@interface CBCentralManager (LionSDK)
+@property(readonly) CBCentralManagerState state;
+- (id)initWithDelegate:(id<CBCentralManagerDelegate>)delegate
+ queue:(dispatch_queue_t)queue;
+- (void)scanForPeripheralsWithServices:(NSArray*)serviceUUIDs
+ options:(NSDictionary*)options;
+- (void)stopScan;
+@end
-// ----------------------------------------------------------------------------
-// Define NSStrings only available in newer versions of the OSX SDK to force
-// them to be statically linked.
-// ----------------------------------------------------------------------------
+@interface CBUUID (LionSDK)
+@property(nonatomic, readonly) NSData* data;
++ (CBUUID*)UUIDWithString:(NSString*)theString;
+@end
-extern "C" {
-#if !defined(MAC_OS_X_VERSION_10_11) || \
- MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11
-BASE_EXPORT extern NSString* const CIDetectorTypeText;
-#endif // MAC_OS_X_VERSION_10_11
-} // extern "C"
+BASE_EXPORT extern "C" void NSAccessibilityPostNotificationWithUserInfo(
+ id object,
+ NSString* notification,
+ NSDictionary* user_info);
-// Once Chrome no longer supports OSX 10.10, everything within this
-// preprocessor block can be removed.
-#if !defined(MAC_OS_X_VERSION_10_11) || \
- MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11
+#endif // MAC_OS_X_VERSION_10_7
-@class NSLayoutDimension;
-@class NSLayoutXAxisAnchor;
-@class NSLayoutYAxisAnchor;
+// Once Chrome no longer supports OSX 10.7, everything within this preprocessor
+// block can be removed.
+#if !defined(MAC_OS_X_VERSION_10_8) || \
+ MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8
-@interface NSObject (ElCapitanSDK)
-- (NSLayoutConstraint*)constraintEqualToConstant:(CGFloat)c;
+@interface NSColor (MountainLionSDK)
+- (CGColorRef)CGColor;
@end
-@interface NSView (ElCapitanSDK)
-@property(readonly, strong) NSLayoutXAxisAnchor* leftAnchor;
-@property(readonly, strong) NSLayoutXAxisAnchor* rightAnchor;
-@property(readonly, strong) NSLayoutYAxisAnchor* bottomAnchor;
-@property(readonly, strong) NSLayoutDimension* widthAnchor;
+@interface NSUUID (MountainLionSDK)
+- (NSString*)UUIDString;
@end
-@interface NSWindow (ElCapitanSDK)
-- (void)performWindowDragWithEvent:(NSEvent*)event;
+@interface NSControl (MountainLionSDK)
+@property BOOL allowsExpansionToolTips;
@end
-@interface CIRectangleFeature (ElCapitanSDK)
-@property(readonly) CGRect bounds;
+#endif // MAC_OS_X_VERSION_10_8
+
+// Once Chrome no longer supports OSX 10.8, everything within this preprocessor
+// block can be removed.
+#if !defined(MAC_OS_X_VERSION_10_9) || \
+ MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9
+
+@interface NSProgress (MavericksSDK)
+
+- (instancetype)initWithParent:(NSProgress*)parentProgressOrNil
+ userInfo:(NSDictionary*)userInfoOrNil;
+@property(copy) NSString* kind;
+
+@property int64_t totalUnitCount;
+@property int64_t completedUnitCount;
+
+@property(getter=isCancellable) BOOL cancellable;
+@property(getter=isPausable) BOOL pausable;
+@property(readonly, getter=isCancelled) BOOL cancelled;
+@property(readonly, getter=isPaused) BOOL paused;
+@property(copy) void (^cancellationHandler)(void);
+@property(copy) void (^pausingHandler)(void);
+- (void)cancel;
+- (void)pause;
+
+- (void)setUserInfoObject:(id)objectOrNil forKey:(NSString*)key;
+- (NSDictionary*)userInfo;
+
+@property(readonly, getter=isIndeterminate) BOOL indeterminate;
+@property(readonly) double fractionCompleted;
+
+- (void)publish;
+- (void)unpublish;
+
@end
-@class CIRectangleFeature;
+@interface NSScreen (MavericksSDK)
++ (BOOL)screensHaveSeparateSpaces;
+@end
-#endif // MAC_OS_X_VERSION_10_11
+@interface NSView (MavericksSDK)
+- (void)setCanDrawSubviewsIntoLayer:(BOOL)flag;
+- (void)setAppearance:(NSAppearance*)appearance;
+- (NSAppearance*)effectiveAppearance;
+@end
-// Once Chrome no longer supports OSX 10.11, everything within this
-// preprocessor block can be removed.
-#if !defined(MAC_OS_X_VERSION_10_12) || \
- MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12
+@interface NSWindow (MavericksSDK)
+- (NSWindowOcclusionState)occlusionState;
+@end
+
+@interface NSAppearance (MavericksSDK)
++ (id<NSObject>)appearanceNamed:(NSString*)name;
+@end
+
+@interface CBPeripheral (MavericksSDK)
+@property(readonly, nonatomic) NSUUID* identifier;
+@end
+
+#endif // MAC_OS_X_VERSION_10_9
+
+// Once Chrome no longer supports OSX 10.9, everything within this preprocessor
+// block can be removed.
+#if !defined(MAC_OS_X_VERSION_10_10) || \
+ MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
-@interface NSWindow (SierraSDK)
-@property(class) BOOL allowsAutomaticWindowTabbing;
+@interface CBUUID (YosemiteSDK)
+- (NSString*)UUIDString;
@end
-#endif // MAC_OS_X_VERSION_10_12
+@interface NSViewController (YosemiteSDK)
+- (void)viewDidLoad;
+@end
-// Once Chrome no longer supports OSX 10.12.0, everything within this
+@interface NSWindow (YosemiteSDK)
+- (void)setTitlebarAppearsTransparent:(BOOL)flag;
+@end
+
+#endif // MAC_OS_X_VERSION_10_10
+
+// Once Chrome no longer supports OSX 10.10.2, everything within this
// preprocessor block can be removed.
-#if !defined(MAC_OS_X_VERSION_10_12_1) || \
- MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12_1
-
-@interface NSButton (SierraPointOneSDK)
-@property(copy) NSColor* bezelColor;
-@property BOOL imageHugsTitle;
-+ (instancetype)buttonWithTitle:(NSString*)title
- target:(id)target
- action:(SEL)action;
-+ (instancetype)buttonWithImage:(NSImage*)image
- target:(id)target
- action:(SEL)action;
-+ (instancetype)buttonWithTitle:(NSString*)title
- image:(NSImage*)image
- target:(id)target
- action:(SEL)action;
+#if !defined(MAC_OS_X_VERSION_10_10_3) || \
+ MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10_3
+
+@interface NSEvent (YosemiteSDK)
+@property(readonly) NSInteger stage;
@end
@interface NSView (YosemiteSDK)
@@ -250,6 +526,8 @@ BASE_EXPORT extern NSString* const CIDetectorTypeText;
// declared in the OSX 10.9+ SDK, so when compiling against an OSX 10.9+ SDK,
// declare the symbol.
// ----------------------------------------------------------------------------
+#if defined(MAC_OS_X_VERSION_10_9) && \
+ MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
BASE_EXPORT extern "C" NSString* const kCWSSIDDidChangeNotification;
-
+#endif
#endif // BASE_MAC_SDK_FORWARD_DECLARATIONS_H_
diff --git a/base/mac/sdk_forward_declarations.mm b/base/mac/sdk_forward_declarations.mm
index c624daedd8..4e1d7ec670 100644
--- a/base/mac/sdk_forward_declarations.mm
+++ b/base/mac/sdk_forward_declarations.mm
@@ -4,17 +4,43 @@
#include "base/mac/sdk_forward_declarations.h"
+#if !defined(MAC_OS_X_VERSION_10_7) || \
+ MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7
+NSString* const NSWindowWillEnterFullScreenNotification =
+ @"NSWindowWillEnterFullScreenNotification";
+
+NSString* const NSWindowWillExitFullScreenNotification =
+ @"NSWindowWillExitFullScreenNotification";
+
+NSString* const NSWindowDidEnterFullScreenNotification =
+ @"NSWindowDidEnterFullScreenNotification";
+
+NSString* const NSWindowDidExitFullScreenNotification =
+ @"NSWindowDidExitFullScreenNotification";
+
+NSString* const NSWindowDidChangeBackingPropertiesNotification =
+ @"NSWindowDidChangeBackingPropertiesNotification";
+
+NSString* const CBAdvertisementDataServiceDataKey = @"kCBAdvDataServiceData";
+
+NSString* const CBAdvertisementDataServiceUUIDsKey = @"kCBAdvDataServiceUUIDs";
+#endif // MAC_OS_X_VERSION_10_7
+
+#if !defined(MAC_OS_X_VERSION_10_9) || \
+ MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9
+NSString* const NSWindowDidChangeOcclusionStateNotification =
+ @"NSWindowDidChangeOcclusionStateNotification";
+
+NSString* const CBAdvertisementDataOverflowServiceUUIDsKey =
+ @"kCBAdvDataOverflowServiceUUIDs";
+
+NSString* const CBAdvertisementDataIsConnectable = @"kCBAdvDataIsConnectable";
+#endif // MAC_OS_X_VERSION_10_9
+
#if !defined(MAC_OS_X_VERSION_10_10) || \
MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
-NSString* const CIDetectorTypeQRCode = @"CIDetectorTypeQRCode";
-
NSString* const NSUserActivityTypeBrowsingWeb =
@"NSUserActivityTypeBrowsingWeb";
NSString* const NSAppearanceNameVibrantDark = @"NSAppearanceNameVibrantDark";
#endif // MAC_OS_X_VERSION_10_10
-
-#if !defined(MAC_OS_X_VERSION_10_11) || \
- MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11
-NSString* const CIDetectorTypeText = @"CIDetectorTypeText";
-#endif // MAC_OS_X_VERSION_10_11
diff --git a/base/memory/aligned_memory_unittest.cc b/base/memory/aligned_memory_unittest.cc
index 892c50ef70..abe0cf3ff5 100644
--- a/base/memory/aligned_memory_unittest.cc
+++ b/base/memory/aligned_memory_unittest.cc
@@ -44,6 +44,10 @@ TEST(AlignedMemoryTest, StackAlignment) {
EXPECT_ALIGNED(raw8.void_data(), 8);
EXPECT_ALIGNED(raw16.void_data(), 16);
+
+ // TODO(ios): __attribute__((aligned(X))) with X >= 128 does not works on
+ // the stack when building for arm64 on iOS, http://crbug.com/349003
+#if !(defined(OS_IOS) && defined(ARCH_CPU_ARM64))
EXPECT_ALIGNED(raw128.void_data(), 128);
// NaCl x86-64 compiler emits non-validating instructions for >128
@@ -57,10 +61,14 @@ TEST(AlignedMemoryTest, StackAlignment) {
EXPECT_EQ(256u, ALIGNOF(raw256));
EXPECT_ALIGNED(raw256.void_data(), 256);
+ // TODO(ios): This test hits an armv7 bug in clang. crbug.com/138066
+#if !(defined(OS_IOS) && defined(ARCH_CPU_ARM_FAMILY))
AlignedMemory<8, 4096> raw4096;
EXPECT_EQ(4096u, ALIGNOF(raw4096));
EXPECT_ALIGNED(raw4096.void_data(), 4096);
+#endif // !(defined(OS_IOS) && defined(ARCH_CPU_ARM_FAMILY))
#endif // !(defined(OS_NACL) && defined(ARCH_CPU_X86_64))
+#endif // !(defined(OS_IOS) && defined(ARCH_CPU_ARM64))
}
TEST(AlignedMemoryTest, DynamicAllocation) {
diff --git a/base/memory/linked_ptr.h b/base/memory/linked_ptr.h
index 68512864b2..649dc10db7 100644
--- a/base/memory/linked_ptr.h
+++ b/base/memory/linked_ptr.h
@@ -69,7 +69,7 @@ class linked_ptr_internal {
mutable linked_ptr_internal const* next_;
};
-// TODO(http://crbug.com/556939): DEPRECATED: Use unique_ptr instead (now that
+// TODO(http://crbug.com/556939): DEPRECATED: Use scoped_ptr instead (now that
// we have support for moveable types inside STL containers).
template <typename T>
class linked_ptr {
diff --git a/base/memory/ref_counted.cc b/base/memory/ref_counted.cc
index 46bbd7ad85..f5924d0fe7 100644
--- a/base/memory/ref_counted.cc
+++ b/base/memory/ref_counted.cc
@@ -10,32 +10,37 @@ namespace base {
namespace subtle {
bool RefCountedThreadSafeBase::HasOneRef() const {
- return AtomicRefCountIsOne(&ref_count_);
+ return AtomicRefCountIsOne(
+ &const_cast<RefCountedThreadSafeBase*>(this)->ref_count_);
}
-RefCountedThreadSafeBase::RefCountedThreadSafeBase() = default;
+RefCountedThreadSafeBase::RefCountedThreadSafeBase() : ref_count_(0) {
+#ifndef NDEBUG
+ in_dtor_ = false;
+#endif
+}
RefCountedThreadSafeBase::~RefCountedThreadSafeBase() {
-#if DCHECK_IS_ON()
+#ifndef NDEBUG
DCHECK(in_dtor_) << "RefCountedThreadSafe object deleted without "
"calling Release()";
#endif
}
void RefCountedThreadSafeBase::AddRef() const {
-#if DCHECK_IS_ON()
+#ifndef NDEBUG
DCHECK(!in_dtor_);
#endif
AtomicRefCountInc(&ref_count_);
}
bool RefCountedThreadSafeBase::Release() const {
-#if DCHECK_IS_ON()
+#ifndef NDEBUG
DCHECK(!in_dtor_);
DCHECK(!AtomicRefCountIsZero(&ref_count_));
#endif
if (!AtomicRefCountDec(&ref_count_)) {
-#if DCHECK_IS_ON()
+#ifndef NDEBUG
in_dtor_ = true;
#endif
return true;
diff --git a/base/memory/ref_counted.h b/base/memory/ref_counted.h
index 9dd09ad346..b026d9ab03 100644
--- a/base/memory/ref_counted.h
+++ b/base/memory/ref_counted.h
@@ -14,8 +14,10 @@
#include "base/atomic_ref_count.h"
#include "base/base_export.h"
#include "base/compiler_specific.h"
-#include "base/logging.h"
#include "base/macros.h"
+#ifndef NDEBUG
+#include "base/logging.h"
+#endif
#include "base/threading/thread_collision_warner.h"
#include "build/build_config.h"
@@ -30,16 +32,16 @@ class BASE_EXPORT RefCountedBase {
protected:
RefCountedBase()
: ref_count_(0)
-#if DCHECK_IS_ON()
- , in_dtor_(false)
-#endif
- {
+ #ifndef NDEBUG
+ , in_dtor_(false)
+ #endif
+ {
}
~RefCountedBase() {
-#if DCHECK_IS_ON()
+ #ifndef NDEBUG
DCHECK(in_dtor_) << "RefCounted object deleted without calling Release()";
-#endif
+ #endif
}
@@ -48,9 +50,9 @@ class BASE_EXPORT RefCountedBase {
// Current thread books the critical section "AddRelease"
// without release it.
// DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_);
-#if DCHECK_IS_ON()
+ #ifndef NDEBUG
DCHECK(!in_dtor_);
-#endif
+ #endif
++ref_count_;
}
@@ -60,21 +62,21 @@ class BASE_EXPORT RefCountedBase {
// Current thread books the critical section "AddRelease"
// without release it.
// DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_);
-#if DCHECK_IS_ON()
+ #ifndef NDEBUG
DCHECK(!in_dtor_);
-#endif
+ #endif
if (--ref_count_ == 0) {
-#if DCHECK_IS_ON()
+ #ifndef NDEBUG
in_dtor_ = true;
-#endif
+ #endif
return true;
}
return false;
}
private:
- mutable size_t ref_count_;
-#if DCHECK_IS_ON()
+ mutable int ref_count_;
+#ifndef NDEBUG
mutable bool in_dtor_;
#endif
@@ -97,9 +99,9 @@ class BASE_EXPORT RefCountedThreadSafeBase {
bool Release() const;
private:
- mutable AtomicRefCount ref_count_ = 0;
-#if DCHECK_IS_ON()
- mutable bool in_dtor_ = false;
+ mutable AtomicRefCount ref_count_;
+#ifndef NDEBUG
+ mutable bool in_dtor_;
#endif
DISALLOW_COPY_AND_ASSIGN(RefCountedThreadSafeBase);
@@ -124,7 +126,7 @@ class BASE_EXPORT RefCountedThreadSafeBase {
template <class T>
class RefCounted : public subtle::RefCountedBase {
public:
- RefCounted() = default;
+ RefCounted() {}
void AddRef() const {
subtle::RefCountedBase::AddRef();
@@ -137,7 +139,7 @@ class RefCounted : public subtle::RefCountedBase {
}
protected:
- ~RefCounted() = default;
+ ~RefCounted() {}
private:
DISALLOW_COPY_AND_ASSIGN(RefCounted<T>);
@@ -174,7 +176,7 @@ struct DefaultRefCountedThreadSafeTraits {
template <class T, typename Traits = DefaultRefCountedThreadSafeTraits<T> >
class RefCountedThreadSafe : public subtle::RefCountedThreadSafeBase {
public:
- RefCountedThreadSafe() = default;
+ RefCountedThreadSafe() {}
void AddRef() const {
subtle::RefCountedThreadSafeBase::AddRef();
@@ -187,7 +189,7 @@ class RefCountedThreadSafe : public subtle::RefCountedThreadSafeBase {
}
protected:
- ~RefCountedThreadSafe() = default;
+ ~RefCountedThreadSafe() {}
private:
friend struct DefaultRefCountedThreadSafeTraits<T>;
@@ -211,7 +213,7 @@ class RefCountedData
private:
friend class base::RefCountedThreadSafe<base::RefCountedData<T> >;
- ~RefCountedData() = default;
+ ~RefCountedData() {}
};
} // namespace base
@@ -224,9 +226,6 @@ class RefCountedData
//
// class MyFoo : public RefCounted<MyFoo> {
// ...
-// private:
-// friend class RefCounted<MyFoo>; // Allow destruction by RefCounted<>.
-// ~MyFoo(); // Destructor must be private/protected.
// };
//
// void some_function() {
@@ -238,7 +237,7 @@ class RefCountedData
// void some_other_function() {
// scoped_refptr<MyFoo> foo = new MyFoo();
// ...
-// foo = nullptr; // explicitly releases |foo|
+// foo = NULL; // explicitly releases |foo|
// ...
// if (foo)
// foo->Method(param);
@@ -253,7 +252,7 @@ class RefCountedData
// scoped_refptr<MyFoo> b;
//
// b.swap(a);
-// // now, |b| references the MyFoo object, and |a| references nullptr.
+// // now, |b| references the MyFoo object, and |a| references NULL.
// }
//
// To make both |a| and |b| in the above example reference the same MyFoo
@@ -272,7 +271,8 @@ class scoped_refptr {
public:
typedef T element_type;
- scoped_refptr() {}
+ scoped_refptr() : ptr_(NULL) {
+ }
scoped_refptr(T* p) : ptr_(p) {
if (ptr_)
@@ -314,12 +314,12 @@ class scoped_refptr {
T* get() const { return ptr_; }
T& operator*() const {
- assert(ptr_ != nullptr);
+ assert(ptr_ != NULL);
return *ptr_;
}
T* operator->() const {
- assert(ptr_ != nullptr);
+ assert(ptr_ != NULL);
return ptr_;
}
@@ -382,7 +382,7 @@ class scoped_refptr {
}
protected:
- T* ptr_ = nullptr;
+ T* ptr_;
private:
// Friend required for move constructors that set r.ptr_ to null.
@@ -397,13 +397,11 @@ class scoped_refptr {
static void Release(T* ptr);
};
-// static
template <typename T>
void scoped_refptr<T>::AddRef(T* ptr) {
ptr->AddRef();
}
-// static
template <typename T>
void scoped_refptr<T>::Release(T* ptr) {
ptr->Release();
diff --git a/base/memory/ref_counted_delete_on_message_loop.h b/base/memory/ref_counted_delete_on_message_loop.h
new file mode 100644
index 0000000000..de194e8479
--- /dev/null
+++ b/base/memory/ref_counted_delete_on_message_loop.h
@@ -0,0 +1,75 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MEMORY_REF_COUNTED_DELETE_ON_MESSAGE_LOOP_H_
+#define BASE_MEMORY_REF_COUNTED_DELETE_ON_MESSAGE_LOOP_H_
+
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+
+namespace base {
+
+// RefCountedDeleteOnMessageLoop is similar to RefCountedThreadSafe, and ensures
+// that the object will be deleted on a specified message loop.
+//
+// Sample usage:
+// class Foo : public RefCountedDeleteOnMessageLoop<Foo> {
+//
+// Foo(scoped_refptr<SingleThreadTaskRunner> loop)
+// : RefCountedDeleteOnMessageLoop<Foo>(std::move(loop)) {}
+// ...
+// private:
+// friend class RefCountedDeleteOnMessageLoop<Foo>;
+// friend class DeleteHelper<Foo>;
+//
+// ~Foo();
+// };
+
+// TODO(skyostil): Rename this to RefCountedDeleteOnTaskRunner.
+template <class T>
+class RefCountedDeleteOnMessageLoop : public subtle::RefCountedThreadSafeBase {
+ public:
+ // This constructor will accept a MessageL00pProxy object, but new code should
+ // prefer a SingleThreadTaskRunner. A SingleThreadTaskRunner for the
+ // MessageLoop on the current thread can be acquired by calling
+ // MessageLoop::current()->task_runner().
+ RefCountedDeleteOnMessageLoop(
+ scoped_refptr<SingleThreadTaskRunner> task_runner)
+ : task_runner_(std::move(task_runner)) {
+ DCHECK(task_runner_);
+ }
+
+ void AddRef() const {
+ subtle::RefCountedThreadSafeBase::AddRef();
+ }
+
+ void Release() const {
+ if (subtle::RefCountedThreadSafeBase::Release())
+ DestructOnMessageLoop();
+ }
+
+ protected:
+ friend class DeleteHelper<RefCountedDeleteOnMessageLoop>;
+ ~RefCountedDeleteOnMessageLoop() {}
+
+ void DestructOnMessageLoop() const {
+ const T* t = static_cast<const T*>(this);
+ if (task_runner_->BelongsToCurrentThread())
+ delete t;
+ else
+ task_runner_->DeleteSoon(FROM_HERE, t);
+ }
+
+ scoped_refptr<SingleThreadTaskRunner> task_runner_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RefCountedDeleteOnMessageLoop);
+};
+
+} // namespace base
+
+#endif // BASE_MEMORY_REF_COUNTED_DELETE_ON_MESSAGE_LOOP_H_
diff --git a/base/memory/ref_counted_unittest.cc b/base/memory/ref_counted_unittest.cc
index 65c15d26ab..7c4e07af49 100644
--- a/base/memory/ref_counted_unittest.cc
+++ b/base/memory/ref_counted_unittest.cc
@@ -4,8 +4,6 @@
#include "base/memory/ref_counted.h"
-#include <utility>
-
#include "base/test/opaque_ref_counted.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -158,34 +156,13 @@ TEST(RefCountedUnitTest, ScopedRefPtrToSelfMoveAssignment) {
}
TEST(RefCountedUnitTest, ScopedRefPtrToOpaque) {
- scoped_refptr<base::OpaqueRefCounted> initial = base::MakeOpaqueRefCounted();
- base::TestOpaqueRefCounted(initial);
-
- scoped_refptr<base::OpaqueRefCounted> assigned;
- assigned = initial;
-
- scoped_refptr<base::OpaqueRefCounted> copied(initial);
-
- scoped_refptr<base::OpaqueRefCounted> moved(std::move(initial));
-
- scoped_refptr<base::OpaqueRefCounted> move_assigned;
- move_assigned = std::move(moved);
-}
-
-TEST(RefCountedUnitTest, ScopedRefPtrToOpaqueThreadSafe) {
- scoped_refptr<base::OpaqueRefCountedThreadSafe> initial =
- base::MakeOpaqueRefCountedThreadSafe();
- base::TestOpaqueRefCountedThreadSafe(initial);
-
- scoped_refptr<base::OpaqueRefCountedThreadSafe> assigned;
- assigned = initial;
-
- scoped_refptr<base::OpaqueRefCountedThreadSafe> copied(initial);
-
- scoped_refptr<base::OpaqueRefCountedThreadSafe> moved(std::move(initial));
+ scoped_refptr<base::OpaqueRefCounted> p = base::MakeOpaqueRefCounted();
+ base::TestOpaqueRefCounted(p);
- scoped_refptr<base::OpaqueRefCountedThreadSafe> move_assigned;
- move_assigned = std::move(moved);
+ scoped_refptr<base::OpaqueRefCounted> q;
+ q = p;
+ base::TestOpaqueRefCounted(p);
+ base::TestOpaqueRefCounted(q);
}
TEST(RefCountedUnitTest, BooleanTesting) {
diff --git a/base/memory/scoped_vector.h b/base/memory/scoped_vector.h
index a320b1e5d1..f3581eaa9b 100644
--- a/base/memory/scoped_vector.h
+++ b/base/memory/scoped_vector.h
@@ -12,6 +12,7 @@
#include "base/logging.h"
#include "base/macros.h"
+#include "base/stl_util.h"
// ScopedVector wraps a vector deleting the elements from its
// destructor.
@@ -87,10 +88,8 @@ class ScopedVector {
// Resize, deleting elements in the disappearing range if we are shrinking.
void resize(size_t new_size) {
- if (v_.size() > new_size) {
- for (auto it = v_.begin() + new_size; it != v_.end(); ++it)
- delete *it;
- }
+ if (v_.size() > new_size)
+ STLDeleteContainerPointers(v_.begin() + new_size, v_.end());
v_.resize(new_size);
}
@@ -99,11 +98,7 @@ class ScopedVector {
v_.assign(begin, end);
}
- void clear() {
- for (auto* item : *this)
- delete item;
- v_.clear();
- }
+ void clear() { STLDeleteElements(&v_); }
// Like |clear()|, but doesn't delete any elements.
void weak_clear() { v_.clear(); }
@@ -129,8 +124,7 @@ class ScopedVector {
}
iterator erase(iterator first, iterator last) {
- for (auto it = first; it != last; ++it)
- delete *it;
+ STLDeleteContainerPointers(first, last);
return v_.erase(first, last);
}
diff --git a/base/memory/scoped_vector_unittest.cc b/base/memory/scoped_vector_unittest.cc
index 916dab9a15..ea3dcdc485 100644
--- a/base/memory/scoped_vector_unittest.cc
+++ b/base/memory/scoped_vector_unittest.cc
@@ -322,7 +322,7 @@ TEST(ScopedVectorTest, InsertRange) {
EXPECT_EQ(LC_CONSTRUCTED, it->life_cycle_state());
}
-// Assertions for push_back(unique_ptr).
+// Assertions for push_back(scoped_ptr).
TEST(ScopedVectorTest, PushBackScopedPtr) {
int delete_counter = 0;
std::unique_ptr<DeleteCounter> elem(new DeleteCounter(&delete_counter));
diff --git a/base/memory/shared_memory.h b/base/memory/shared_memory.h
index 4b66cc6edd..e1c9fa70bd 100644
--- a/base/memory/shared_memory.h
+++ b/base/memory/shared_memory.h
@@ -34,32 +34,31 @@ class FilePath;
// Options for creating a shared memory object.
struct BASE_EXPORT SharedMemoryCreateOptions {
-#if defined(OS_MACOSX) && !defined(OS_IOS)
- // The type of OS primitive that should back the SharedMemory object.
- SharedMemoryHandle::Type type = SharedMemoryHandle::MACH;
-#else
+ SharedMemoryCreateOptions();
+
+#if !(defined(OS_MACOSX) && !defined(OS_IOS))
// DEPRECATED (crbug.com/345734):
// If NULL, the object is anonymous. This pointer is owned by the caller
// and must live through the call to Create().
- const std::string* name_deprecated = nullptr;
+ const std::string* name_deprecated;
// DEPRECATED (crbug.com/345734):
// If true, and the shared memory already exists, Create() will open the
// existing shared memory and ignore the size parameter. If false,
// shared memory must not exist. This flag is meaningless unless
// name_deprecated is non-NULL.
- bool open_existing_deprecated = false;
-#endif // defined(OS_MACOSX) && !defined(OS_IOS)
+ bool open_existing_deprecated;
+#endif // !(defined(OS_MACOSX) && !defined(OS_IOS))
// Size of the shared memory object to be created.
// When opening an existing object, this has no effect.
- size_t size = 0;
+ size_t size;
// If true, mappings might need to be made executable later.
- bool executable = false;
+ bool executable;
// If true, the file can be shared read-only to a process.
- bool share_read_only = false;
+ bool share_read_only;
};
// Platform abstraction for shared memory. Provides a C++ wrapper
@@ -104,7 +103,7 @@ class BASE_EXPORT SharedMemory {
// The caller is responsible for destroying the duplicated OS primitive.
static SharedMemoryHandle DuplicateHandle(const SharedMemoryHandle& handle);
-#if defined(OS_POSIX)
+#if defined(OS_POSIX) && !(defined(OS_MACOSX) && !defined(OS_IOS))
// This method requires that the SharedMemoryHandle is backed by a POSIX fd.
static int GetFdFromSharedMemoryHandle(const SharedMemoryHandle& handle);
#endif
@@ -195,13 +194,6 @@ class BASE_EXPORT SharedMemory {
// identifier is not portable.
SharedMemoryHandle handle() const;
- // Returns the underlying OS handle for this segment. The caller also gets
- // ownership of the handle. This is logically equivalent to:
- // SharedMemoryHandle dup = DuplicateHandle(handle());
- // Close();
- // return dup;
- SharedMemoryHandle TakeHandle();
-
// Closes the open shared memory segment. The memory will remain mapped if
// it was previously mapped.
// It is safe to call Close repeatedly.
@@ -255,36 +247,16 @@ class BASE_EXPORT SharedMemory {
return ShareToProcessCommon(process, new_handle, true, SHARE_CURRENT_MODE);
}
-#if defined(OS_POSIX) && (!defined(OS_MACOSX) || defined(OS_IOS)) && \
- !defined(OS_NACL)
- using UniqueId = std::pair<dev_t, ino_t>;
-
- struct UniqueIdHash {
- size_t operator()(const UniqueId& id) const {
- return HashInts(id.first, id.second);
- }
- };
-
- // Returns a unique ID for this shared memory's handle. Note this function may
- // access file system and be slow.
- bool GetUniqueId(UniqueId* id) const;
-#endif
-
private:
#if defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_ANDROID) && \
- (!defined(OS_MACOSX) || defined(OS_IOS))
+ !(defined(OS_MACOSX) && !defined(OS_IOS))
+ bool PrepareMapFile(ScopedFILE fp, ScopedFD readonly);
bool FilePathForMemoryName(const std::string& mem_name, FilePath* path);
#endif
-
enum ShareMode {
SHARE_READONLY,
SHARE_CURRENT_MODE,
};
-
-#if defined(OS_MACOSX)
- bool Share(SharedMemoryHandle* new_handle, ShareMode share_mode);
-#endif
-
bool ShareToProcessCommon(ProcessHandle process,
SharedMemoryHandle* new_handle,
bool close_self,
@@ -299,12 +271,6 @@ class BASE_EXPORT SharedMemory {
#elif defined(OS_MACOSX) && !defined(OS_IOS)
// The OS primitive that backs the shared memory region.
SharedMemoryHandle shm_;
-
- // The mechanism by which the memory is mapped. Only valid if |memory_| is not
- // |nullptr|.
- SharedMemoryHandle::Type mapped_memory_mechanism_;
-
- int readonly_mapped_file_;
#elif defined(OS_POSIX)
int mapped_file_;
int readonly_mapped_file_;
@@ -316,7 +282,6 @@ class BASE_EXPORT SharedMemory {
DISALLOW_COPY_AND_ASSIGN(SharedMemory);
};
-
} // namespace base
#endif // BASE_MEMORY_SHARED_MEMORY_H_
diff --git a/base/memory/shared_memory_handle.h b/base/memory/shared_memory_handle.h
index dc33eeafa1..8eff26b9dc 100644
--- a/base/memory/shared_memory_handle.h
+++ b/base/memory/shared_memory_handle.h
@@ -15,7 +15,6 @@
#elif defined(OS_MACOSX) && !defined(OS_IOS)
#include <mach/mach.h>
#include "base/base_export.h"
-#include "base/file_descriptor_posix.h"
#include "base/macros.h"
#include "base/process/process_handle.h"
#elif defined(OS_POSIX)
@@ -25,6 +24,8 @@
namespace base {
+class Pickle;
+
// SharedMemoryHandle is a platform specific type which represents
// the underlying OS handle to a shared memory segment.
#if defined(OS_POSIX) && !(defined(OS_MACOSX) && !defined(OS_IOS))
@@ -84,25 +85,9 @@ class BASE_EXPORT SharedMemoryHandle {
#else
class BASE_EXPORT SharedMemoryHandle {
public:
- enum Type {
- // The SharedMemoryHandle is backed by a POSIX fd.
- POSIX,
- // The SharedMemoryHandle is backed by the Mach primitive "memory object".
- MACH,
- };
-
// The default constructor returns an invalid SharedMemoryHandle.
SharedMemoryHandle();
- // Constructs a SharedMemoryHandle backed by the components of a
- // FileDescriptor. The newly created instance has the same ownership semantics
- // as base::FileDescriptor. This typically means that the SharedMemoryHandle
- // takes ownership of the |fd| if |auto_close| is true. Unfortunately, it's
- // common for existing code to make shallow copies of SharedMemoryHandle, and
- // the one that is finally passed into a base::SharedMemory is the one that
- // "consumes" the fd.
- explicit SharedMemoryHandle(const base::FileDescriptor& file_descriptor);
-
// Makes a Mach-based SharedMemoryHandle of the given size. On error,
// subsequent calls to IsValid() return false.
explicit SharedMemoryHandle(mach_vm_size_t size);
@@ -137,7 +122,7 @@ class BASE_EXPORT SharedMemoryHandle {
mach_port_t GetMemoryObject() const;
// Returns false on a failure to determine the size. On success, populates the
- // output variable |size|.
+ // output variable |size|. Returns 0 if the handle is invalid.
bool GetSize(size_t* size) const;
// The SharedMemoryHandle must be valid.
@@ -153,36 +138,24 @@ class BASE_EXPORT SharedMemoryHandle {
bool OwnershipPassesToIPC() const;
private:
- friend class SharedMemory;
-
// Shared code between copy constructor and operator=.
void CopyRelevantData(const SharedMemoryHandle& handle);
- Type type_;
+ mach_port_t memory_object_ = MACH_PORT_NULL;
- // Each instance of a SharedMemoryHandle is backed either by a POSIX fd or a
- // mach port. |type_| determines the backing member.
- union {
- FileDescriptor file_descriptor_;
+ // The size of the shared memory region when |type_| is MACH. Only
+ // relevant if |memory_object_| is not |MACH_PORT_NULL|.
+ mach_vm_size_t size_ = 0;
- struct {
- mach_port_t memory_object_;
+ // The pid of the process in which |memory_object_| is usable. Only
+ // relevant if |memory_object_| is not |MACH_PORT_NULL|.
+ base::ProcessId pid_ = 0;
- // The size of the shared memory region when |type_| is MACH. Only
- // relevant if |memory_object_| is not |MACH_PORT_NULL|.
- mach_vm_size_t size_;
-
- // The pid of the process in which |memory_object_| is usable. Only
- // relevant if |memory_object_| is not |MACH_PORT_NULL|.
- base::ProcessId pid_;
-
- // Whether passing this object as a parameter to an IPC message passes
- // ownership of |memory_object_| to the IPC stack. This is meant to mimic
- // the behavior of the |auto_close| parameter of FileDescriptor.
- // Defaults to |false|.
- bool ownership_passes_to_ipc_;
- };
- };
+ // Whether passing this object as a parameter to an IPC message passes
+ // ownership of |memory_object_| to the IPC stack. This is meant to mimic
+ // the behavior of the |auto_close| parameter of FileDescriptor.
+ // Defaults to |false|.
+ bool ownership_passes_to_ipc_ = false;
};
#endif
diff --git a/base/memory/shared_memory_handle_mac.cc b/base/memory/shared_memory_handle_mac.cc
index 9dfd3c1aea..ad470bea81 100644
--- a/base/memory/shared_memory_handle_mac.cc
+++ b/base/memory/shared_memory_handle_mac.cc
@@ -10,20 +10,13 @@
#include <unistd.h>
#include "base/mac/mac_util.h"
-#include "base/mac/mach_logging.h"
#include "base/posix/eintr_wrapper.h"
namespace base {
-SharedMemoryHandle::SharedMemoryHandle()
- : type_(MACH), memory_object_(MACH_PORT_NULL) {}
-
-SharedMemoryHandle::SharedMemoryHandle(
- const base::FileDescriptor& file_descriptor)
- : type_(POSIX), file_descriptor_(file_descriptor) {}
+SharedMemoryHandle::SharedMemoryHandle() {}
SharedMemoryHandle::SharedMemoryHandle(mach_vm_size_t size) {
- type_ = MACH;
mach_port_t named_right;
kern_return_t kr = mach_make_memory_entry_64(
mach_task_self(),
@@ -46,8 +39,7 @@ SharedMemoryHandle::SharedMemoryHandle(mach_vm_size_t size) {
SharedMemoryHandle::SharedMemoryHandle(mach_port_t memory_object,
mach_vm_size_t size,
base::ProcessId pid)
- : type_(MACH),
- memory_object_(memory_object),
+ : memory_object_(memory_object),
size_(size),
pid_(pid),
ownership_passes_to_ipc_(false) {}
@@ -61,50 +53,29 @@ SharedMemoryHandle& SharedMemoryHandle::operator=(
if (this == &handle)
return *this;
- type_ = handle.type_;
CopyRelevantData(handle);
return *this;
}
SharedMemoryHandle SharedMemoryHandle::Duplicate() const {
- switch (type_) {
- case POSIX: {
- if (!IsValid())
- return SharedMemoryHandle();
- int duped_fd = HANDLE_EINTR(dup(file_descriptor_.fd));
- if (duped_fd < 0)
- return SharedMemoryHandle();
- return SharedMemoryHandle(FileDescriptor(duped_fd, true));
- }
- case MACH: {
- if (!IsValid())
- return SharedMemoryHandle(MACH_PORT_NULL, 0, 0);
-
- // Increment the ref count.
- kern_return_t kr = mach_port_mod_refs(mach_task_self(), memory_object_,
- MACH_PORT_RIGHT_SEND, 1);
- DCHECK_EQ(kr, KERN_SUCCESS);
- SharedMemoryHandle handle(*this);
- handle.SetOwnershipPassesToIPC(true);
- return handle;
- }
- }
+ if (!IsValid())
+ return SharedMemoryHandle(MACH_PORT_NULL, 0, 0);
+
+ // Increment the ref count.
+ kern_return_t kr = mach_port_mod_refs(mach_task_self(), memory_object_,
+ MACH_PORT_RIGHT_SEND, 1);
+ DCHECK_EQ(kr, KERN_SUCCESS);
+ SharedMemoryHandle handle(*this);
+ handle.SetOwnershipPassesToIPC(true);
+ return handle;
}
bool SharedMemoryHandle::operator==(const SharedMemoryHandle& handle) const {
if (!IsValid() && !handle.IsValid())
return true;
- if (type_ != handle.type_)
- return false;
-
- switch (type_) {
- case POSIX:
- return file_descriptor_.fd == handle.file_descriptor_.fd;
- case MACH:
- return memory_object_ == handle.memory_object_ && size_ == handle.size_ &&
- pid_ == handle.pid_;
- }
+ return memory_object_ == handle.memory_object_ && size_ == handle.size_ &&
+ pid_ == handle.pid_;
}
bool SharedMemoryHandle::operator!=(const SharedMemoryHandle& handle) const {
@@ -112,16 +83,10 @@ bool SharedMemoryHandle::operator!=(const SharedMemoryHandle& handle) const {
}
bool SharedMemoryHandle::IsValid() const {
- switch (type_) {
- case POSIX:
- return file_descriptor_.fd >= 0;
- case MACH:
- return memory_object_ != MACH_PORT_NULL;
- }
+ return memory_object_ != MACH_PORT_NULL;
}
mach_port_t SharedMemoryHandle::GetMemoryObject() const {
- DCHECK_EQ(type_, MACH);
return memory_object_;
}
@@ -131,19 +96,8 @@ bool SharedMemoryHandle::GetSize(size_t* size) const {
return true;
}
- switch (type_) {
- case SharedMemoryHandle::POSIX:
- struct stat st;
- if (fstat(file_descriptor_.fd, &st) != 0)
- return false;
- if (st.st_size < 0)
- return false;
- *size = st.st_size;
- return true;
- case SharedMemoryHandle::MACH:
- *size = size_;
- return true;
- }
+ *size = size_;
+ return true;
}
bool SharedMemoryHandle::MapAt(off_t offset,
@@ -151,69 +105,42 @@ bool SharedMemoryHandle::MapAt(off_t offset,
void** memory,
bool read_only) {
DCHECK(IsValid());
- switch (type_) {
- case SharedMemoryHandle::POSIX:
- *memory = mmap(nullptr, bytes, PROT_READ | (read_only ? 0 : PROT_WRITE),
- MAP_SHARED, file_descriptor_.fd, offset);
- return *memory != MAP_FAILED;
- case SharedMemoryHandle::MACH:
- DCHECK_EQ(pid_, GetCurrentProcId());
- kern_return_t kr = mach_vm_map(
- mach_task_self(),
- reinterpret_cast<mach_vm_address_t*>(memory), // Output parameter
- bytes,
- 0, // Alignment mask
- VM_FLAGS_ANYWHERE,
- memory_object_,
- offset,
- FALSE, // Copy
- VM_PROT_READ | (read_only ? 0 : VM_PROT_WRITE), // Current protection
- VM_PROT_WRITE | VM_PROT_READ | VM_PROT_IS_MASK, // Maximum protection
- VM_INHERIT_NONE);
- return kr == KERN_SUCCESS;
- }
+ DCHECK_EQ(pid_, GetCurrentProcId());
+ kern_return_t kr = mach_vm_map(
+ mach_task_self(),
+ reinterpret_cast<mach_vm_address_t*>(memory), // Output parameter
+ bytes,
+ 0, // Alignment mask
+ VM_FLAGS_ANYWHERE, memory_object_, offset,
+ FALSE, // Copy
+ VM_PROT_READ | (read_only ? 0 : VM_PROT_WRITE), // Current protection
+ VM_PROT_WRITE | VM_PROT_READ | VM_PROT_IS_MASK, // Maximum protection
+ VM_INHERIT_NONE);
+ return kr == KERN_SUCCESS;
}
void SharedMemoryHandle::Close() const {
if (!IsValid())
return;
- switch (type_) {
- case POSIX:
- if (IGNORE_EINTR(close(file_descriptor_.fd)) < 0)
- DPLOG(ERROR) << "Error closing fd";
- break;
- case MACH:
- kern_return_t kr = mach_port_deallocate(mach_task_self(), memory_object_);
- if (kr != KERN_SUCCESS)
- MACH_DLOG(ERROR, kr) << "Error deallocating mach port";
- break;
- }
+ kern_return_t kr = mach_port_deallocate(mach_task_self(), memory_object_);
+ if (kr != KERN_SUCCESS)
+ DPLOG(ERROR) << "Error deallocating mach port: " << kr;
}
void SharedMemoryHandle::SetOwnershipPassesToIPC(bool ownership_passes) {
- DCHECK_EQ(type_, MACH);
ownership_passes_to_ipc_ = ownership_passes;
}
bool SharedMemoryHandle::OwnershipPassesToIPC() const {
- DCHECK_EQ(type_, MACH);
return ownership_passes_to_ipc_;
}
void SharedMemoryHandle::CopyRelevantData(const SharedMemoryHandle& handle) {
- type_ = handle.type_;
- switch (type_) {
- case POSIX:
- file_descriptor_ = handle.file_descriptor_;
- break;
- case MACH:
- memory_object_ = handle.memory_object_;
- size_ = handle.size_;
- pid_ = handle.pid_;
- ownership_passes_to_ipc_ = handle.ownership_passes_to_ipc_;
- break;
- }
+ memory_object_ = handle.memory_object_;
+ size_ = handle.size_;
+ pid_ = handle.pid_;
+ ownership_passes_to_ipc_ = handle.ownership_passes_to_ipc_;
}
} // namespace base
diff --git a/base/memory/shared_memory_helper.cc b/base/memory/shared_memory_helper.cc
deleted file mode 100644
index 7fbfb7afad..0000000000
--- a/base/memory/shared_memory_helper.cc
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2016 The Chromium 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/memory/shared_memory_helper.h"
-
-#include "base/threading/thread_restrictions.h"
-
-namespace base {
-
-struct ScopedPathUnlinkerTraits {
- static const FilePath* InvalidValue() { return nullptr; }
-
- static void Free(const FilePath* path) {
- if (unlink(path->value().c_str()))
- PLOG(WARNING) << "unlink";
- }
-};
-
-// Unlinks the FilePath when the object is destroyed.
-using ScopedPathUnlinker =
- ScopedGeneric<const FilePath*, ScopedPathUnlinkerTraits>;
-
-#if !defined(OS_ANDROID)
-bool CreateAnonymousSharedMemory(const SharedMemoryCreateOptions& options,
- ScopedFILE* fp,
- ScopedFD* readonly_fd,
- FilePath* path) {
-#if !(defined(OS_MACOSX) && !defined(OS_IOS))
- // It doesn't make sense to have a open-existing private piece of shmem
- DCHECK(!options.open_existing_deprecated);
-#endif // !(defined(OS_MACOSX) && !defined(OS_IOS)
- // Q: Why not use the shm_open() etc. APIs?
- // A: Because they're limited to 4mb on OS X. FFFFFFFUUUUUUUUUUU
- FilePath directory;
- ScopedPathUnlinker path_unlinker;
- if (!GetShmemTempDir(options.executable, &directory))
- return false;
-
- fp->reset(base::CreateAndOpenTemporaryFileInDir(directory, path));
-
- if (!*fp)
- return false;
-
- // Deleting the file prevents anyone else from mapping it in (making it
- // private), and prevents the need for cleanup (once the last fd is
- // closed, it is truly freed).
- path_unlinker.reset(path);
-
- if (options.share_read_only) {
- // Also open as readonly so that we can ShareReadOnlyToProcess.
- readonly_fd->reset(HANDLE_EINTR(open(path->value().c_str(), O_RDONLY)));
- if (!readonly_fd->is_valid()) {
- DPLOG(ERROR) << "open(\"" << path->value() << "\", O_RDONLY) failed";
- fp->reset();
- return false;
- }
- }
- return true;
-}
-
-bool PrepareMapFile(ScopedFILE fp, ScopedFD readonly_fd, int* mapped_file,
- int* readonly_mapped_file) {
- DCHECK_EQ(-1, *mapped_file);
- DCHECK_EQ(-1, *readonly_mapped_file);
- if (fp == NULL)
- return false;
-
- // This function theoretically can block on the disk, but realistically
- // the temporary files we create will just go into the buffer cache
- // and be deleted before they ever make it out to disk.
- base::ThreadRestrictions::ScopedAllowIO allow_io;
-
- if (readonly_fd.is_valid()) {
- struct stat st = {};
- if (fstat(fileno(fp.get()), &st))
- NOTREACHED();
-
- struct stat readonly_st = {};
- if (fstat(readonly_fd.get(), &readonly_st))
- NOTREACHED();
- if (st.st_dev != readonly_st.st_dev || st.st_ino != readonly_st.st_ino) {
- LOG(ERROR) << "writable and read-only inodes don't match; bailing";
- return false;
- }
- }
-
- *mapped_file = HANDLE_EINTR(dup(fileno(fp.get())));
- if (*mapped_file == -1) {
- NOTREACHED() << "Call to dup failed, errno=" << errno;
- }
- *readonly_mapped_file = readonly_fd.release();
-
- return true;
-}
-#endif // !defined(OS_ANDROID)
-
-} // namespace base
diff --git a/base/memory/shared_memory_helper.h b/base/memory/shared_memory_helper.h
deleted file mode 100644
index b515828c08..0000000000
--- a/base/memory/shared_memory_helper.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_MEMORY_SHARED_MEMORY_HELPER_H_
-#define BASE_MEMORY_SHARED_MEMORY_HELPER_H_
-
-#include "base/memory/shared_memory.h"
-
-#include <fcntl.h>
-
-namespace base {
-
-#if !defined(OS_ANDROID)
-// Makes a temporary file, fdopens it, and then unlinks it. |fp| is populated
-// with the fdopened FILE. |readonly_fd| is populated with the opened fd if
-// options.share_read_only is true. |path| is populated with the location of
-// the file before it was unlinked.
-// Returns false if there's an unhandled failure.
-bool CreateAnonymousSharedMemory(const SharedMemoryCreateOptions& options,
- ScopedFILE* fp,
- ScopedFD* readonly_fd,
- FilePath* path);
-
-// Takes the outputs of CreateAnonymousSharedMemory and maps them properly to
-// |mapped_file| or |readonly_mapped_file|, depending on which one is populated.
-bool PrepareMapFile(ScopedFILE fp, ScopedFD readonly_fd, int* mapped_file,
- int* readonly_mapped_file);
-#endif
-
-} // namespace base
-
-#endif // BASE_MEMORY_SHARED_MEMORY_HELPER_H_
diff --git a/base/memory/shared_memory_mac.cc b/base/memory/shared_memory_mac.cc
index d376daa579..d15c63266d 100644
--- a/base/memory/shared_memory_mac.cc
+++ b/base/memory/shared_memory_mac.cc
@@ -4,33 +4,22 @@
#include "base/memory/shared_memory.h"
-#include <errno.h>
#include <mach/mach_vm.h>
-#include <stddef.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <unistd.h>
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
+#include "base/mac/foundation_util.h"
#include "base/mac/mac_util.h"
#include "base/mac/scoped_mach_vm.h"
-#include "base/memory/shared_memory_helper.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram_macros.h"
-#include "base/posix/eintr_wrapper.h"
-#include "base/posix/safe_strerror.h"
#include "base/process/process_metrics.h"
+#include "base/profiler/scoped_tracker.h"
#include "base/scoped_generic.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
-#if defined(OS_MACOSX)
-#include "base/mac/foundation_util.h"
-#endif // OS_MACOSX
-
namespace base {
namespace {
@@ -78,21 +67,18 @@ bool MakeMachSharedMemoryHandleReadOnly(SharedMemoryHandle* new_handle,
return true;
}
-
} // namespace
+SharedMemoryCreateOptions::SharedMemoryCreateOptions()
+ : size(0),
+ executable(false),
+ share_read_only(false) {}
+
SharedMemory::SharedMemory()
- : mapped_memory_mechanism_(SharedMemoryHandle::MACH),
- readonly_mapped_file_(-1),
- mapped_size_(0),
- memory_(NULL),
- read_only_(false),
- requested_size_(0) {}
+ : mapped_size_(0), memory_(NULL), read_only_(false), requested_size_(0) {}
SharedMemory::SharedMemory(const SharedMemoryHandle& handle, bool read_only)
: shm_(handle),
- mapped_memory_mechanism_(SharedMemoryHandle::POSIX),
- readonly_mapped_file_(-1),
mapped_size_(0),
memory_(NULL),
read_only_(read_only),
@@ -120,7 +106,8 @@ void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) {
// static
size_t SharedMemory::GetHandleLimit() {
- return GetMaxFds();
+ // This should be effectively unlimited on OS X.
+ return 10000;
}
// static
@@ -129,12 +116,6 @@ SharedMemoryHandle SharedMemory::DuplicateHandle(
return handle.Duplicate();
}
-// static
-int SharedMemory::GetFdFromSharedMemoryHandle(
- const SharedMemoryHandle& handle) {
- return handle.file_descriptor_.fd;
-}
-
bool SharedMemory::CreateAndMapAnonymous(size_t size) {
return CreateAnonymous(size) && Map(size);
}
@@ -149,53 +130,20 @@ bool SharedMemory::GetSizeFromSharedMemoryHandle(
// Chromium mostly only uses the unique/private shmem as specified by
// "name == L"". The exception is in the StatsTable.
bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
+ // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466437
+ // is fixed.
+ tracked_objects::ScopedTracker tracking_profile1(
+ FROM_HERE_WITH_EXPLICIT_FUNCTION(
+ "466437 SharedMemory::Create::Start"));
DCHECK(!shm_.IsValid());
if (options.size == 0) return false;
if (options.size > static_cast<size_t>(std::numeric_limits<int>::max()))
return false;
- if (options.type == SharedMemoryHandle::MACH) {
- shm_ = SharedMemoryHandle(options.size);
- requested_size_ = options.size;
- return shm_.IsValid();
- }
-
- // This function theoretically can block on the disk. Both profiling of real
- // users and local instrumentation shows that this is a real problem.
- // https://code.google.com/p/chromium/issues/detail?id=466437
- base::ThreadRestrictions::ScopedAllowIO allow_io;
-
- ScopedFILE fp;
- ScopedFD readonly_fd;
-
- FilePath path;
- bool result = CreateAnonymousSharedMemory(options, &fp, &readonly_fd, &path);
- if (!result)
- return false;
-
- if (!fp) {
- PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed";
- return false;
- }
-
- // Get current size.
- struct stat stat;
- if (fstat(fileno(fp.get()), &stat) != 0)
- return false;
- const size_t current_size = stat.st_size;
- if (current_size != options.size) {
- if (HANDLE_EINTR(ftruncate(fileno(fp.get()), options.size)) != 0)
- return false;
- }
+ shm_ = SharedMemoryHandle(options.size);
requested_size_ = options.size;
-
- int mapped_file = -1;
- result = PrepareMapFile(std::move(fp), std::move(readonly_fd), &mapped_file,
- &readonly_mapped_file_);
-
- shm_ = SharedMemoryHandle(FileDescriptor(mapped_file, false));
- return result;
+ return shm_.IsValid();
}
bool SharedMemory::MapAt(off_t offset, size_t bytes) {
@@ -211,7 +159,6 @@ bool SharedMemory::MapAt(off_t offset, size_t bytes) {
mapped_size_ = bytes;
DCHECK_EQ(0U, reinterpret_cast<uintptr_t>(memory_) &
(SharedMemory::MAP_MINIMUM_ALIGNMENT - 1));
- mapped_memory_mechanism_ = shm_.type_;
} else {
memory_ = NULL;
}
@@ -223,105 +170,48 @@ bool SharedMemory::Unmap() {
if (memory_ == NULL)
return false;
- switch (mapped_memory_mechanism_) {
- case SharedMemoryHandle::POSIX:
- munmap(memory_, mapped_size_);
- break;
- case SharedMemoryHandle::MACH:
- mach_vm_deallocate(mach_task_self(),
- reinterpret_cast<mach_vm_address_t>(memory_),
- mapped_size_);
- break;
- }
-
+ mach_vm_deallocate(mach_task_self(),
+ reinterpret_cast<mach_vm_address_t>(memory_),
+ mapped_size_);
memory_ = NULL;
mapped_size_ = 0;
return true;
}
SharedMemoryHandle SharedMemory::handle() const {
- switch (shm_.type_) {
- case SharedMemoryHandle::POSIX:
- return SharedMemoryHandle(
- FileDescriptor(shm_.file_descriptor_.fd, false));
- case SharedMemoryHandle::MACH:
- return shm_;
- }
-}
-
-SharedMemoryHandle SharedMemory::TakeHandle() {
- SharedMemoryHandle dup = DuplicateHandle(handle());
- Close();
- return dup;
+ return shm_;
}
void SharedMemory::Close() {
shm_.Close();
shm_ = SharedMemoryHandle();
- if (shm_.type_ == SharedMemoryHandle::POSIX) {
- if (readonly_mapped_file_ > 0) {
- if (IGNORE_EINTR(close(readonly_mapped_file_)) < 0)
- PLOG(ERROR) << "close";
- readonly_mapped_file_ = -1;
- }
- }
}
-bool SharedMemory::Share(SharedMemoryHandle* new_handle, ShareMode share_mode) {
- if (shm_.type_ == SharedMemoryHandle::MACH) {
- DCHECK(shm_.IsValid());
-
- bool success = false;
- switch (share_mode) {
- case SHARE_CURRENT_MODE:
- *new_handle = shm_.Duplicate();
- success = true;
- break;
- case SHARE_READONLY:
- success = MakeMachSharedMemoryHandleReadOnly(new_handle, shm_, memory_);
- break;
- }
-
- if (success)
- new_handle->SetOwnershipPassesToIPC(true);
-
- return success;
- }
+bool SharedMemory::ShareToProcessCommon(ProcessHandle /*process*/,
+ SharedMemoryHandle* new_handle,
+ bool close_self,
+ ShareMode share_mode) {
+ DCHECK(shm_.IsValid());
- int handle_to_dup = -1;
+ bool success = false;
switch (share_mode) {
case SHARE_CURRENT_MODE:
- handle_to_dup = shm_.file_descriptor_.fd;
+ *new_handle = shm_.Duplicate();
+ success = true;
break;
case SHARE_READONLY:
- // We could imagine re-opening the file from /dev/fd, but that can't make
- // it readonly on Mac: https://codereview.chromium.org/27265002/#msg10
- CHECK_GE(readonly_mapped_file_, 0);
- handle_to_dup = readonly_mapped_file_;
+ success = MakeMachSharedMemoryHandleReadOnly(new_handle, shm_, memory_);
break;
}
- const int new_fd = HANDLE_EINTR(dup(handle_to_dup));
- if (new_fd < 0) {
- DPLOG(ERROR) << "dup() failed.";
- return false;
- }
-
- new_handle->file_descriptor_.fd = new_fd;
- new_handle->type_ = SharedMemoryHandle::POSIX;
-
- return true;
-}
+ if (success)
+ new_handle->SetOwnershipPassesToIPC(true);
-bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
- SharedMemoryHandle* new_handle,
- bool close_self,
- ShareMode share_mode) {
- bool success = Share(new_handle, share_mode);
if (close_self) {
Unmap();
Close();
}
+
return success;
}
diff --git a/base/memory/shared_memory_posix.cc b/base/memory/shared_memory_posix.cc
index fb1a343906..7e94223a5c 100644
--- a/base/memory/shared_memory_posix.cc
+++ b/base/memory/shared_memory_posix.cc
@@ -14,15 +14,12 @@
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
-#include "base/memory/shared_memory_helper.h"
-#include "base/memory/shared_memory_tracker.h"
#include "base/posix/eintr_wrapper.h"
#include "base/posix/safe_strerror.h"
#include "base/process/process_metrics.h"
+#include "base/profiler/scoped_tracker.h"
#include "base/scoped_generic.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/threading/thread_restrictions.h"
-#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#if defined(OS_ANDROID)
@@ -34,6 +31,84 @@
namespace base {
+namespace {
+
+struct ScopedPathUnlinkerTraits {
+ static FilePath* InvalidValue() { return nullptr; }
+
+ static void Free(FilePath* path) {
+ // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466437
+ // is fixed.
+ tracked_objects::ScopedTracker tracking_profile(
+ FROM_HERE_WITH_EXPLICIT_FUNCTION(
+ "466437 SharedMemory::Create::Unlink"));
+ if (unlink(path->value().c_str()))
+ PLOG(WARNING) << "unlink";
+ }
+};
+
+// Unlinks the FilePath when the object is destroyed.
+typedef ScopedGeneric<FilePath*, ScopedPathUnlinkerTraits> ScopedPathUnlinker;
+
+#if !defined(OS_ANDROID) && !defined(__ANDROID__)
+// Makes a temporary file, fdopens it, and then unlinks it. |fp| is populated
+// with the fdopened FILE. |readonly_fd| is populated with the opened fd if
+// options.share_read_only is true. |path| is populated with the location of
+// the file before it was unlinked.
+// Returns false if there's an unhandled failure.
+bool CreateAnonymousSharedMemory(const SharedMemoryCreateOptions& options,
+ ScopedFILE* fp,
+ ScopedFD* readonly_fd,
+ FilePath* path) {
+ // It doesn't make sense to have a open-existing private piece of shmem
+ DCHECK(!options.open_existing_deprecated);
+ // Q: Why not use the shm_open() etc. APIs?
+ // A: Because they're limited to 4mb on OS X. FFFFFFFUUUUUUUUUUU
+ FilePath directory;
+ ScopedPathUnlinker path_unlinker;
+ if (GetShmemTempDir(options.executable, &directory)) {
+ // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466437
+ // is fixed.
+ tracked_objects::ScopedTracker tracking_profile(
+ FROM_HERE_WITH_EXPLICIT_FUNCTION(
+ "466437 SharedMemory::Create::OpenTemporaryFile"));
+ fp->reset(base::CreateAndOpenTemporaryFileInDir(directory, path));
+
+ // Deleting the file prevents anyone else from mapping it in (making it
+ // private), and prevents the need for cleanup (once the last fd is
+ // closed, it is truly freed).
+ if (*fp)
+ path_unlinker.reset(path);
+ }
+
+ if (*fp) {
+ if (options.share_read_only) {
+ // TODO(erikchen): Remove ScopedTracker below once
+ // http://crbug.com/466437 is fixed.
+ tracked_objects::ScopedTracker tracking_profile(
+ FROM_HERE_WITH_EXPLICIT_FUNCTION(
+ "466437 SharedMemory::Create::OpenReadonly"));
+ // Also open as readonly so that we can ShareReadOnlyToProcess.
+ readonly_fd->reset(HANDLE_EINTR(open(path->value().c_str(), O_RDONLY)));
+ if (!readonly_fd->is_valid()) {
+ DPLOG(ERROR) << "open(\"" << path->value() << "\", O_RDONLY) failed";
+ fp->reset();
+ return false;
+ }
+ }
+ }
+ return true;
+}
+#endif // !defined(OS_ANDROID) && !defined(__ANDROID__)
+}
+
+SharedMemoryCreateOptions::SharedMemoryCreateOptions()
+ : name_deprecated(nullptr),
+ open_existing_deprecated(false),
+ size(0),
+ executable(false),
+ share_read_only(false) {}
+
SharedMemory::SharedMemory()
: mapped_file_(-1),
readonly_mapped_file_(-1),
@@ -119,6 +194,11 @@ bool SharedMemory::GetSizeFromSharedMemoryHandle(
// In case we want to delete it later, it may be useful to save the value
// of mem_filename after FilePathForMemoryName().
bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
+ // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466437
+ // is fixed.
+ tracked_objects::ScopedTracker tracking_profile1(
+ FROM_HERE_WITH_EXPLICIT_FUNCTION(
+ "466437 SharedMemory::Create::Start"));
DCHECK_EQ(-1, mapped_file_);
if (options.size == 0) return false;
@@ -208,7 +288,7 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
}
requested_size_ = options.size;
}
- if (fp == NULL) {
+ if (fp == nullptr) {
PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed";
FilePath dir = path.DirName();
if (access(dir.value().c_str(), W_OK | X_OK) < 0) {
@@ -221,8 +301,7 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
return false;
}
- return PrepareMapFile(std::move(fp), std::move(readonly_fd), &mapped_file_,
- &readonly_mapped_file_);
+ return PrepareMapFile(std::move(fp), std::move(readonly_fd));
}
// Our current implementation of shmem is with mmap()ing of files.
@@ -254,8 +333,7 @@ bool SharedMemory::Open(const std::string& name, bool read_only) {
DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed";
return false;
}
- return PrepareMapFile(std::move(fp), std::move(readonly_fd), &mapped_file_,
- &readonly_mapped_file_);
+ return PrepareMapFile(std::move(fp), std::move(readonly_fd));
}
#endif // !defined(OS_ANDROID) && !defined(__ANDROID__)
@@ -287,10 +365,8 @@ bool SharedMemory::MapAt(off_t offset, size_t bytes) {
bool mmap_succeeded = memory_ != (void*)-1 && memory_ != NULL;
if (mmap_succeeded) {
mapped_size_ = bytes;
- DCHECK_EQ(0U,
- reinterpret_cast<uintptr_t>(memory_) &
- (SharedMemory::MAP_MINIMUM_ALIGNMENT - 1));
- SharedMemoryTracker::GetInstance()->IncrementMemoryUsage(*this);
+ DCHECK_EQ(0U, reinterpret_cast<uintptr_t>(memory_) &
+ (SharedMemory::MAP_MINIMUM_ALIGNMENT - 1));
} else {
memory_ = NULL;
}
@@ -303,7 +379,6 @@ bool SharedMemory::Unmap() {
return false;
munmap(memory_, mapped_size_);
- SharedMemoryTracker::GetInstance()->DecrementMemoryUsage(*this);
memory_ = NULL;
mapped_size_ = 0;
return true;
@@ -313,14 +388,6 @@ SharedMemoryHandle SharedMemory::handle() const {
return FileDescriptor(mapped_file_, false);
}
-SharedMemoryHandle SharedMemory::TakeHandle() {
- FileDescriptor handle(mapped_file_, true);
- mapped_file_ = -1;
- memory_ = nullptr;
- mapped_size_ = 0;
- return handle;
-}
-
void SharedMemory::Close() {
if (mapped_file_ > 0) {
if (IGNORE_EINTR(close(mapped_file_)) < 0)
@@ -335,6 +402,44 @@ void SharedMemory::Close() {
}
#if !defined(OS_ANDROID) && !defined(__ANDROID__)
+bool SharedMemory::PrepareMapFile(ScopedFILE fp, ScopedFD readonly_fd) {
+ DCHECK_EQ(-1, mapped_file_);
+ DCHECK_EQ(-1, readonly_mapped_file_);
+ if (fp == nullptr)
+ return false;
+
+ // This function theoretically can block on the disk, but realistically
+ // the temporary files we create will just go into the buffer cache
+ // and be deleted before they ever make it out to disk.
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+
+ struct stat st = {};
+ if (fstat(fileno(fp.get()), &st))
+ NOTREACHED();
+ if (readonly_fd.is_valid()) {
+ struct stat readonly_st = {};
+ if (fstat(readonly_fd.get(), &readonly_st))
+ NOTREACHED();
+ if (st.st_dev != readonly_st.st_dev || st.st_ino != readonly_st.st_ino) {
+ LOG(ERROR) << "writable and read-only inodes don't match; bailing";
+ return false;
+ }
+ }
+
+ mapped_file_ = HANDLE_EINTR(dup(fileno(fp.get())));
+ if (mapped_file_ == -1) {
+ if (errno == EMFILE) {
+ LOG(WARNING) << "Shared memory creation failed; out of file descriptors";
+ return false;
+ } else {
+ NOTREACHED() << "Call to dup failed, errno=" << errno;
+ }
+ }
+ readonly_mapped_file_ = readonly_fd.release();
+
+ return true;
+}
+
// For the given shmem named |mem_name|, return a filename to mmap()
// (and possibly create). Modifies |filename|. Return false on
// error, or true of we are happy.
@@ -397,22 +502,4 @@ bool SharedMemory::ShareToProcessCommon(ProcessHandle,
return true;
}
-bool SharedMemory::GetUniqueId(SharedMemory::UniqueId* id) const {
- // This function is called just after mmap. fstat is a system call that might
- // cause I/O. It's safe to call fstat here because mmap for shared memory is
- // called in two cases:
- // 1) To handle file-mapped memory
- // 2) To handle annonymous shared memory
- // In 1), I/O is already permitted. In 2), the backend is on page cache and
- // fstat doesn't cause I/O access to the disk. See the discussion at
- // crbug.com/604726#c41.
- base::ThreadRestrictions::ScopedAllowIO allow_io;
- struct stat file_stat;
- if (HANDLE_EINTR(::fstat(static_cast<int>(handle().fd), &file_stat)) != 0)
- return false;
- id->first = file_stat.st_dev;
- id->second = file_stat.st_ino;
- return true;
-}
-
} // namespace base
diff --git a/base/memory/shared_memory_tracker.cc b/base/memory/shared_memory_tracker.cc
deleted file mode 100644
index cfd4c85c53..0000000000
--- a/base/memory/shared_memory_tracker.cc
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2017 The Chromium 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/memory/shared_memory_tracker.h"
-
-#include "base/memory/shared_memory.h"
-#include "base/strings/stringprintf.h"
-#include "base/trace_event/memory_dump_manager.h"
-#include "base/trace_event/process_memory_dump.h"
-
-namespace base {
-
-SharedMemoryTracker::Usage::Usage() = default;
-
-SharedMemoryTracker::Usage::Usage(const Usage& rhs) = default;
-
-SharedMemoryTracker::Usage::~Usage() = default;
-
-// static
-SharedMemoryTracker* SharedMemoryTracker::GetInstance() {
- static SharedMemoryTracker* instance = new SharedMemoryTracker;
- return instance;
-}
-
-void SharedMemoryTracker::IncrementMemoryUsage(
- const SharedMemory& shared_memory) {
- Usage usage;
- // |shared_memory|'s unique ID must be generated here and it'd be too late at
- // OnMemoryDump. An ID is generated with a SharedMemoryHandle, but the handle
- // might already be closed at that time. Now IncrementMemoryUsage is called
- // just after mmap and the handle must live then. See the discussion at
- // crbug.com/604726#c30.
- SharedMemory::UniqueId id;
- if (!shared_memory.GetUniqueId(&id))
- return;
- usage.unique_id = id;
- usage.size = shared_memory.mapped_size();
- AutoLock hold(usages_lock_);
- usages_[&shared_memory] = usage;
-}
-
-void SharedMemoryTracker::DecrementMemoryUsage(
- const SharedMemory& shared_memory) {
- AutoLock hold(usages_lock_);
- usages_.erase(&shared_memory);
-}
-
-bool SharedMemoryTracker::OnMemoryDump(const trace_event::MemoryDumpArgs& args,
- trace_event::ProcessMemoryDump* pmd) {
- ALLOW_UNUSED_PARAM(args);
- std::unordered_map<SharedMemory::UniqueId, size_t, SharedMemory::UniqueIdHash>
- sizes;
- {
- AutoLock hold(usages_lock_);
- for (const auto& usage : usages_)
- sizes[usage.second.unique_id] += usage.second.size;
- }
- for (auto& size : sizes) {
- const SharedMemory::UniqueId& id = size.first;
- std::string dump_name = StringPrintf("%s/%lld.%lld", "shared_memory",
- static_cast<long long>(id.first),
- static_cast<long long>(id.second));
- auto guid = trace_event::MemoryAllocatorDumpGuid(dump_name);
- trace_event::MemoryAllocatorDump* local_dump =
- pmd->CreateAllocatorDump(dump_name);
- // TODO(hajimehoshi): The size is not resident size but virtual size so far.
- // Fix this to record resident size.
- local_dump->AddScalar(trace_event::MemoryAllocatorDump::kNameSize,
- trace_event::MemoryAllocatorDump::kUnitsBytes,
- size.second);
- trace_event::MemoryAllocatorDump* global_dump =
- pmd->CreateSharedGlobalAllocatorDump(guid);
- global_dump->AddScalar(trace_event::MemoryAllocatorDump::kNameSize,
- trace_event::MemoryAllocatorDump::kUnitsBytes,
- size.second);
- // TOOD(hajimehoshi): Detect which the shared memory comes from browser,
- // renderer or GPU process.
- // TODO(hajimehoshi): Shared memory reported by GPU and discardable is
- // currently double-counted. Add ownership edges to avoid this.
- pmd->AddOwnershipEdge(local_dump->guid(), global_dump->guid());
- }
- return true;
-}
-
-SharedMemoryTracker::SharedMemoryTracker() {
- trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
- this, "SharedMemoryTracker", nullptr);
-}
-
-SharedMemoryTracker::~SharedMemoryTracker() = default;
-
-} // namespace
diff --git a/base/memory/shared_memory_tracker.h b/base/memory/shared_memory_tracker.h
deleted file mode 100644
index fe1a3dd392..0000000000
--- a/base/memory/shared_memory_tracker.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_MEMORY_SHARED_MEMORY_TRACKER_H_
-#define BASE_MEMORY_SHARED_MEMORY_TRACKER_H_
-
-#include "base/memory/shared_memory.h"
-#include "base/synchronization/lock.h"
-#include "base/trace_event/memory_dump_provider.h"
-
-namespace base {
-
-namespace trace_event {
-class ProcessMemoryDump;
-}
-
-// SharedMemoryTracker tracks shared memory usage.
-class BASE_EXPORT SharedMemoryTracker
- : public base::trace_event::MemoryDumpProvider {
- public:
- // Returns a singleton instance.
- static SharedMemoryTracker* GetInstance();
-
- // Records shared memory usage on mapping.
- void IncrementMemoryUsage(const SharedMemory& shared_memory);
-
- // Records shared memory usage on unmapping.
- void DecrementMemoryUsage(const SharedMemory& shared_memory);
-
- private:
- struct Usage {
- Usage();
- Usage(const Usage& rhs);
- ~Usage();
- SharedMemory::UniqueId unique_id;
- size_t size;
- };
-
- SharedMemoryTracker();
- ~SharedMemoryTracker() override;
-
- // base::trace_event::MemoryDumpProvider implementation.
- bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
- base::trace_event::ProcessMemoryDump* pmd) override;
-
- // Used to lock when |usages_| is modified or read.
- Lock usages_lock_;
- std::unordered_map<const SharedMemory*, Usage> usages_;
-
- DISALLOW_COPY_AND_ASSIGN(SharedMemoryTracker);
-};
-
-} // namespace base
-
-#endif // BASE_MEMORY_SHARED_MEMORY_TRACKER_H_
diff --git a/base/memory/shared_memory_unittest.cc b/base/memory/shared_memory_unittest.cc
index 19dedccb47..f29865c21a 100644
--- a/base/memory/shared_memory_unittest.cc
+++ b/base/memory/shared_memory_unittest.cc
@@ -316,6 +316,8 @@ TEST(SharedMemoryTest, AnonymousPrivate) {
}
}
+// The Mach functionality is tested in shared_memory_mac_unittest.cc.
+#if !(defined(OS_MACOSX) && !defined(OS_IOS))
TEST(SharedMemoryTest, ShareReadOnly) {
StringPiece contents = "Hello World";
@@ -323,10 +325,6 @@ TEST(SharedMemoryTest, ShareReadOnly) {
SharedMemoryCreateOptions options;
options.size = contents.size();
options.share_read_only = true;
-#if defined(OS_MACOSX) && !defined(OS_IOS)
- // The Mach functionality is tested in shared_memory_mac_unittest.cc.
- options.type = SharedMemoryHandle::POSIX;
-#endif
ASSERT_TRUE(writable_shmem.Create(options));
ASSERT_TRUE(writable_shmem.Map(options.size));
memcpy(writable_shmem.memory(), contents.data(), contents.size());
@@ -402,6 +400,7 @@ TEST(SharedMemoryTest, ShareReadOnly) {
#error Unexpected platform; write a test that tries to make 'handle' writable.
#endif // defined(OS_POSIX) || defined(OS_WIN)
}
+#endif // !(defined(OS_MACOSX) && !defined(OS_IOS))
TEST(SharedMemoryTest, ShareToSelf) {
StringPiece contents = "Hello World";
@@ -475,7 +474,7 @@ TEST(SharedMemoryTest, MapTwice) {
EXPECT_EQ(old_address, memory.memory());
}
-#if defined(OS_POSIX)
+#if defined(OS_POSIX) && !(defined(OS_MACOSX) && !defined(OS_IOS))
// This test is not applicable for iOS (crbug.com/399384).
#if !defined(OS_IOS)
// Create a shared memory object, mmap it, and mprotect it to PROT_EXEC.
@@ -486,10 +485,6 @@ TEST(SharedMemoryTest, AnonymousExecutable) {
SharedMemoryCreateOptions options;
options.size = kTestSize;
options.executable = true;
-#if defined(OS_MACOSX) && !defined(OS_IOS)
- // The Mach functionality is tested in shared_memory_mac_unittest.cc.
- options.type = SharedMemoryHandle::POSIX;
-#endif
EXPECT_TRUE(shared_memory.Create(options));
EXPECT_TRUE(shared_memory.Map(shared_memory.requested_size()));
@@ -523,10 +518,6 @@ TEST(SharedMemoryTest, FilePermissionsAnonymous) {
SharedMemory shared_memory;
SharedMemoryCreateOptions options;
options.size = kTestSize;
-#if defined(OS_MACOSX) && !defined(OS_IOS)
- // The Mach functionality is tested in shared_memory_mac_unittest.cc.
- options.type = SharedMemoryHandle::POSIX;
-#endif
// Set a file mode creation mask that gives all permissions.
ScopedUmaskSetter permissive_mask(S_IWGRP | S_IWOTH);
@@ -549,10 +540,6 @@ TEST(SharedMemoryTest, FilePermissionsNamed) {
SharedMemory shared_memory;
SharedMemoryCreateOptions options;
options.size = kTestSize;
-#if defined(OS_MACOSX) && !defined(OS_IOS)
- // The Mach functionality is tested in shared_memory_mac_unittest.cc.
- options.type = SharedMemoryHandle::POSIX;
-#endif
// Set a file mode creation mask that gives all permissions.
ScopedUmaskSetter permissive_mask(S_IWGRP | S_IWOTH);
@@ -569,7 +556,7 @@ TEST(SharedMemoryTest, FilePermissionsNamed) {
}
#endif // !defined(OS_ANDROID)
-#endif // defined(OS_POSIX)
+#endif // defined(OS_POSIX) && !(defined(OS_MACOSX) && !defined(OS_IOS))
// Map() will return addresses which are aligned to the platform page size, this
// varies from platform to platform though. Since we'd like to advertise a
diff --git a/base/memory/singleton.h b/base/memory/singleton.h
index 5c58d5fe29..79e4441a8e 100644
--- a/base/memory/singleton.h
+++ b/base/memory/singleton.h
@@ -22,7 +22,6 @@
#include "base/at_exit.h"
#include "base/atomicops.h"
#include "base/base_export.h"
-#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/aligned_memory.h"
#include "base/threading/thread_restrictions.h"
@@ -64,7 +63,7 @@ struct DefaultSingletonTraits {
// exit. See below for the required call that makes this happen.
static const bool kRegisterAtExit = true;
-#if DCHECK_IS_ON()
+#ifndef NDEBUG
// Set to false to disallow access on a non-joinable thread. This is
// different from kRegisterAtExit because StaticMemorySingletonTraits allows
// access on non-joinable threads, and gracefully handles this.
@@ -79,7 +78,7 @@ struct DefaultSingletonTraits {
template<typename Type>
struct LeakySingletonTraits : public DefaultSingletonTraits<Type> {
static const bool kRegisterAtExit = false;
-#if DCHECK_IS_ON()
+#ifndef NDEBUG
static const bool kAllowedToAccessOnNonjoinableThread = true;
#endif
};
@@ -153,17 +152,14 @@ subtle::Atomic32 StaticMemorySingletonTraits<Type>::dead_ = 0;
// Example usage:
//
// In your header:
-// namespace base {
-// template <typename T>
-// struct DefaultSingletonTraits;
-// }
+// template <typename T> struct DefaultSingletonTraits;
// class FooClass {
// public:
// static FooClass* GetInstance(); <-- See comment below on this.
// void Bar() { ... }
// private:
// FooClass() { ... }
-// friend struct base::DefaultSingletonTraits<FooClass>;
+// friend struct DefaultSingletonTraits<FooClass>;
//
// DISALLOW_COPY_AND_ASSIGN(FooClass);
// };
@@ -171,14 +167,7 @@ subtle::Atomic32 StaticMemorySingletonTraits<Type>::dead_ = 0;
// In your source file:
// #include "base/memory/singleton.h"
// FooClass* FooClass::GetInstance() {
-// return base::Singleton<FooClass>::get();
-// }
-//
-// Or for leaky singletons:
-// #include "base/memory/singleton.h"
-// FooClass* FooClass::GetInstance() {
-// return base::Singleton<
-// FooClass, base::LeakySingletonTraits<FooClass>>::get();
+// return Singleton<FooClass>::get();
// }
//
// And to call methods on FooClass:
@@ -238,7 +227,7 @@ class Singleton {
// Return a pointer to the one true instance of the class.
static Type* get() {
-#if DCHECK_IS_ON()
+#ifndef NDEBUG
// Avoid making TLS lookup on release builds.
if (!Traits::kAllowedToAccessOnNonjoinableThread)
ThreadRestrictions::AssertSingletonAllowed();
diff --git a/base/memory/weak_ptr.cc b/base/memory/weak_ptr.cc
index c179b80097..4e77b04973 100644
--- a/base/memory/weak_ptr.cc
+++ b/base/memory/weak_ptr.cc
@@ -17,13 +17,13 @@ WeakReference::Flag::Flag() : is_valid_(true) {
void WeakReference::Flag::Invalidate() {
// The flag being invalidated with a single ref implies that there are no
// weak pointers in existence. Allow deletion on other thread in this case.
- DCHECK(sequence_checker_.CalledOnValidSequence() || HasOneRef())
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread() || HasOneRef())
<< "WeakPtrs must be invalidated on the same sequenced thread.";
is_valid_ = false;
}
bool WeakReference::Flag::IsValid() const {
- DCHECK(sequence_checker_.CalledOnValidSequence())
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread())
<< "WeakPtrs must be checked on the same sequenced thread.";
return is_valid_;
}
diff --git a/base/memory/weak_ptr_unittest.cc b/base/memory/weak_ptr_unittest.cc
index 1a4870eab1..ebcf33c57e 100644
--- a/base/memory/weak_ptr_unittest.cc
+++ b/base/memory/weak_ptr_unittest.cc
@@ -12,7 +12,6 @@
#include "base/location.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/waitable_event.h"
-#include "base/test/gtest_util.h"
#include "base/threading/thread.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -564,6 +563,8 @@ TEST(WeakPtrTest, NonOwnerThreadCanDeleteWeakPtr) {
background.DeleteArrow(arrow);
}
+#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && GTEST_HAS_DEATH_TEST
+
TEST(WeakPtrDeathTest, WeakPtrCopyDoesNotChangeThreadBinding) {
// The default style "fast" does not support multi-threaded tests
// (introduces deadlock on Linux).
@@ -587,7 +588,7 @@ TEST(WeakPtrDeathTest, WeakPtrCopyDoesNotChangeThreadBinding) {
// Although background thread created the copy, it can not deref the copied
// WeakPtr.
- ASSERT_DCHECK_DEATH(background.DeRef(arrow_copy));
+ ASSERT_DEATH(background.DeRef(arrow_copy), "");
background.DeleteArrow(arrow_copy);
}
@@ -609,7 +610,7 @@ TEST(WeakPtrDeathTest, NonOwnerThreadDereferencesWeakPtrAfterReference) {
// Background thread tries to deref target, which violates thread ownership.
BackgroundThread background;
background.Start();
- ASSERT_DCHECK_DEATH(background.DeRef(&arrow));
+ ASSERT_DEATH(background.DeRef(&arrow), "");
}
TEST(WeakPtrDeathTest, NonOwnerThreadDeletesWeakPtrAfterReference) {
@@ -629,7 +630,7 @@ TEST(WeakPtrDeathTest, NonOwnerThreadDeletesWeakPtrAfterReference) {
background.DeRef(&arrow);
// Main thread deletes Target, violating thread binding.
- ASSERT_DCHECK_DEATH(target.reset());
+ ASSERT_DEATH(target.reset(), "");
// |target.reset()| died so |target| still holds the object, so we
// must pass it to the background thread to teardown.
@@ -652,7 +653,7 @@ TEST(WeakPtrDeathTest, NonOwnerThreadDeletesObjectAfterReference) {
// Background thread tries to delete target, volating thread binding.
BackgroundThread background;
background.Start();
- ASSERT_DCHECK_DEATH(background.DeleteTarget(target.release()));
+ ASSERT_DEATH(background.DeleteTarget(target.release()), "");
}
TEST(WeakPtrDeathTest, NonOwnerThreadReferencesObjectAfterDeletion) {
@@ -672,7 +673,9 @@ TEST(WeakPtrDeathTest, NonOwnerThreadReferencesObjectAfterDeletion) {
background.DeleteTarget(target.release());
// Main thread attempts to dereference the target, violating thread binding.
- ASSERT_DCHECK_DEATH(arrow.target.get());
+ ASSERT_DEATH(arrow.target.get(), "");
}
+#endif
+
} // namespace base
diff --git a/base/message_loop/incoming_task_queue.cc b/base/message_loop/incoming_task_queue.cc
index fed1494c04..bca1d52762 100644
--- a/base/message_loop/incoming_task_queue.cc
+++ b/base/message_loop/incoming_task_queue.cc
@@ -17,7 +17,7 @@ namespace internal {
namespace {
-#if DCHECK_IS_ON()
+#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
// Delays larger than this are often bogus, and a warning should be emitted in
// debug builds to warn developers. http://crbug.com/450045
const int kTaskDelayWarningThresholdInSeconds =
@@ -68,8 +68,8 @@ bool IncomingTaskQueue::AddToIncomingQueue(
<< "Requesting super-long task delay period of " << delay.InSeconds()
<< " seconds from here: " << from_here.ToString();
- PendingTask pending_task(from_here, task, CalculateDelayedRuntime(delay),
- nestable);
+ PendingTask pending_task(
+ from_here, task, CalculateDelayedRuntime(delay), nestable);
#if defined(OS_WIN)
// We consider the task needs a high resolution timer if the delay is
// more than 0 and less than 32ms. This caps the relative error to
diff --git a/base/message_loop/incoming_task_queue.h b/base/message_loop/incoming_task_queue.h
index 157e47fa14..aff71d20bf 100644
--- a/base/message_loop/incoming_task_queue.h
+++ b/base/message_loop/incoming_task_queue.h
@@ -16,6 +16,7 @@
namespace base {
class MessageLoop;
+class WaitableEvent;
namespace internal {
diff --git a/base/message_loop/message_loop.cc b/base/message_loop/message_loop.cc
index eba68a72ba..54369a9b27 100644
--- a/base/message_loop/message_loop.cc
+++ b/base/message_loop/message_loop.cc
@@ -5,19 +5,26 @@
#include "base/message_loop/message_loop.h"
#include <algorithm>
+#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/compiler_specific.h"
+#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_pump_default.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/statistics_recorder.h"
#include "base/run_loop.h"
#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
#include "base/threading/thread_id_name_manager.h"
#include "base/threading/thread_local.h"
#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
+#include "base/tracked_objects.h"
+#include "build/build_config.h"
#if defined(OS_MACOSX)
#include "base/message_loop/message_pump_mac.h"
@@ -37,11 +44,51 @@ namespace base {
namespace {
// A lazily created thread local storage for quick access to a thread's message
-// loop, if one exists.
-base::ThreadLocalPointer<MessageLoop>* GetTLSMessageLoop() {
- static auto* lazy_tls_ptr = new base::ThreadLocalPointer<MessageLoop>();
- return lazy_tls_ptr;
-}
+// loop, if one exists. This should be safe and free of static constructors.
+LazyInstance<base::ThreadLocalPointer<MessageLoop> >::Leaky lazy_tls_ptr =
+ LAZY_INSTANCE_INITIALIZER;
+
+// Logical events for Histogram profiling. Run with --message-loop-histogrammer
+// to get an accounting of messages and actions taken on each thread.
+const int kTaskRunEvent = 0x1;
+#if !defined(OS_NACL)
+const int kTimerEvent = 0x2;
+
+// Provide range of message IDs for use in histogramming and debug display.
+const int kLeastNonZeroMessageId = 1;
+const int kMaxMessageId = 1099;
+const int kNumberOfDistinctMessagesDisplayed = 1100;
+
+// Provide a macro that takes an expression (such as a constant, or macro
+// constant) and creates a pair to initialize an array of pairs. In this case,
+// our pair consists of the expressions value, and the "stringized" version
+// of the expression (i.e., the expression put in quotes). For example, if
+// we have:
+// #define FOO 2
+// #define BAR 5
+// then the following:
+// VALUE_TO_NUMBER_AND_NAME(FOO + BAR)
+// will expand to:
+// {7, "FOO + BAR"}
+// We use the resulting array as an argument to our histogram, which reads the
+// number as a bucket identifier, and proceeds to use the corresponding name
+// in the pair (i.e., the quoted string) when printing out a histogram.
+#define VALUE_TO_NUMBER_AND_NAME(name) {name, #name},
+
+const LinearHistogram::DescriptionPair event_descriptions_[] = {
+ // Provide some pretty print capability in our histogram for our internal
+ // messages.
+
+ // A few events we handle (kindred to messages), and used to profile actions.
+ VALUE_TO_NUMBER_AND_NAME(kTaskRunEvent)
+ VALUE_TO_NUMBER_AND_NAME(kTimerEvent)
+
+ {-1, NULL} // The list must be null-terminated, per API to histogram.
+};
+#endif // !defined(OS_NACL)
+
+bool enable_histogrammer_ = false;
+
MessageLoop::MessagePumpFactory* message_pump_for_ui_factory_ = NULL;
#if defined(OS_IOS)
@@ -124,8 +171,8 @@ MessageLoop::~MessageLoop() {
DCHECK(!did_work);
// Let interested parties have one last shot at accessing this.
- for (auto& observer : destruction_observers_)
- observer.WillDestroyCurrentMessageLoop();
+ FOR_EACH_OBSERVER(DestructionObserver, destruction_observers_,
+ WillDestroyCurrentMessageLoop());
thread_task_runner_handle_.reset();
@@ -137,7 +184,7 @@ MessageLoop::~MessageLoop() {
// OK, now make it so that no one can find us.
if (current() == this)
- GetTLSMessageLoop()->Set(nullptr);
+ lazy_tls_ptr.Pointer()->Set(nullptr);
}
// static
@@ -145,7 +192,12 @@ MessageLoop* MessageLoop::current() {
// TODO(darin): sadly, we cannot enable this yet since people call us even
// when they have no intention of using us.
// DCHECK(loop) << "Ouch, did you forget to initialize me?";
- return GetTLSMessageLoop()->Get();
+ return lazy_tls_ptr.Pointer()->Get();
+}
+
+// static
+void MessageLoop::EnableHistogrammer(bool enable) {
+ enable_histogrammer_ = enable;
}
// static
@@ -162,7 +214,7 @@ std::unique_ptr<MessagePump> MessageLoop::CreateMessagePumpForType(Type type) {
// TODO(rvargas): Get rid of the OS guards.
#if defined(USE_GLIB) && !defined(OS_NACL)
typedef MessagePumpGlib MessagePumpForUI;
-#elif (defined(OS_LINUX) && !defined(OS_NACL)) || defined(OS_BSD)
+#elif defined(OS_LINUX) && !defined(OS_NACL)
typedef MessagePumpLibevent MessagePumpForUI;
#endif
@@ -216,16 +268,39 @@ void MessageLoop::RemoveDestructionObserver(
void MessageLoop::AddNestingObserver(NestingObserver* observer) {
DCHECK_EQ(this, current());
- CHECK(allow_nesting_);
nesting_observers_.AddObserver(observer);
}
void MessageLoop::RemoveNestingObserver(NestingObserver* observer) {
DCHECK_EQ(this, current());
- CHECK(allow_nesting_);
nesting_observers_.RemoveObserver(observer);
}
+void MessageLoop::PostTask(
+ const tracked_objects::Location& from_here,
+ const Closure& task) {
+ task_runner_->PostTask(from_here, task);
+}
+
+void MessageLoop::PostDelayedTask(
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay) {
+ task_runner_->PostDelayedTask(from_here, task, delay);
+}
+
+void MessageLoop::Run() {
+ DCHECK(pump_);
+ RunLoop run_loop;
+ run_loop.Run();
+}
+
+void MessageLoop::RunUntilIdle() {
+ DCHECK(pump_);
+ RunLoop run_loop;
+ run_loop.RunUntilIdle();
+}
+
void MessageLoop::QuitWhenIdle() {
DCHECK_EQ(this, current());
if (run_loop_) {
@@ -259,8 +334,6 @@ Closure MessageLoop::QuitWhenIdleClosure() {
void MessageLoop::SetNestableTasksAllowed(bool allowed) {
if (allowed) {
- CHECK(allow_nesting_);
-
// Kick the native pump just in case we enter a OS-driven nested message
// loop.
pump_->ScheduleWork();
@@ -278,13 +351,11 @@ bool MessageLoop::IsNested() {
void MessageLoop::AddTaskObserver(TaskObserver* task_observer) {
DCHECK_EQ(this, current());
- CHECK(allow_task_observers_);
task_observers_.AddObserver(task_observer);
}
void MessageLoop::RemoveTaskObserver(TaskObserver* task_observer) {
DCHECK_EQ(this, current());
- CHECK(allow_task_observers_);
task_observers_.RemoveObserver(task_observer);
}
@@ -320,8 +391,8 @@ MessageLoop::MessageLoop(Type type, MessagePumpFactoryCallback pump_factory)
#endif
nestable_tasks_allowed_(true),
pump_factory_(pump_factory),
- run_loop_(nullptr),
- current_pending_task_(nullptr),
+ message_histogram_(NULL),
+ run_loop_(NULL),
incoming_task_queue_(new internal::IncomingTaskQueue(this)),
unbound_task_runner_(
new internal::MessageLoopTaskRunner(incoming_task_queue_)),
@@ -339,39 +410,39 @@ void MessageLoop::BindToCurrentThread() {
pump_ = CreateMessagePumpForType(type_);
DCHECK(!current()) << "should only have one message loop per thread";
- GetTLSMessageLoop()->Set(this);
+ lazy_tls_ptr.Pointer()->Set(this);
incoming_task_queue_->StartScheduling();
unbound_task_runner_->BindToCurrentThread();
unbound_task_runner_ = nullptr;
SetThreadTaskRunnerHandle();
- thread_id_ = PlatformThread::CurrentId();
+ {
+ // Save the current thread's ID for potential use by other threads
+ // later from GetThreadName().
+ thread_id_ = PlatformThread::CurrentId();
+ subtle::MemoryBarrier();
+ }
}
std::string MessageLoop::GetThreadName() const {
- DCHECK_NE(kInvalidThreadId, thread_id_)
- << "GetThreadName() must only be called after BindToCurrentThread()'s "
- << "side-effects have been synchronized with this thread.";
+ if (thread_id_ == kInvalidThreadId) {
+ // |thread_id_| may already have been initialized but this thread might not
+ // have received the update yet.
+ subtle::MemoryBarrier();
+ DCHECK_NE(kInvalidThreadId, thread_id_);
+ }
return ThreadIdNameManager::GetInstance()->GetName(thread_id_);
}
void MessageLoop::SetTaskRunner(
scoped_refptr<SingleThreadTaskRunner> task_runner) {
DCHECK_EQ(this, current());
- DCHECK(task_runner);
DCHECK(task_runner->BelongsToCurrentThread());
DCHECK(!unbound_task_runner_);
task_runner_ = std::move(task_runner);
SetThreadTaskRunnerHandle();
}
-void MessageLoop::ClearTaskRunnerForTesting() {
- DCHECK_EQ(this, current());
- DCHECK(!unbound_task_runner_);
- task_runner_ = nullptr;
- thread_task_runner_handle_.reset();
-}
-
void MessageLoop::SetThreadTaskRunnerHandle() {
DCHECK_EQ(this, current());
// Clear the previous thread task runner first, because only one can exist at
@@ -382,8 +453,7 @@ void MessageLoop::SetThreadTaskRunnerHandle() {
void MessageLoop::RunHandler() {
DCHECK_EQ(this, current());
- DCHECK(run_loop_);
- CHECK(allow_nesting_ || run_loop_->run_depth_ == 1);
+ StartHistogrammer();
pump_->Run(this);
}
@@ -398,16 +468,15 @@ bool MessageLoop::ProcessNextDelayedNonNestableTask() {
std::move(deferred_non_nestable_work_queue_.front());
deferred_non_nestable_work_queue_.pop();
- RunTask(&pending_task);
+ RunTask(pending_task);
return true;
}
-void MessageLoop::RunTask(PendingTask* pending_task) {
+void MessageLoop::RunTask(const PendingTask& pending_task) {
DCHECK(nestable_tasks_allowed_);
- current_pending_task_ = pending_task;
#if defined(OS_WIN)
- if (pending_task->is_high_res) {
+ if (pending_task.is_high_res) {
pending_high_res_tasks_--;
CHECK_GE(pending_high_res_tasks_, 0);
}
@@ -416,22 +485,22 @@ void MessageLoop::RunTask(PendingTask* pending_task) {
// Execute the task and assume the worst: It is probably not reentrant.
nestable_tasks_allowed_ = false;
- TRACE_TASK_EXECUTION("MessageLoop::RunTask", *pending_task);
+ HistogramEvent(kTaskRunEvent);
- for (auto& observer : task_observers_)
- observer.WillProcessTask(*pending_task);
+ TRACE_TASK_EXECUTION("MessageLoop::RunTask", pending_task);
+
+ FOR_EACH_OBSERVER(TaskObserver, task_observers_,
+ WillProcessTask(pending_task));
task_annotator_.RunTask("MessageLoop::PostTask", pending_task);
- for (auto& observer : task_observers_)
- observer.DidProcessTask(*pending_task);
+ FOR_EACH_OBSERVER(TaskObserver, task_observers_,
+ DidProcessTask(pending_task));
nestable_tasks_allowed_ = true;
-
- current_pending_task_ = nullptr;
}
bool MessageLoop::DeferOrRunPendingTask(PendingTask pending_task) {
if (pending_task.nestable || run_loop_->run_depth_ == 1) {
- RunTask(&pending_task);
+ RunTask(pending_task);
// Show that we ran a task (Note: a new one might arrive as a
// consequence!).
return true;
@@ -496,9 +565,40 @@ void MessageLoop::ScheduleWork() {
pump_->ScheduleWork();
}
+#if defined(OS_WIN)
+bool MessageLoop::MessagePumpWasSignaled() {
+ return pump_->WasSignaled();
+}
+#endif
+
+//------------------------------------------------------------------------------
+// Method and data for histogramming events and actions taken by each instance
+// on each thread.
+
+void MessageLoop::StartHistogrammer() {
+#if !defined(OS_NACL) // NaCl build has no metrics code.
+ if (enable_histogrammer_ && !message_histogram_
+ && StatisticsRecorder::IsActive()) {
+ std::string thread_name = GetThreadName();
+ DCHECK(!thread_name.empty());
+ message_histogram_ = LinearHistogram::FactoryGetWithRangeDescription(
+ "MsgLoop:" + thread_name, kLeastNonZeroMessageId, kMaxMessageId,
+ kNumberOfDistinctMessagesDisplayed,
+ HistogramBase::kHexRangePrintingFlag, event_descriptions_);
+ }
+#endif
+}
+
+void MessageLoop::HistogramEvent(int event) {
+#if !defined(OS_NACL)
+ if (message_histogram_)
+ message_histogram_->Add(event);
+#endif
+}
+
void MessageLoop::NotifyBeginNestedLoop() {
- for (auto& observer : nesting_observers_)
- observer.OnBeginNestedMessageLoop();
+ FOR_EACH_OBSERVER(NestingObserver, nesting_observers_,
+ OnBeginNestedMessageLoop());
}
bool MessageLoop::DoWork() {
@@ -588,6 +688,19 @@ bool MessageLoop::DoIdleWork() {
return false;
}
+void MessageLoop::DeleteSoonInternal(const tracked_objects::Location& from_here,
+ void(*deleter)(const void*),
+ const void* object) {
+ task_runner()->PostNonNestableTask(from_here, Bind(deleter, object));
+}
+
+void MessageLoop::ReleaseSoonInternal(
+ const tracked_objects::Location& from_here,
+ void(*releaser)(const void*),
+ const void* object) {
+ task_runner()->PostNonNestableTask(from_here, Bind(releaser, object));
+}
+
#if !defined(OS_NACL)
//------------------------------------------------------------------------------
// MessageLoopForUI
@@ -600,18 +713,6 @@ void MessageLoopForUI::Start() {
// No Histogram support for UI message loop as it is managed by Java side
static_cast<MessagePumpForUI*>(pump_.get())->Start(this);
}
-
-void MessageLoopForUI::StartForTesting(
- base::android::JavaMessageHandlerFactory* factory,
- WaitableEvent* test_done_event) {
- // No Histogram support for UI message loop as it is managed by Java side
- static_cast<MessagePumpForUI*>(pump_.get())
- ->StartForUnitTest(this, factory, test_done_event);
-}
-
-void MessageLoopForUI::Abort() {
- static_cast<MessagePumpForUI*>(pump_.get())->Abort();
-}
#endif
#if defined(OS_IOS)
diff --git a/base/message_loop/message_loop.h b/base/message_loop/message_loop.h
index bfef261c38..ac522cf133 100644
--- a/base/message_loop/message_loop.h
+++ b/base/message_loop/message_loop.h
@@ -13,6 +13,7 @@
#include "base/callback_forward.h"
#include "base/debug/task_annotator.h"
#include "base/gtest_prod_util.h"
+#include "base/location.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/incoming_task_queue.h"
@@ -21,8 +22,10 @@
#include "base/message_loop/timer_slack.h"
#include "base/observer_list.h"
#include "base/pending_task.h"
+#include "base/sequenced_task_runner_helpers.h"
#include "base/synchronization/lock.h"
#include "base/time/time.h"
+#include "base/tracking_info.h"
#include "build/build_config.h"
// TODO(sky): these includes should not be necessary. Nuke them.
@@ -34,18 +37,9 @@
#include "base/message_loop/message_pump_libevent.h"
#endif
-#if defined(OS_ANDROID)
-namespace base {
-namespace android {
-
-class JavaMessageHandlerFactory;
-
-} // namespace android
-} // namespace base
-#endif // defined(OS_ANDROID)
-
namespace base {
+class HistogramBase;
class RunLoop;
class ThreadTaskRunnerHandle;
class WaitableEvent;
@@ -53,8 +47,8 @@ class WaitableEvent;
// A MessageLoop is used to process events for a particular thread. There is
// at most one MessageLoop instance per thread.
//
-// Events include at a minimum Task instances submitted to the MessageLoop's
-// TaskRunner. Depending on the type of message pump used by the MessageLoop
+// Events include at a minimum Task instances submitted to PostTask and its
+// variants. Depending on the type of message pump used by the MessageLoop
// other events such as UI messages may be processed. On Windows APC calls (as
// time permits) and signals sent to a registered set of HANDLEs may also be
// processed.
@@ -128,6 +122,8 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
// Returns the MessageLoop object for the current thread, or null if none.
static MessageLoop* current();
+ static void EnableHistogrammer(bool enable_histogrammer);
+
typedef std::unique_ptr<MessagePump>(MessagePumpFactory)();
// Uses the given base::MessagePumpForUIFactory to override the default
// MessagePump implementation for 'TYPE_UI'. Returns true if the factory
@@ -175,6 +171,86 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
void AddNestingObserver(NestingObserver* observer);
void RemoveNestingObserver(NestingObserver* observer);
+ // NOTE: Deprecated; prefer task_runner() and the TaskRunner interfaces.
+ // TODO(skyostil): Remove these functions (crbug.com/465354).
+ //
+ // The "PostTask" family of methods call the task's Run method asynchronously
+ // from within a message loop at some point in the future.
+ //
+ // With the PostTask variant, tasks are invoked in FIFO order, inter-mixed
+ // with normal UI or IO event processing. With the PostDelayedTask variant,
+ // tasks are called after at least approximately 'delay_ms' have elapsed.
+ //
+ // The NonNestable variants work similarly except that they promise never to
+ // dispatch the task from a nested invocation of MessageLoop::Run. Instead,
+ // such tasks get deferred until the top-most MessageLoop::Run is executing.
+ //
+ // The MessageLoop takes ownership of the Task, and deletes it after it has
+ // been Run().
+ //
+ // PostTask(from_here, task) is equivalent to
+ // PostDelayedTask(from_here, task, 0).
+ //
+ // NOTE: These methods may be called on any thread. The Task will be invoked
+ // on the thread that executes MessageLoop::Run().
+ void PostTask(const tracked_objects::Location& from_here,
+ const Closure& task);
+
+ void PostDelayedTask(const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay);
+
+ // A variant on PostTask that deletes the given object. This is useful
+ // if the object needs to live until the next run of the MessageLoop (for
+ // example, deleting a RenderProcessHost from within an IPC callback is not
+ // good).
+ //
+ // NOTE: This method may be called on any thread. The object will be deleted
+ // on the thread that executes MessageLoop::Run().
+ template <class T>
+ void DeleteSoon(const tracked_objects::Location& from_here, const T* object) {
+ base::subtle::DeleteHelperInternal<T, void>::DeleteViaSequencedTaskRunner(
+ this, from_here, object);
+ }
+
+ // A variant on PostTask that releases the given reference counted object
+ // (by calling its Release method). This is useful if the object needs to
+ // live until the next run of the MessageLoop, or if the object needs to be
+ // released on a particular thread.
+ //
+ // A common pattern is to manually increment the object's reference count
+ // (AddRef), clear the pointer, then issue a ReleaseSoon. The reference count
+ // is incremented manually to ensure clearing the pointer does not trigger a
+ // delete and to account for the upcoming decrement (ReleaseSoon). For
+ // example:
+ //
+ // scoped_refptr<Foo> foo = ...
+ // foo->AddRef();
+ // Foo* raw_foo = foo.get();
+ // foo = NULL;
+ // message_loop->ReleaseSoon(raw_foo);
+ //
+ // NOTE: This method may be called on any thread. The object will be
+ // released (and thus possibly deleted) on the thread that executes
+ // MessageLoop::Run(). If this is not the same as the thread that calls
+ // ReleaseSoon(FROM_HERE, ), then T MUST inherit from
+ // RefCountedThreadSafe<T>!
+ template <class T>
+ void ReleaseSoon(const tracked_objects::Location& from_here,
+ const T* object) {
+ base::subtle::ReleaseHelperInternal<T, void>::ReleaseViaSequencedTaskRunner(
+ this, from_here, object);
+ }
+
+ // Deprecated: use RunLoop instead.
+ // Run the message loop.
+ void Run();
+
+ // Deprecated: use RunLoop instead.
+ // Process all pending tasks, windows messages, etc., but don't wait/sleep.
+ // Return as soon as all items that can be run are taken care of.
+ void RunUntilIdle();
+
// Deprecated: use RunLoop instead.
//
// Signals the Run method to return when it becomes idle. It will continue to
@@ -215,11 +291,9 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
// Returns the type passed to the constructor.
Type type() const { return type_; }
- // Returns the name of the thread this message loop is bound to. This function
- // is only valid when this message loop is running, BindToCurrentThread has
- // already been called and has an "happens-before" relationship with this call
- // (this relationship is obtained implicitly by the MessageLoop's task posting
- // system unless calling this very early).
+ // Returns the name of the thread this message loop is bound to.
+ // This function is only valid when this message loop is running and
+ // BindToCurrentThread has already been called.
std::string GetThreadName() const;
// Gets the TaskRunner associated with this message loop.
@@ -234,10 +308,6 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
// thread to which the message loop is bound.
void SetTaskRunner(scoped_refptr<SingleThreadTaskRunner> task_runner);
- // Clears task_runner() and the ThreadTaskRunnerHandle for the target thread.
- // Must be called on the thread to which the message loop is bound.
- void ClearTaskRunnerForTesting();
-
// Enables or disables the recursive task processing. This happens in the case
// of recursive message loops. Some unwanted message loops may occur when
// using common controls or printer functions. By default, recursive task
@@ -318,15 +388,16 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
debug::TaskAnnotator* task_annotator() { return &task_annotator_; }
// Runs the specified PendingTask.
- void RunTask(PendingTask* pending_task);
+ void RunTask(const PendingTask& pending_task);
- // Disallow nesting. After this is called, running a nested RunLoop or calling
- // Add/RemoveNestingObserver() on this MessageLoop will crash.
- void DisallowNesting() { allow_nesting_ = false; }
-
- // Disallow task observers. After this is called, calling
- // Add/RemoveTaskObserver() on this MessageLoop will crash.
- void DisallowTaskObservers() { allow_task_observers_ = false; }
+#if defined(OS_WIN)
+ // TODO (stanisc): crbug.com/596190: Remove this after the signaling issue
+ // has been investigated.
+ // This should be used for diagnostic only. If message pump wake-up mechanism
+ // is based on auto-reset event this call would reset the event to unset
+ // state.
+ bool MessagePumpWasSignaled();
+#endif
//----------------------------------------------------------------------------
protected:
@@ -346,13 +417,11 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
void BindToCurrentThread();
private:
- friend class internal::IncomingTaskQueue;
friend class RunLoop;
+ friend class internal::IncomingTaskQueue;
friend class ScheduleWorkTest;
friend class Thread;
- friend struct PendingTask;
FRIEND_TEST_ALL_PREFIXES(MessageLoopTest, DeleteUnboundLoop);
- friend class PendingTaskTest;
// Creates a MessageLoop without binding to a thread.
// If |type| is TYPE_CUSTOM non-null |pump_factory| must be also given
@@ -399,6 +468,15 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
// responsible for synchronizing ScheduleWork() calls.
void ScheduleWork();
+ // Start recording histogram info about events and action IF it was enabled
+ // and IF the statistics recorder can accept a registration of our histogram.
+ void StartHistogrammer();
+
+ // Add occurrence of event to our histogram, so that we can see what is being
+ // done in a specific MessageLoop instance (i.e., specific thread).
+ // If message_histogram_ is NULL, this is a no-op.
+ void HistogramEvent(int event);
+
// Notify observers that a nested message loop is starting.
void NotifyBeginNestedLoop();
@@ -446,19 +524,15 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
// if type_ is TYPE_CUSTOM and pump_ is null.
MessagePumpFactoryCallback pump_factory_;
+ // A profiling histogram showing the counts of various messages and events.
+ HistogramBase* message_histogram_;
+
RunLoop* run_loop_;
ObserverList<TaskObserver> task_observers_;
debug::TaskAnnotator task_annotator_;
- // Used to allow creating a breadcrumb of program counters in PostTask.
- // This variable is only initialized while a task is being executed and is
- // meant only to store context for creating a backtrace breadcrumb. Do not
- // attach other semantics to it without thinking through the use caes
- // thoroughly.
- const PendingTask* current_pending_task_;
-
scoped_refptr<internal::IncomingTaskQueue> incoming_task_queue_;
// A task runner which we haven't bound to a thread yet.
@@ -468,15 +542,18 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
scoped_refptr<SingleThreadTaskRunner> task_runner_;
std::unique_ptr<ThreadTaskRunnerHandle> thread_task_runner_handle_;
- // Id of the thread this message loop is bound to. Initialized once when the
- // MessageLoop is bound to its thread and constant forever after.
+ // Id of the thread this message loop is bound to.
PlatformThreadId thread_id_;
- // Whether nesting is allowed.
- bool allow_nesting_ = true;
+ template <class T, class R> friend class base::subtle::DeleteHelperInternal;
+ template <class T, class R> friend class base::subtle::ReleaseHelperInternal;
- // Whether task observers are allowed.
- bool allow_task_observers_ = true;
+ void DeleteSoonInternal(const tracked_objects::Location& from_here,
+ void(*deleter)(const void*),
+ const void* object);
+ void ReleaseSoonInternal(const tracked_objects::Location& from_here,
+ void(*releaser)(const void*),
+ const void* object);
DISALLOW_COPY_AND_ASSIGN(MessageLoop);
};
@@ -522,11 +599,6 @@ class BASE_EXPORT MessageLoopForUI : public MessageLoop {
// never be called. Instead use Start(), which will forward all the native UI
// events to the Java message loop.
void Start();
- void StartForTesting(base::android::JavaMessageHandlerFactory* factory,
- WaitableEvent* test_done_event);
- // In Android there are cases where we want to abort immediately without
- // calling Quit(), in these cases we call Abort().
- void Abort();
#endif
#if defined(USE_OZONE) || (defined(USE_X11) && !defined(USE_GLIB))
diff --git a/base/message_loop/message_loop_task_runner_unittest.cc b/base/message_loop/message_loop_task_runner_unittest.cc
index 54551daadd..cabd25013b 100644
--- a/base/message_loop/message_loop_task_runner_unittest.cc
+++ b/base/message_loop/message_loop_task_runner_unittest.cc
@@ -12,6 +12,7 @@
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_task_runner.h"
#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "base/threading/thread_task_runner_handle.h"
@@ -37,7 +38,7 @@ class MessageLoopTaskRunnerTest : public testing::Test {
task_thread_.Start();
// Allow us to pause the |task_thread_|'s MessageLoop.
- task_thread_.task_runner()->PostTask(
+ task_thread_.message_loop()->task_runner()->PostTask(
FROM_HERE, Bind(&MessageLoopTaskRunnerTest::BlockTaskThreadHelper,
Unretained(this)));
}
@@ -108,23 +109,23 @@ TEST_F(MessageLoopTaskRunnerTest, PostTaskAndReply_Basic) {
MessageLoop* reply_deleted_on = NULL;
int reply_delete_order = -1;
- scoped_refptr<LoopRecorder> task_recorder =
+ scoped_refptr<LoopRecorder> task_recoder =
new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
- scoped_refptr<LoopRecorder> reply_recorder =
+ scoped_refptr<LoopRecorder> reply_recoder =
new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
ASSERT_TRUE(task_thread_.task_runner()->PostTaskAndReply(
- FROM_HERE, Bind(&RecordLoop, task_recorder),
- Bind(&RecordLoopAndQuit, reply_recorder)));
+ FROM_HERE, Bind(&RecordLoop, task_recoder),
+ Bind(&RecordLoopAndQuit, reply_recoder)));
// Die if base::Bind doesn't retain a reference to the recorders.
- task_recorder = NULL;
- reply_recorder = NULL;
+ task_recoder = NULL;
+ reply_recoder = NULL;
ASSERT_FALSE(task_deleted_on);
ASSERT_FALSE(reply_deleted_on);
UnblockTaskThread();
- RunLoop().Run();
+ current_loop_->Run();
EXPECT_EQ(task_thread_.message_loop(), task_run_on);
EXPECT_EQ(current_loop_.get(), task_deleted_on);
@@ -141,9 +142,9 @@ TEST_F(MessageLoopTaskRunnerTest, PostTaskAndReplyOnDeletedThreadDoesNotLeak) {
MessageLoop* reply_deleted_on = NULL;
int reply_delete_order = -1;
- scoped_refptr<LoopRecorder> task_recorder =
+ scoped_refptr<LoopRecorder> task_recoder =
new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
- scoped_refptr<LoopRecorder> reply_recorder =
+ scoped_refptr<LoopRecorder> reply_recoder =
new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
// Grab a task runner to a dead MessageLoop.
@@ -153,14 +154,14 @@ TEST_F(MessageLoopTaskRunnerTest, PostTaskAndReplyOnDeletedThreadDoesNotLeak) {
task_thread_.Stop();
ASSERT_FALSE(
- task_runner->PostTaskAndReply(FROM_HERE, Bind(&RecordLoop, task_recorder),
- Bind(&RecordLoopAndQuit, reply_recorder)));
+ task_runner->PostTaskAndReply(FROM_HERE, Bind(&RecordLoop, task_recoder),
+ Bind(&RecordLoopAndQuit, reply_recoder)));
// The relay should have properly deleted its resources leaving us as the only
// reference.
EXPECT_EQ(task_delete_order, reply_delete_order);
- ASSERT_TRUE(task_recorder->HasOneRef());
- ASSERT_TRUE(reply_recorder->HasOneRef());
+ ASSERT_TRUE(task_recoder->HasOneRef());
+ ASSERT_TRUE(reply_recoder->HasOneRef());
// Nothing should have run though.
EXPECT_FALSE(task_run_on);
@@ -175,23 +176,23 @@ TEST_F(MessageLoopTaskRunnerTest, PostTaskAndReply_SameLoop) {
MessageLoop* reply_deleted_on = NULL;
int reply_delete_order = -1;
- scoped_refptr<LoopRecorder> task_recorder =
+ scoped_refptr<LoopRecorder> task_recoder =
new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
- scoped_refptr<LoopRecorder> reply_recorder =
+ scoped_refptr<LoopRecorder> reply_recoder =
new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
// Enqueue the relay.
ASSERT_TRUE(current_loop_->task_runner()->PostTaskAndReply(
- FROM_HERE, Bind(&RecordLoop, task_recorder),
- Bind(&RecordLoopAndQuit, reply_recorder)));
+ FROM_HERE, Bind(&RecordLoop, task_recoder),
+ Bind(&RecordLoopAndQuit, reply_recoder)));
// Die if base::Bind doesn't retain a reference to the recorders.
- task_recorder = NULL;
- reply_recorder = NULL;
+ task_recoder = NULL;
+ reply_recoder = NULL;
ASSERT_FALSE(task_deleted_on);
ASSERT_FALSE(reply_deleted_on);
- RunLoop().Run();
+ current_loop_->Run();
EXPECT_EQ(current_loop_.get(), task_run_on);
EXPECT_EQ(current_loop_.get(), task_deleted_on);
@@ -210,19 +211,19 @@ TEST_F(MessageLoopTaskRunnerTest, PostTaskAndReply_DeadReplyLoopDoesNotDelete) {
MessageLoop* reply_deleted_on = NULL;
int reply_delete_order = -1;
- scoped_refptr<LoopRecorder> task_recorder =
+ scoped_refptr<LoopRecorder> task_recoder =
new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
- scoped_refptr<LoopRecorder> reply_recorder =
+ scoped_refptr<LoopRecorder> reply_recoder =
new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
// Enqueue the relay.
task_thread_.task_runner()->PostTaskAndReply(
- FROM_HERE, Bind(&RecordLoop, task_recorder),
- Bind(&RecordLoopAndQuit, reply_recorder));
+ FROM_HERE, Bind(&RecordLoop, task_recoder),
+ Bind(&RecordLoopAndQuit, reply_recoder));
// Die if base::Bind doesn't retain a reference to the recorders.
- task_recorder = NULL;
- reply_recorder = NULL;
+ task_recoder = NULL;
+ reply_recoder = NULL;
ASSERT_FALSE(task_deleted_on);
ASSERT_FALSE(reply_deleted_on);
diff --git a/base/message_loop/message_loop_test.cc b/base/message_loop/message_loop_test.cc
index 6ffb16d05a..1ab946f9e2 100644
--- a/base/message_loop/message_loop_test.cc
+++ b/base/message_loop/message_loop_test.cc
@@ -15,7 +15,6 @@
#include "base/single_thread_task_runner.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
-#include "base/threading/thread_task_runner_handle.h"
namespace base {
namespace test {
@@ -98,19 +97,20 @@ void RunTest_PostTask(MessagePumpFactory factory) {
// Add tests to message loop
scoped_refptr<Foo> foo(new Foo());
std::string a("a"), b("b"), c("c"), d("d");
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, Bind(&Foo::Test0, foo));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&Foo::Test1ConstRef, foo, a));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&Foo::Test1Ptr, foo, &b));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&Foo::Test1Int, foo, 100));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&Foo::Test2Ptr, foo, &a, &c));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&Foo::Test2Mixed, foo, a, &d));
+ MessageLoop::current()->task_runner()->PostTask(FROM_HERE,
+ Bind(&Foo::Test0, foo.get()));
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE, Bind(&Foo::Test1ConstRef, foo.get(), a));
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE, Bind(&Foo::Test1Ptr, foo.get(), &b));
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE, Bind(&Foo::Test1Int, foo.get(), 100));
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE, Bind(&Foo::Test2Ptr, foo.get(), &a, &c));
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE, Bind(&Foo::Test2Mixed, foo.get(), a, &d));
// After all tests, post a message that will shut down the message loop
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->task_runner()->PostTask(
FROM_HERE,
Bind(&MessageLoop::QuitWhenIdle, Unretained(MessageLoop::current())));
@@ -302,8 +302,8 @@ class RecordDeletionProbe : public RefCounted<RecordDeletionProbe> {
~RecordDeletionProbe() {
*was_deleted_ = true;
if (post_on_delete_.get())
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, Bind(&RecordDeletionProbe::Run, post_on_delete_));
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE, Bind(&RecordDeletionProbe::Run, post_on_delete_.get()));
}
scoped_refptr<RecordDeletionProbe> post_on_delete_;
@@ -351,8 +351,8 @@ void RunTest_EnsureDeletion_Chain(MessagePumpFactory factory) {
void NestingFunc(int* depth) {
if (*depth > 0) {
*depth -= 1;
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&NestingFunc, depth));
+ MessageLoop::current()->task_runner()->PostTask(FROM_HERE,
+ Bind(&NestingFunc, depth));
MessageLoop::current()->SetNestableTasksAllowed(true);
RunLoop().Run();
@@ -365,8 +365,8 @@ void RunTest_Nesting(MessagePumpFactory factory) {
MessageLoop loop(std::move(pump));
int depth = 100;
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&NestingFunc, &depth));
+ MessageLoop::current()->task_runner()->PostTask(FROM_HERE,
+ Bind(&NestingFunc, &depth));
RunLoop().Run();
EXPECT_EQ(depth, 0);
}
@@ -403,9 +403,10 @@ void RunNestedLoop(TestNestingObserver* observer,
RunLoop nested_loop;
// Verify that by the time the first task is run the observer has seen the
// message loop begin.
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->task_runner()->PostTask(
FROM_HERE, Bind(&ExpectOneBeginNestedLoop, observer));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, nested_loop.QuitClosure());
+ MessageLoop::current()->task_runner()->PostTask(FROM_HERE,
+ nested_loop.QuitClosure());
nested_loop.Run();
// Quitting message loops doesn't change the begin count.
@@ -517,7 +518,7 @@ void RecursiveFunc(TaskList* order, int cookie, int depth,
if (depth > 0) {
if (is_reentrant)
MessageLoop::current()->SetNestableTasksAllowed(true);
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->task_runner()->PostTask(
FROM_HERE,
Bind(&RecursiveFunc, order, cookie, depth - 1, is_reentrant));
}
@@ -535,12 +536,12 @@ void RunTest_RecursiveDenial1(MessagePumpFactory factory) {
EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed());
TaskList order;
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->task_runner()->PostTask(
FROM_HERE, Bind(&RecursiveFunc, &order, 1, 2, false));
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->task_runner()->PostTask(
FROM_HERE, Bind(&RecursiveFunc, &order, 2, 2, false));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&QuitFunc, &order, 3));
+ MessageLoop::current()->task_runner()->PostTask(FROM_HERE,
+ Bind(&QuitFunc, &order, 3));
RunLoop().Run();
@@ -579,13 +580,13 @@ void RunTest_RecursiveDenial3(MessagePumpFactory factory) {
EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed());
TaskList order;
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->task_runner()->PostTask(
FROM_HERE, Bind(&RecursiveSlowFunc, &order, 1, 2, false));
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->task_runner()->PostTask(
FROM_HERE, Bind(&RecursiveSlowFunc, &order, 2, 2, false));
- ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ MessageLoop::current()->task_runner()->PostDelayedTask(
FROM_HERE, Bind(&OrderedFunc, &order, 3), TimeDelta::FromMilliseconds(5));
- ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ MessageLoop::current()->task_runner()->PostDelayedTask(
FROM_HERE, Bind(&QuitFunc, &order, 4), TimeDelta::FromMilliseconds(5));
RunLoop().Run();
@@ -615,12 +616,12 @@ void RunTest_RecursiveSupport1(MessagePumpFactory factory) {
MessageLoop loop(std::move(pump));
TaskList order;
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->task_runner()->PostTask(
FROM_HERE, Bind(&RecursiveFunc, &order, 1, 2, true));
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->task_runner()->PostTask(
FROM_HERE, Bind(&RecursiveFunc, &order, 2, 2, true));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&QuitFunc, &order, 3));
+ MessageLoop::current()->task_runner()->PostTask(FROM_HERE,
+ Bind(&QuitFunc, &order, 3));
RunLoop().Run();
@@ -649,12 +650,14 @@ void RunTest_NonNestableWithNoNesting(MessagePumpFactory factory) {
TaskList order;
- ThreadTaskRunnerHandle::Get()->PostNonNestableTask(
- FROM_HERE, Bind(&OrderedFunc, &order, 1));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&OrderedFunc, &order, 2));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&QuitFunc, &order, 3));
+ MessageLoop::current()->task_runner()->PostNonNestableTask(
+ FROM_HERE,
+ Bind(&OrderedFunc, &order, 1));
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE,
+ Bind(&OrderedFunc, &order, 2));
+ MessageLoop::current()->task_runner()->PostTask(FROM_HERE,
+ Bind(&QuitFunc, &order, 3));
RunLoop().Run();
// FIFO order.
@@ -689,18 +692,24 @@ void RunTest_NonNestableInNestedLoop(MessagePumpFactory factory) {
TaskList order;
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&FuncThatPumps, &order, 1));
- ThreadTaskRunnerHandle::Get()->PostNonNestableTask(
- FROM_HERE, Bind(&OrderedFunc, &order, 2));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&OrderedFunc, &order, 3));
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, Bind(&SleepFunc, &order, 4, TimeDelta::FromMilliseconds(50)));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&OrderedFunc, &order, 5));
- ThreadTaskRunnerHandle::Get()->PostNonNestableTask(
- FROM_HERE, Bind(&QuitFunc, &order, 6));
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE,
+ Bind(&FuncThatPumps, &order, 1));
+ MessageLoop::current()->task_runner()->PostNonNestableTask(
+ FROM_HERE,
+ Bind(&OrderedFunc, &order, 2));
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE,
+ Bind(&OrderedFunc, &order, 3));
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE,
+ Bind(&SleepFunc, &order, 4, TimeDelta::FromMilliseconds(50)));
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE,
+ Bind(&OrderedFunc, &order, 5));
+ MessageLoop::current()->task_runner()->PostNonNestableTask(
+ FROM_HERE,
+ Bind(&QuitFunc, &order, 6));
RunLoop().Run();
@@ -741,15 +750,17 @@ void RunTest_QuitNow(MessagePumpFactory factory) {
RunLoop run_loop;
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->task_runner()->PostTask(
FROM_HERE, Bind(&FuncThatRuns, &order, 1, Unretained(&run_loop)));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&OrderedFunc, &order, 2));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, Bind(&FuncThatQuitsNow));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&OrderedFunc, &order, 3));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, Bind(&FuncThatQuitsNow));
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 2));
+ MessageLoop::current()->task_runner()->PostTask(FROM_HERE,
+ Bind(&FuncThatQuitsNow));
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 3));
+ MessageLoop::current()->task_runner()->PostTask(FROM_HERE,
+ Bind(&FuncThatQuitsNow));
+ MessageLoop::current()->task_runner()->PostTask(
FROM_HERE, Bind(&OrderedFunc, &order, 4)); // never runs
RunLoop().Run();
@@ -775,14 +786,14 @@ void RunTest_RunLoopQuitTop(MessagePumpFactory factory) {
RunLoop outer_run_loop;
RunLoop nested_run_loop;
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->task_runner()->PostTask(
FROM_HERE, Bind(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop)));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- outer_run_loop.QuitClosure());
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&OrderedFunc, &order, 2));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- nested_run_loop.QuitClosure());
+ MessageLoop::current()->task_runner()->PostTask(FROM_HERE,
+ outer_run_loop.QuitClosure());
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 2));
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE, nested_run_loop.QuitClosure());
outer_run_loop.Run();
@@ -805,14 +816,14 @@ void RunTest_RunLoopQuitNested(MessagePumpFactory factory) {
RunLoop outer_run_loop;
RunLoop nested_run_loop;
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->task_runner()->PostTask(
FROM_HERE, Bind(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop)));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- nested_run_loop.QuitClosure());
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&OrderedFunc, &order, 2));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- outer_run_loop.QuitClosure());
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE, nested_run_loop.QuitClosure());
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 2));
+ MessageLoop::current()->task_runner()->PostTask(FROM_HERE,
+ outer_run_loop.QuitClosure());
outer_run_loop.Run();
@@ -836,16 +847,16 @@ void RunTest_RunLoopQuitBogus(MessagePumpFactory factory) {
RunLoop nested_run_loop;
RunLoop bogus_run_loop;
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->task_runner()->PostTask(
FROM_HERE, Bind(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop)));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- bogus_run_loop.QuitClosure());
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&OrderedFunc, &order, 2));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- outer_run_loop.QuitClosure());
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- nested_run_loop.QuitClosure());
+ MessageLoop::current()->task_runner()->PostTask(FROM_HERE,
+ bogus_run_loop.QuitClosure());
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 2));
+ MessageLoop::current()->task_runner()->PostTask(FROM_HERE,
+ outer_run_loop.QuitClosure());
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE, nested_run_loop.QuitClosure());
outer_run_loop.Run();
@@ -871,36 +882,36 @@ void RunTest_RunLoopQuitDeep(MessagePumpFactory factory) {
RunLoop nested_loop3;
RunLoop nested_loop4;
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->task_runner()->PostTask(
FROM_HERE, Bind(&FuncThatRuns, &order, 1, Unretained(&nested_loop1)));
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->task_runner()->PostTask(
FROM_HERE, Bind(&FuncThatRuns, &order, 2, Unretained(&nested_loop2)));
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->task_runner()->PostTask(
FROM_HERE, Bind(&FuncThatRuns, &order, 3, Unretained(&nested_loop3)));
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->task_runner()->PostTask(
FROM_HERE, Bind(&FuncThatRuns, &order, 4, Unretained(&nested_loop4)));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&OrderedFunc, &order, 5));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- outer_run_loop.QuitClosure());
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&OrderedFunc, &order, 6));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- nested_loop1.QuitClosure());
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&OrderedFunc, &order, 7));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- nested_loop2.QuitClosure());
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&OrderedFunc, &order, 8));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- nested_loop3.QuitClosure());
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&OrderedFunc, &order, 9));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- nested_loop4.QuitClosure());
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&OrderedFunc, &order, 10));
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 5));
+ MessageLoop::current()->task_runner()->PostTask(FROM_HERE,
+ outer_run_loop.QuitClosure());
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 6));
+ MessageLoop::current()->task_runner()->PostTask(FROM_HERE,
+ nested_loop1.QuitClosure());
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 7));
+ MessageLoop::current()->task_runner()->PostTask(FROM_HERE,
+ nested_loop2.QuitClosure());
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 8));
+ MessageLoop::current()->task_runner()->PostTask(FROM_HERE,
+ nested_loop3.QuitClosure());
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 9));
+ MessageLoop::current()->task_runner()->PostTask(FROM_HERE,
+ nested_loop4.QuitClosure());
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 10));
outer_run_loop.Run();
@@ -938,9 +949,9 @@ void RunTest_RunLoopQuitOrderBefore(MessagePumpFactory factory) {
run_loop.Quit();
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->task_runner()->PostTask(
FROM_HERE, Bind(&OrderedFunc, &order, 1)); // never runs
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->task_runner()->PostTask(
FROM_HERE, Bind(&FuncThatQuitsNow)); // never runs
run_loop.Run();
@@ -957,12 +968,13 @@ void RunTest_RunLoopQuitOrderDuring(MessagePumpFactory factory) {
RunLoop run_loop;
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&OrderedFunc, &order, 1));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_loop.QuitClosure());
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 1));
+ MessageLoop::current()->task_runner()->PostTask(FROM_HERE,
+ run_loop.QuitClosure());
+ MessageLoop::current()->task_runner()->PostTask(
FROM_HERE, Bind(&OrderedFunc, &order, 2)); // never runs
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->task_runner()->PostTask(
FROM_HERE, Bind(&FuncThatQuitsNow)); // never runs
run_loop.Run();
@@ -983,18 +995,20 @@ void RunTest_RunLoopQuitOrderAfter(MessagePumpFactory factory) {
RunLoop run_loop;
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->task_runner()->PostTask(
FROM_HERE, Bind(&FuncThatRuns, &order, 1, Unretained(&run_loop)));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&OrderedFunc, &order, 2));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, Bind(&FuncThatQuitsNow));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&OrderedFunc, &order, 3));
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 2));
+ MessageLoop::current()->task_runner()->PostTask(FROM_HERE,
+ Bind(&FuncThatQuitsNow));
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 3));
+ MessageLoop::current()->task_runner()->PostTask(
FROM_HERE, run_loop.QuitClosure()); // has no affect
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&OrderedFunc, &order, 4));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, Bind(&FuncThatQuitsNow));
+ MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 4));
+ MessageLoop::current()->task_runner()->PostTask(FROM_HERE,
+ Bind(&FuncThatQuitsNow));
RunLoop outer_run_loop;
outer_run_loop.Run();
@@ -1014,7 +1028,7 @@ void RunTest_RunLoopQuitOrderAfter(MessagePumpFactory factory) {
void PostNTasksThenQuit(int posts_remaining) {
if (posts_remaining > 1) {
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->task_runner()->PostTask(
FROM_HERE, Bind(&PostNTasksThenQuit, posts_remaining - 1));
} else {
MessageLoop::current()->QuitWhenIdle();
diff --git a/base/message_loop/message_loop_unittest.cc b/base/message_loop/message_loop_unittest.cc
index 14fe1ee391..52337e31a8 100644
--- a/base/message_loop/message_loop_unittest.cc
+++ b/base/message_loop/message_loop_unittest.cc
@@ -27,11 +27,6 @@
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
-#if defined(OS_ANDROID)
-#include "base/android/jni_android.h"
-#include "base/test/android/java_handler_thread_for_testing.h"
-#endif
-
#if defined(OS_WIN)
#include "base/message_loop/message_pump_win.h"
#include "base/process/memory.h"
@@ -81,53 +76,6 @@ class Foo : public RefCounted<Foo> {
std::string result_;
};
-#if defined(OS_ANDROID)
-void AbortMessagePump() {
- JNIEnv* env = base::android::AttachCurrentThread();
- jclass exception = env->FindClass(
- "org/chromium/base/TestSystemMessageHandler$TestException");
-
- env->ThrowNew(exception,
- "This is a test exception that should be caught in "
- "TestSystemMessageHandler.handleMessage");
- static_cast<base::MessageLoopForUI*>(base::MessageLoop::current())->Abort();
-}
-
-void RunTest_AbortDontRunMoreTasks(bool delayed) {
- MessageLoop loop(MessageLoop::TYPE_JAVA);
-
- WaitableEvent test_done_event(WaitableEvent::ResetPolicy::MANUAL,
- WaitableEvent::InitialState::NOT_SIGNALED);
-
- std::unique_ptr<android::JavaHandlerThreadForTesting> java_thread;
- java_thread.reset(new android::JavaHandlerThreadForTesting(
- "JavaHandlerThreadForTesting from AbortDontRunMoreTasks",
- &test_done_event));
- java_thread->Start();
-
- if (delayed) {
- java_thread->message_loop()->task_runner()->PostDelayedTask(
- FROM_HERE, Bind(&AbortMessagePump), TimeDelta::FromMilliseconds(10));
- } else {
- java_thread->message_loop()->task_runner()->PostTask(
- FROM_HERE, Bind(&AbortMessagePump));
- }
-
- // Wait to ensure we catch the correct exception (and don't crash)
- test_done_event.Wait();
-
- java_thread->Stop();
- java_thread.reset();
-}
-
-TEST(MessageLoopTest, JavaExceptionAbort) {
- RunTest_AbortDontRunMoreTasks(false);
-}
-TEST(MessageLoopTest, DelayedJavaExceptionAbort) {
- RunTest_AbortDontRunMoreTasks(true);
-}
-#endif // defined(OS_ANDROID)
-
#if defined(OS_WIN)
// This function runs slowly to simulate a large amount of work being done.
@@ -159,7 +107,7 @@ void SubPumpFunc() {
}
void RunTest_PostDelayedTask_SharedTimer_SubPump() {
- MessageLoop message_loop(MessageLoop::TYPE_UI);
+ MessageLoop loop(MessageLoop::TYPE_UI);
// Test that the interval of the timer, used to run the next delayed task, is
// set to a value corresponding to when the next delayed task should run.
@@ -169,20 +117,23 @@ void RunTest_PostDelayedTask_SharedTimer_SubPump() {
int num_tasks = 1;
Time run_time;
- message_loop.task_runner()->PostTask(FROM_HERE, Bind(&SubPumpFunc));
+ loop.PostTask(FROM_HERE, Bind(&SubPumpFunc));
// This very delayed task should never run.
- message_loop.task_runner()->PostDelayedTask(
- FROM_HERE, Bind(&RecordRunTimeFunc, &run_time, &num_tasks),
+ loop.PostDelayedTask(
+ FROM_HERE,
+ Bind(&RecordRunTimeFunc, &run_time, &num_tasks),
TimeDelta::FromSeconds(1000));
// This slightly delayed task should run from within SubPumpFunc.
- message_loop.task_runner()->PostDelayedTask(
- FROM_HERE, Bind(&PostQuitMessage, 0), TimeDelta::FromMilliseconds(10));
+ loop.PostDelayedTask(
+ FROM_HERE,
+ Bind(&PostQuitMessage, 0),
+ TimeDelta::FromMilliseconds(10));
Time start_time = Time::Now();
- RunLoop().Run();
+ loop.Run();
EXPECT_EQ(1, num_tasks);
// Ensure that we ran in far less time than the slower timer.
@@ -309,7 +260,7 @@ void RecursiveFunc(TaskList* order, int cookie, int depth,
if (depth > 0) {
if (is_reentrant)
MessageLoop::current()->SetNestableTasksAllowed(true);
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->PostTask(
FROM_HERE,
Bind(&RecursiveFunc, order, cookie, depth - 1, is_reentrant));
}
@@ -322,25 +273,27 @@ void QuitFunc(TaskList* order, int cookie) {
order->RecordEnd(QUITMESSAGELOOP, cookie);
}
-void RecursiveFuncWin(scoped_refptr<SingleThreadTaskRunner> task_runner,
+void RecursiveFuncWin(MessageLoop* target,
HANDLE event,
bool expect_window,
TaskList* order,
bool is_reentrant) {
- task_runner->PostTask(FROM_HERE,
- Bind(&RecursiveFunc, order, 1, 2, is_reentrant));
- task_runner->PostTask(FROM_HERE,
- Bind(&MessageBoxFunc, order, 2, is_reentrant));
- task_runner->PostTask(FROM_HERE,
- Bind(&RecursiveFunc, order, 3, 2, is_reentrant));
+ target->PostTask(FROM_HERE,
+ Bind(&RecursiveFunc, order, 1, 2, is_reentrant));
+ target->PostTask(FROM_HERE,
+ Bind(&MessageBoxFunc, order, 2, is_reentrant));
+ target->PostTask(FROM_HERE,
+ Bind(&RecursiveFunc, order, 3, 2, is_reentrant));
// The trick here is that for recursive task processing, this task will be
// ran _inside_ the MessageBox message loop, dismissing the MessageBox
// without a chance.
// For non-recursive task processing, this will be executed _after_ the
// MessageBox will have been dismissed by the code below, where
// expect_window_ is true.
- task_runner->PostTask(FROM_HERE, Bind(&EndDialogFunc, order, 4));
- task_runner->PostTask(FROM_HERE, Bind(&QuitFunc, order, 5));
+ target->PostTask(FROM_HERE,
+ Bind(&EndDialogFunc, order, 4));
+ target->PostTask(FROM_HERE,
+ Bind(&QuitFunc, order, 5));
// Enforce that every tasks are sent before starting to run the main thread
// message loop.
@@ -378,12 +331,16 @@ void RunTest_RecursiveDenial2(MessageLoop::Type message_loop_type) {
ASSERT_EQ(true, worker.StartWithOptions(options));
TaskList order;
win::ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL));
- worker.task_runner()->PostTask(
- FROM_HERE, Bind(&RecursiveFuncWin, ThreadTaskRunnerHandle::Get(),
- event.Get(), true, &order, false));
+ worker.message_loop()->PostTask(FROM_HERE,
+ Bind(&RecursiveFuncWin,
+ MessageLoop::current(),
+ event.Get(),
+ true,
+ &order,
+ false));
// Let the other thread execute.
WaitForSingleObject(event.Get(), INFINITE);
- RunLoop().Run();
+ MessageLoop::current()->Run();
ASSERT_EQ(17u, order.Size());
EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true));
@@ -418,12 +375,16 @@ void RunTest_RecursiveSupport2(MessageLoop::Type message_loop_type) {
ASSERT_EQ(true, worker.StartWithOptions(options));
TaskList order;
win::ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL));
- worker.task_runner()->PostTask(
- FROM_HERE, Bind(&RecursiveFuncWin, ThreadTaskRunnerHandle::Get(),
- event.Get(), false, &order, true));
+ worker.message_loop()->PostTask(FROM_HERE,
+ Bind(&RecursiveFuncWin,
+ MessageLoop::current(),
+ event.Get(),
+ false,
+ &order,
+ true));
// Let the other thread execute.
WaitForSingleObject(event.Get(), INFINITE);
- RunLoop().Run();
+ MessageLoop::current()->Run();
ASSERT_EQ(18u, order.Size());
EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true));
@@ -456,7 +417,7 @@ void RunTest_RecursiveSupport2(MessageLoop::Type message_loop_type) {
void PostNTasksThenQuit(int posts_remaining) {
if (posts_remaining > 1) {
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->task_runner()->PostTask(
FROM_HERE, Bind(&PostNTasksThenQuit, posts_remaining - 1));
} else {
MessageLoop::current()->QuitWhenIdle();
@@ -530,9 +491,12 @@ void RunTest_IOHandler() {
options.message_loop_type = MessageLoop::TYPE_IO;
ASSERT_TRUE(thread.StartWithOptions(options));
+ MessageLoop* thread_loop = thread.message_loop();
+ ASSERT_TRUE(NULL != thread_loop);
+
TestIOHandler handler(kPipeName, callback_called.Get(), false);
- thread.task_runner()->PostTask(
- FROM_HERE, Bind(&TestIOHandler::Init, Unretained(&handler)));
+ thread_loop->PostTask(FROM_HERE, Bind(&TestIOHandler::Init,
+ Unretained(&handler)));
// Make sure the thread runs and sleeps for lack of work.
PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
@@ -568,16 +532,19 @@ void RunTest_WaitForIO() {
options.message_loop_type = MessageLoop::TYPE_IO;
ASSERT_TRUE(thread.StartWithOptions(options));
+ MessageLoop* thread_loop = thread.message_loop();
+ ASSERT_TRUE(NULL != thread_loop);
+
TestIOHandler handler1(kPipeName1, callback1_called.Get(), false);
TestIOHandler handler2(kPipeName2, callback2_called.Get(), true);
- thread.task_runner()->PostTask(
- FROM_HERE, Bind(&TestIOHandler::Init, Unretained(&handler1)));
+ thread_loop->PostTask(FROM_HERE, Bind(&TestIOHandler::Init,
+ Unretained(&handler1)));
// TODO(ajwong): Do we really need such long Sleeps in this function?
// Make sure the thread runs and sleeps for lack of work.
TimeDelta delay = TimeDelta::FromMilliseconds(100);
PlatformThread::Sleep(delay);
- thread.task_runner()->PostTask(
- FROM_HERE, Bind(&TestIOHandler::Init, Unretained(&handler2)));
+ thread_loop->PostTask(FROM_HERE, Bind(&TestIOHandler::Init,
+ Unretained(&handler2)));
PlatformThread::Sleep(delay);
// At this time handler1 is waiting to be called, and the thread is waiting
@@ -614,6 +581,9 @@ RUN_MESSAGE_LOOP_TESTS(UI, &TypeUIMessagePumpFactory);
RUN_MESSAGE_LOOP_TESTS(IO, &TypeIOMessagePumpFactory);
#if defined(OS_WIN)
+// Additional set of tests for GPU version of UI message loop.
+RUN_MESSAGE_LOOP_TESTS(GPU, &MessagePumpForGpu::CreateMessagePumpForGpu);
+
TEST(MessageLoopTest, PostDelayedTask_SharedTimer_SubPump) {
RunTest_PostDelayedTask_SharedTimer_SubPump();
}
@@ -687,26 +657,26 @@ TEST(MessageLoopTest, WaitForIO) {
}
TEST(MessageLoopTest, HighResolutionTimer) {
- MessageLoop message_loop;
+ MessageLoop loop;
Time::EnableHighResolutionTimer(true);
const TimeDelta kFastTimer = TimeDelta::FromMilliseconds(5);
const TimeDelta kSlowTimer = TimeDelta::FromMilliseconds(100);
- EXPECT_FALSE(message_loop.HasHighResolutionTasks());
+ EXPECT_FALSE(loop.HasHighResolutionTasks());
// Post a fast task to enable the high resolution timers.
- message_loop.task_runner()->PostDelayedTask(
- FROM_HERE, Bind(&PostNTasksThenQuit, 1), kFastTimer);
- EXPECT_TRUE(message_loop.HasHighResolutionTasks());
- RunLoop().Run();
- EXPECT_FALSE(message_loop.HasHighResolutionTasks());
+ loop.PostDelayedTask(FROM_HERE, Bind(&PostNTasksThenQuit, 1),
+ kFastTimer);
+ EXPECT_TRUE(loop.HasHighResolutionTasks());
+ loop.Run();
+ EXPECT_FALSE(loop.HasHighResolutionTasks());
EXPECT_FALSE(Time::IsHighResolutionTimerInUse());
// Check that a slow task does not trigger the high resolution logic.
- message_loop.task_runner()->PostDelayedTask(
- FROM_HERE, Bind(&PostNTasksThenQuit, 1), kSlowTimer);
- EXPECT_FALSE(message_loop.HasHighResolutionTasks());
- RunLoop().Run();
- EXPECT_FALSE(message_loop.HasHighResolutionTasks());
+ loop.PostDelayedTask(FROM_HERE, Bind(&PostNTasksThenQuit, 1),
+ kSlowTimer);
+ EXPECT_FALSE(loop.HasHighResolutionTasks());
+ loop.Run();
+ EXPECT_FALSE(loop.HasHighResolutionTasks());
Time::EnableHighResolutionTimer(false);
}
@@ -739,7 +709,7 @@ TEST(MessageLoopTest, FileDescriptorWatcherOutlivesMessageLoop) {
int fd = pipefds[1];
{
// Arrange for controller to live longer than message loop.
- MessageLoopForIO::FileDescriptorWatcher controller(FROM_HERE);
+ MessageLoopForIO::FileDescriptorWatcher controller;
{
MessageLoopForIO message_loop;
@@ -766,7 +736,7 @@ TEST(MessageLoopTest, FileDescriptorWatcherDoubleStop) {
// Arrange for message loop to live longer than controller.
MessageLoopForIO message_loop;
{
- MessageLoopForIO::FileDescriptorWatcher controller(FROM_HERE);
+ MessageLoopForIO::FileDescriptorWatcher controller;
QuitDelegate delegate;
message_loop.WatchFileDescriptor(fd,
@@ -866,10 +836,10 @@ TEST(MessageLoopTest, ThreadMainTaskRunner) {
scoped_refptr<Foo> foo(new Foo());
std::string a("a");
ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, Bind(
- &Foo::Test1ConstRef, foo, a));
+ &Foo::Test1ConstRef, foo.get(), a));
// Post quit task;
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->task_runner()->PostTask(
FROM_HERE,
Bind(&MessageLoop::QuitWhenIdle, Unretained(MessageLoop::current())));
@@ -891,10 +861,8 @@ TEST(MessageLoopTest, IsType) {
void EmptyFunction() {}
void PostMultipleTasks() {
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- base::Bind(&EmptyFunction));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- base::Bind(&EmptyFunction));
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&EmptyFunction));
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&EmptyFunction));
}
static const int kSignalMsg = WM_USER + 2;
@@ -922,20 +890,19 @@ LRESULT CALLBACK TestWndProcThunk(HWND hwnd, UINT message,
// First, we post a task that will post multiple no-op tasks to make sure
// that the pump's incoming task queue does not become empty during the
// test.
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- base::Bind(&PostMultipleTasks));
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&PostMultipleTasks));
// Next, we post a task that posts a windows message to trigger the second
// stage of the test.
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::Bind(&PostWindowsMessage, hwnd));
+ MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(&PostWindowsMessage, hwnd));
break;
case 2:
// Since we're about to enter a modal loop, tell the message loop that we
// intend to nest tasks.
MessageLoop::current()->SetNestableTasksAllowed(true);
bool did_run = false;
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::Bind(&EndTest, &did_run, hwnd));
+ MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(&EndTest, &did_run, hwnd));
// Run a nested windows-style message loop and verify that our task runs. If
// it doesn't, then we'll loop here until the test times out.
MSG msg;
@@ -972,7 +939,7 @@ TEST(MessageLoopTest, AlwaysHaveUserMessageWhenNesting) {
ASSERT_TRUE(PostMessage(message_hwnd, kSignalMsg, 0, 1));
- RunLoop().Run();
+ loop.Run();
ASSERT_TRUE(UnregisterClass(MAKEINTATOM(atom), instance));
}
@@ -995,7 +962,7 @@ TEST(MessageLoopTest, OriginalRunnerWorks) {
scoped_refptr<Foo> foo(new Foo());
original_runner->PostTask(FROM_HERE,
- Bind(&Foo::Test1ConstRef, foo, "a"));
+ Bind(&Foo::Test1ConstRef, foo.get(), "a"));
RunLoop().RunUntilIdle();
EXPECT_EQ(1, foo->test_count());
}
diff --git a/base/message_loop/message_pump.cc b/base/message_loop/message_pump.cc
index 3d85b9b564..2f740f2423 100644
--- a/base/message_loop/message_pump.cc
+++ b/base/message_loop/message_pump.cc
@@ -15,4 +15,11 @@ MessagePump::~MessagePump() {
void MessagePump::SetTimerSlack(TimerSlack) {
}
+#if defined(OS_WIN)
+bool MessagePump::WasSignaled() {
+ NOTREACHED();
+ return false;
+}
+#endif
+
} // namespace base
diff --git a/base/message_loop/message_pump.h b/base/message_loop/message_pump.h
index c53be80410..af8ed41f27 100644
--- a/base/message_loop/message_pump.h
+++ b/base/message_loop/message_pump.h
@@ -124,6 +124,15 @@ class BASE_EXPORT MessagePump : public NonThreadSafe {
// Sets the timer slack to the specified value.
virtual void SetTimerSlack(TimerSlack timer_slack);
+
+#if defined(OS_WIN)
+ // TODO (stanisc): crbug.com/596190: Remove this after the signaling issue
+ // has been investigated.
+ // This should be used for diagnostic only. If message pump wake-up mechanism
+ // is based on auto-reset event this call would reset the event to unset
+ // state.
+ virtual bool WasSignaled();
+#endif
};
} // namespace base
diff --git a/base/message_loop/message_pump_default.cc b/base/message_loop/message_pump_default.cc
index cf68270c56..3449aec860 100644
--- a/base/message_loop/message_pump_default.cc
+++ b/base/message_loop/message_pump_default.cc
@@ -4,6 +4,8 @@
#include "base/message_loop/message_pump_default.h"
+#include <algorithm>
+
#include "base/logging.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
@@ -52,11 +54,38 @@ void MessagePumpDefault::Run(Delegate* delegate) {
if (delayed_work_time_.is_null()) {
event_.Wait();
} else {
- // No need to handle already expired |delayed_work_time_| in any special
- // way. When |delayed_work_time_| is in the past TimeWaitUntil returns
- // promptly and |delayed_work_time_| will re-initialized on a next
- // DoDelayedWork call which has to be called in order to get here again.
- event_.TimedWaitUntil(delayed_work_time_);
+ TimeDelta delay = delayed_work_time_ - TimeTicks::Now();
+ if (delay > TimeDelta()) {
+#if defined(OS_WIN)
+ // TODO(stanisc): crbug.com/623223: Consider moving the OS_WIN specific
+ // logic into TimedWait implementation in waitable_event_win.cc.
+
+ // crbug.com/487724: on Windows, waiting for less than 1 ms results in
+ // returning from TimedWait promptly and spinning
+ // MessagePumpDefault::Run loop for up to 1 ms - until it is time to
+ // run a delayed task. |min_delay| is the minimum possible wait to
+ // to avoid the spinning.
+ constexpr TimeDelta min_delay = TimeDelta::FromMilliseconds(1);
+ do {
+ delay = std::max(delay, min_delay);
+ if (event_.TimedWait(delay))
+ break;
+
+ // TimedWait can time out earlier than the specified |delay| on
+ // Windows. It doesn't make sense to run the outer loop in that case
+ // because there isn't going to be any new work. It is less overhead
+ // to just go back to wait.
+ // In practice this inner wait loop might have up to 3 iterations.
+ delay = delayed_work_time_ - TimeTicks::Now();
+ } while (delay > TimeDelta());
+#else
+ event_.TimedWait(delay);
+#endif
+ } else {
+ // It looks like delayed_work_time_ indicates a time in the past, so we
+ // need to call DoDelayedWork now.
+ delayed_work_time_ = TimeTicks();
+ }
}
// Since event_ is auto-reset, we don't need to do anything special here
// other than service each delegate method.
diff --git a/base/message_loop/message_pump_glib.h b/base/message_loop/message_pump_glib.h
index d79dba55a2..a2b54d8542 100644
--- a/base/message_loop/message_pump_glib.h
+++ b/base/message_loop/message_pump_glib.h
@@ -69,7 +69,7 @@ class BASE_EXPORT MessagePumpGlib : public MessagePump {
// Dispatch() will be called.
int wakeup_pipe_read_;
int wakeup_pipe_write_;
- // Use a unique_ptr to avoid needing the definition of GPollFD in the header.
+ // Use a scoped_ptr to avoid needing the definition of GPollFD in the header.
std::unique_ptr<GPollFD> wakeup_gpollfd_;
DISALLOW_COPY_AND_ASSIGN(MessagePumpGlib);
diff --git a/base/message_loop/message_pump_glib_unittest.cc b/base/message_loop/message_pump_glib_unittest.cc
index a89ccb9365..7ddd4f08a0 100644
--- a/base/message_loop/message_pump_glib_unittest.cc
+++ b/base/message_loop/message_pump_glib_unittest.cc
@@ -16,9 +16,7 @@
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
-#include "base/single_thread_task_runner.h"
#include "base/threading/thread.h"
-#include "base/threading/thread_task_runner_handle.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
@@ -154,7 +152,7 @@ void ExpectProcessedEvents(EventInjector* injector, int count) {
// Posts a task on the current message loop.
void PostMessageLoopTask(const tracked_objects::Location& from_here,
const Closure& task) {
- ThreadTaskRunnerHandle::Get()->PostTask(from_here, task);
+ MessageLoop::current()->PostTask(from_here, task);
}
// Test fixture.
@@ -195,7 +193,7 @@ TEST_F(MessagePumpGLibTest, TestQuit) {
injector()->Reset();
// Quit from an event
injector()->AddEvent(0, MessageLoop::QuitWhenIdleClosure());
- RunLoop().Run();
+ loop()->Run();
EXPECT_EQ(1, injector()->processed_events());
}
@@ -215,7 +213,7 @@ TEST_F(MessagePumpGLibTest, TestEventTaskInterleave) {
injector()->AddEventAsTask(0, posted_task);
injector()->AddEventAsTask(0, Bind(&DoNothing));
injector()->AddEvent(0, MessageLoop::QuitWhenIdleClosure());
- RunLoop().Run();
+ loop()->Run();
EXPECT_EQ(4, injector()->processed_events());
injector()->Reset();
@@ -226,7 +224,7 @@ TEST_F(MessagePumpGLibTest, TestEventTaskInterleave) {
injector()->AddEventAsTask(0, posted_task);
injector()->AddEventAsTask(10, Bind(&DoNothing));
injector()->AddEvent(0, MessageLoop::QuitWhenIdleClosure());
- RunLoop().Run();
+ loop()->Run();
EXPECT_EQ(4, injector()->processed_events());
}
@@ -235,15 +233,15 @@ TEST_F(MessagePumpGLibTest, TestWorkWhileWaitingForEvents) {
// Tests that we process tasks while waiting for new events.
// The event queue is empty at first.
for (int i = 0; i < 10; ++i) {
- loop()->task_runner()->PostTask(FROM_HERE,
- Bind(&IncrementInt, &task_count));
+ loop()->PostTask(FROM_HERE, Bind(&IncrementInt, &task_count));
}
// After all the previous tasks have executed, enqueue an event that will
// quit.
- loop()->task_runner()->PostTask(
- FROM_HERE, Bind(&EventInjector::AddEvent, Unretained(injector()), 0,
- MessageLoop::QuitWhenIdleClosure()));
- RunLoop().Run();
+ loop()->PostTask(
+ FROM_HERE,
+ Bind(&EventInjector::AddEvent, Unretained(injector()), 0,
+ MessageLoop::QuitWhenIdleClosure()));
+ loop()->Run();
ASSERT_EQ(10, task_count);
EXPECT_EQ(1, injector()->processed_events());
@@ -251,19 +249,21 @@ TEST_F(MessagePumpGLibTest, TestWorkWhileWaitingForEvents) {
injector()->Reset();
task_count = 0;
for (int i = 0; i < 10; ++i) {
- loop()->task_runner()->PostDelayedTask(FROM_HERE,
- Bind(&IncrementInt, &task_count),
- TimeDelta::FromMilliseconds(10 * i));
+ loop()->PostDelayedTask(
+ FROM_HERE,
+ Bind(&IncrementInt, &task_count),
+ TimeDelta::FromMilliseconds(10*i));
}
// After all the previous tasks have executed, enqueue an event that will
// quit.
// This relies on the fact that delayed tasks are executed in delay order.
// That is verified in message_loop_unittest.cc.
- loop()->task_runner()->PostDelayedTask(
- FROM_HERE, Bind(&EventInjector::AddEvent, Unretained(injector()), 10,
- MessageLoop::QuitWhenIdleClosure()),
+ loop()->PostDelayedTask(
+ FROM_HERE,
+ Bind(&EventInjector::AddEvent, Unretained(injector()), 10,
+ MessageLoop::QuitWhenIdleClosure()),
TimeDelta::FromMilliseconds(150));
- RunLoop().Run();
+ loop()->Run();
ASSERT_EQ(10, task_count);
EXPECT_EQ(1, injector()->processed_events());
}
@@ -285,7 +285,7 @@ TEST_F(MessagePumpGLibTest, TestEventsWhileWaitingForWork) {
// And then quit (relies on the condition tested by TestEventTaskInterleave).
injector()->AddEvent(10, MessageLoop::QuitWhenIdleClosure());
- RunLoop().Run();
+ loop()->Run();
EXPECT_EQ(12, injector()->processed_events());
}
@@ -310,7 +310,7 @@ class ConcurrentHelper : public RefCounted<ConcurrentHelper> {
if (task_count_ == 0 && event_count_ == 0) {
MessageLoop::current()->QuitWhenIdle();
} else {
- ThreadTaskRunnerHandle::Get()->PostTask(
+ MessageLoop::current()->PostTask(
FROM_HERE, Bind(&ConcurrentHelper::FromTask, this));
}
}
@@ -356,17 +356,17 @@ TEST_F(MessagePumpGLibTest, TestConcurrentEventPostedTask) {
// Add 2 events to the queue to make sure it is always full (when we remove
// the event before processing it).
injector()->AddEventAsTask(
- 0, Bind(&ConcurrentHelper::FromEvent, helper));
+ 0, Bind(&ConcurrentHelper::FromEvent, helper.get()));
injector()->AddEventAsTask(
- 0, Bind(&ConcurrentHelper::FromEvent, helper));
+ 0, Bind(&ConcurrentHelper::FromEvent, helper.get()));
// Similarly post 2 tasks.
- loop()->task_runner()->PostTask(
- FROM_HERE, Bind(&ConcurrentHelper::FromTask, helper));
- loop()->task_runner()->PostTask(
- FROM_HERE, Bind(&ConcurrentHelper::FromTask, helper));
+ loop()->PostTask(
+ FROM_HERE, Bind(&ConcurrentHelper::FromTask, helper.get()));
+ loop()->PostTask(
+ FROM_HERE, Bind(&ConcurrentHelper::FromTask, helper.get()));
- RunLoop().Run();
+ loop()->Run();
EXPECT_EQ(0, helper->event_count());
EXPECT_EQ(0, helper->task_count());
}
@@ -381,8 +381,8 @@ void AddEventsAndDrainGLib(EventInjector* injector) {
injector->AddEvent(0, MessageLoop::QuitWhenIdleClosure());
// Post a couple of dummy tasks
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, Bind(&DoNothing));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, Bind(&DoNothing));
+ MessageLoop::current()->PostTask(FROM_HERE, Bind(&DoNothing));
+ MessageLoop::current()->PostTask(FROM_HERE, Bind(&DoNothing));
// Drain the events
while (g_main_context_pending(NULL)) {
@@ -394,9 +394,10 @@ void AddEventsAndDrainGLib(EventInjector* injector) {
TEST_F(MessagePumpGLibTest, TestDrainingGLib) {
// Tests that draining events using GLib works.
- loop()->task_runner()->PostTask(
- FROM_HERE, Bind(&AddEventsAndDrainGLib, Unretained(injector())));
- RunLoop().Run();
+ loop()->PostTask(
+ FROM_HERE,
+ Bind(&AddEventsAndDrainGLib, Unretained(injector())));
+ loop()->Run();
EXPECT_EQ(3, injector()->processed_events());
}
@@ -446,19 +447,21 @@ void TestGLibLoopInternal(EventInjector* injector) {
injector->AddDummyEvent(0);
injector->AddDummyEvent(0);
// Post a couple of dummy tasks
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&IncrementInt, &task_count));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&IncrementInt, &task_count));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&IncrementInt, &task_count));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&IncrementInt, &task_count));
// Delayed events
injector->AddDummyEvent(10);
injector->AddDummyEvent(10);
// Delayed work
- ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE, Bind(&IncrementInt, &task_count),
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ Bind(&IncrementInt, &task_count),
TimeDelta::FromMilliseconds(30));
- ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE, Bind(&GLibLoopRunner::Quit, runner),
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ Bind(&GLibLoopRunner::Quit, runner.get()),
TimeDelta::FromMilliseconds(40));
// Run a nested, straight GLib message loop.
@@ -479,19 +482,21 @@ void TestGtkLoopInternal(EventInjector* injector) {
injector->AddDummyEvent(0);
injector->AddDummyEvent(0);
// Post a couple of dummy tasks
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&IncrementInt, &task_count));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- Bind(&IncrementInt, &task_count));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&IncrementInt, &task_count));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&IncrementInt, &task_count));
// Delayed events
injector->AddDummyEvent(10);
injector->AddDummyEvent(10);
// Delayed work
- ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE, Bind(&IncrementInt, &task_count),
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ Bind(&IncrementInt, &task_count),
TimeDelta::FromMilliseconds(30));
- ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE, Bind(&GLibLoopRunner::Quit, runner),
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ Bind(&GLibLoopRunner::Quit, runner.get()),
TimeDelta::FromMilliseconds(40));
// Run a nested, straight Gtk message loop.
@@ -509,9 +514,10 @@ TEST_F(MessagePumpGLibTest, TestGLibLoop) {
// loop is not run by MessageLoop::Run() but by a straight GLib loop.
// Note that in this case we don't make strong guarantees about niceness
// between events and posted tasks.
- loop()->task_runner()->PostTask(
- FROM_HERE, Bind(&TestGLibLoopInternal, Unretained(injector())));
- RunLoop().Run();
+ loop()->PostTask(
+ FROM_HERE,
+ Bind(&TestGLibLoopInternal, Unretained(injector())));
+ loop()->Run();
}
TEST_F(MessagePumpGLibTest, TestGtkLoop) {
@@ -519,9 +525,10 @@ TEST_F(MessagePumpGLibTest, TestGtkLoop) {
// loop is not run by MessageLoop::Run() but by a straight Gtk loop.
// Note that in this case we don't make strong guarantees about niceness
// between events and posted tasks.
- loop()->task_runner()->PostTask(
- FROM_HERE, Bind(&TestGtkLoopInternal, Unretained(injector())));
- RunLoop().Run();
+ loop()->PostTask(
+ FROM_HERE,
+ Bind(&TestGtkLoopInternal, Unretained(injector())));
+ loop()->Run();
}
} // namespace base
diff --git a/base/message_loop/message_pump_libevent.cc b/base/message_loop/message_pump_libevent.cc
index 86f5faa056..5aa55678d7 100644
--- a/base/message_loop/message_pump_libevent.cc
+++ b/base/message_loop/message_pump_libevent.cc
@@ -44,13 +44,12 @@
namespace base {
-MessagePumpLibevent::FileDescriptorWatcher::FileDescriptorWatcher(
- const tracked_objects::Location& from_here)
+MessagePumpLibevent::FileDescriptorWatcher::FileDescriptorWatcher()
: event_(NULL),
pump_(NULL),
watcher_(NULL),
- was_destroyed_(NULL),
- created_from_location_(from_here) {}
+ was_destroyed_(NULL) {
+}
MessagePumpLibevent::FileDescriptorWatcher::~FileDescriptorWatcher() {
if (event_) {
@@ -75,15 +74,15 @@ bool MessagePumpLibevent::FileDescriptorWatcher::StopWatchingFileDescriptor() {
return (rv == 0);
}
-void MessagePumpLibevent::FileDescriptorWatcher::Init(event* e) {
+void MessagePumpLibevent::FileDescriptorWatcher::Init(event *e) {
DCHECK(e);
DCHECK(!event_);
event_ = e;
}
-event* MessagePumpLibevent::FileDescriptorWatcher::ReleaseEvent() {
- struct event* e = event_;
+event *MessagePumpLibevent::FileDescriptorWatcher::ReleaseEvent() {
+ struct event *e = event_;
event_ = NULL;
return e;
}
@@ -113,7 +112,7 @@ MessagePumpLibevent::MessagePumpLibevent()
wakeup_pipe_in_(-1),
wakeup_pipe_out_(-1) {
if (!Init())
- NOTREACHED();
+ NOTREACHED();
}
MessagePumpLibevent::~MessagePumpLibevent() {
@@ -135,8 +134,8 @@ MessagePumpLibevent::~MessagePumpLibevent() {
bool MessagePumpLibevent::WatchFileDescriptor(int fd,
bool persistent,
int mode,
- FileDescriptorWatcher* controller,
- Watcher* delegate) {
+ FileDescriptorWatcher *controller,
+ Watcher *delegate) {
DCHECK_GE(fd, 0);
DCHECK(controller);
DCHECK(delegate);
@@ -293,8 +292,16 @@ void MessagePumpLibevent::ScheduleDelayedWork(
bool MessagePumpLibevent::Init() {
int fds[2];
- if (!CreateLocalNonBlockingPipe(fds)) {
- DPLOG(ERROR) << "pipe creation failed";
+ if (pipe(fds)) {
+ DLOG(ERROR) << "pipe() failed, errno: " << errno;
+ return false;
+ }
+ if (!SetNonBlocking(fds[0])) {
+ DLOG(ERROR) << "SetNonBlocking for pipe fd[0] failed, errno: " << errno;
+ return false;
+ }
+ if (!SetNonBlocking(fds[1])) {
+ DLOG(ERROR) << "SetNonBlocking for pipe fd[1] failed, errno: " << errno;
return false;
}
wakeup_pipe_out_ = fds[0];
@@ -317,11 +324,8 @@ void MessagePumpLibevent::OnLibeventNotification(int fd,
FileDescriptorWatcher* controller =
static_cast<FileDescriptorWatcher*>(context);
DCHECK(controller);
- TRACE_EVENT2("toplevel", "MessagePumpLibevent::OnLibeventNotification",
- "src_file", controller->created_from_location().file_name(),
- "src_func", controller->created_from_location().function_name());
- TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION heap_profiler_scope(
- controller->created_from_location().file_name());
+ TRACE_EVENT1("toplevel", "MessagePumpLibevent::OnLibeventNotification",
+ "fd", fd);
MessagePumpLibevent* pump = controller->pump();
pump->processed_io_events_ = true;
diff --git a/base/message_loop/message_pump_libevent.h b/base/message_loop/message_pump_libevent.h
index 1124560d66..76f882f680 100644
--- a/base/message_loop/message_pump_libevent.h
+++ b/base/message_loop/message_pump_libevent.h
@@ -6,7 +6,6 @@
#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_LIBEVENT_H_
#include "base/compiler_specific.h"
-#include "base/location.h"
#include "base/macros.h"
#include "base/message_loop/message_pump.h"
#include "base/threading/thread_checker.h"
@@ -38,7 +37,7 @@ class BASE_EXPORT MessagePumpLibevent : public MessagePump {
// Object returned by WatchFileDescriptor to manage further watching.
class FileDescriptorWatcher {
public:
- explicit FileDescriptorWatcher(const tracked_objects::Location& from_here);
+ FileDescriptorWatcher();
~FileDescriptorWatcher(); // Implicitly calls StopWatchingFileDescriptor.
// NOTE: These methods aren't called StartWatching()/StopWatching() to
@@ -48,10 +47,6 @@ class BASE_EXPORT MessagePumpLibevent : public MessagePump {
// to do.
bool StopWatchingFileDescriptor();
- const tracked_objects::Location& created_from_location() {
- return created_from_location_;
- }
-
private:
friend class MessagePumpLibevent;
friend class MessagePumpLibeventTest;
@@ -78,8 +73,6 @@ class BASE_EXPORT MessagePumpLibevent : public MessagePump {
// destructor.
bool* was_destroyed_;
- const tracked_objects::Location created_from_location_;
-
DISALLOW_COPY_AND_ASSIGN(FileDescriptorWatcher);
};
@@ -107,8 +100,8 @@ class BASE_EXPORT MessagePumpLibevent : public MessagePump {
bool WatchFileDescriptor(int fd,
bool persistent,
int mode,
- FileDescriptorWatcher* controller,
- Watcher* delegate);
+ FileDescriptorWatcher *controller,
+ Watcher *delegate);
// MessagePump methods:
void Run(Delegate* delegate) override;
@@ -119,11 +112,15 @@ class BASE_EXPORT MessagePumpLibevent : public MessagePump {
private:
friend class MessagePumpLibeventTest;
+ void WillProcessIOEvent();
+ void DidProcessIOEvent();
+
// Risky part of constructor. Returns true on success.
bool Init();
// Called by libevent to tell us a registered FD can be read/written to.
- static void OnLibeventNotification(int fd, short flags, void* context);
+ static void OnLibeventNotification(int fd, short flags,
+ void* context);
// Unix pipe used to implement ScheduleWork()
// ... callback; called by libevent inside Run() when pipe is ready to read
diff --git a/base/message_loop/message_pump_mac.h b/base/message_loop/message_pump_mac.h
index f0766eb860..14b8377b90 100644
--- a/base/message_loop/message_pump_mac.h
+++ b/base/message_loop/message_pump_mac.h
@@ -78,11 +78,9 @@ class AutoreleasePoolType;
typedef NSAutoreleasePool AutoreleasePoolType;
#endif // !defined(__OBJC__) || __has_feature(objc_arc)
-class BASE_EXPORT MessagePumpCFRunLoopBase : public MessagePump {
+class MessagePumpCFRunLoopBase : public MessagePump {
// Needs access to CreateAutoreleasePool.
friend class MessagePumpScopedAutoreleasePool;
- friend class TestMessagePumpCFRunLoopBase;
-
public:
MessagePumpCFRunLoopBase();
~MessagePumpCFRunLoopBase() override;
@@ -115,21 +113,6 @@ class BASE_EXPORT MessagePumpCFRunLoopBase : public MessagePump {
virtual AutoreleasePoolType* CreateAutoreleasePool();
private:
- // Marking timers as invalid at the right time helps significantly reduce
- // power use (see the comment in RunDelayedWorkTimer()), however there is no
- // public API for doing so. CFRuntime.h states that CFRuntimeBase, upon which
- // the above timer invalidation functions are based, can change from release
- // to release and should not be accessed directly (this struct last changed at
- // least in 2008 in CF-476).
- //
- // This function uses private API to modify a test timer's valid state and
- // uses public API to confirm that the private API changed the right bit.
- static bool CanInvalidateCFRunLoopTimers();
-
- // Sets a Core Foundation object's "invalid" bit to |valid|. Based on code
- // from CFRunLoop.c.
- static void ChromeCFRunLoopTimerSetValid(CFRunLoopTimerRef timer, bool valid);
-
// Timer callback scheduled by ScheduleDelayedWork. This does not do any
// work, but it signals work_source_ so that delayed work can be performed
// within the appropriate priority constraints.
diff --git a/base/message_loop/message_pump_mac.mm b/base/message_loop/message_pump_mac.mm
index a3accee049..95d1c5f1fc 100644
--- a/base/message_loop/message_pump_mac.mm
+++ b/base/message_loop/message_pump_mac.mm
@@ -4,18 +4,16 @@
#import "base/message_loop/message_pump_mac.h"
+#include <dlfcn.h>
#import <Foundation/Foundation.h>
#include <limits>
#include "base/logging.h"
-#include "base/mac/call_with_eh_frame.h"
#include "base/mac/scoped_cftyperef.h"
-#include "base/macros.h"
#include "base/message_loop/timer_slack.h"
#include "base/run_loop.h"
#include "base/time/time.h"
-#include "build/build_config.h"
#if !defined(OS_IOS)
#import <AppKit/AppKit.h>
@@ -69,48 +67,34 @@ const CFTimeInterval kCFTimeIntervalMax =
// Set to true if MessagePumpMac::Create() is called before NSApp is
// initialized. Only accessed from the main thread.
bool g_not_using_cr_app = false;
-
-// Various CoreFoundation definitions.
-typedef struct __CFRuntimeBase {
- uintptr_t _cfisa;
- uint8_t _cfinfo[4];
-#if __LP64__
- uint32_t _rc;
-#endif
-} CFRuntimeBase;
-
-#if defined(__BIG_ENDIAN__)
-#define __CF_BIG_ENDIAN__ 1
-#define __CF_LITTLE_ENDIAN__ 0
#endif
-#if defined(__LITTLE_ENDIAN__)
-#define __CF_LITTLE_ENDIAN__ 1
-#define __CF_BIG_ENDIAN__ 0
-#endif
+// Call through to CFRunLoopTimerSetTolerance(), which is only available on
+// OS X 10.9.
+void SetTimerTolerance(CFRunLoopTimerRef timer, CFTimeInterval tolerance) {
+ typedef void (*CFRunLoopTimerSetTolerancePtr)(CFRunLoopTimerRef timer,
+ CFTimeInterval tolerance);
+
+ static CFRunLoopTimerSetTolerancePtr settimertolerance_function_ptr;
+
+ static dispatch_once_t get_timer_tolerance_function_ptr_once;
+ dispatch_once(&get_timer_tolerance_function_ptr_once, ^{
+ NSBundle* bundle =[NSBundle
+ bundleWithPath:@"/System/Library/Frameworks/CoreFoundation.framework"];
+ const char* path = [[bundle executablePath] fileSystemRepresentation];
+ CHECK(path);
+ void* library_handle = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
+ CHECK(library_handle) << dlerror();
+ settimertolerance_function_ptr =
+ reinterpret_cast<CFRunLoopTimerSetTolerancePtr>(
+ dlsym(library_handle, "CFRunLoopTimerSetTolerance"));
+
+ dlclose(library_handle);
+ });
-#define CF_INFO_BITS (!!(__CF_BIG_ENDIAN__)*3)
-
-#define __CFBitfieldMask(N1, N2) \
- ((((UInt32)~0UL) << (31UL - (N1) + (N2))) >> (31UL - N1))
-#define __CFBitfieldSetValue(V, N1, N2, X) \
- ((V) = ((V) & ~__CFBitfieldMask(N1, N2)) | \
- (((X) << (N2)) & __CFBitfieldMask(N1, N2)))
-
-// Marking timers as invalid at the right time by flipping their valid bit helps
-// significantly reduce power use (see the explanation in
-// RunDelayedWorkTimer()), however there is no public API for doing so.
-// CFRuntime.h states that CFRuntimeBase can change from release to release
-// and should not be accessed directly. The last known change of this struct
-// occurred in 2008 in CF-476 / 10.5; unfortunately the source for 10.11 and
-// 10.12 is not available for inspection at this time.
-// CanInvalidateCFRunLoopTimers() will at least prevent us from invalidating
-// timers if this function starts flipping the wrong bit on a future OS release.
-void __ChromeCFRunLoopTimerSetValid(CFRunLoopTimerRef timer, bool valid) {
- __CFBitfieldSetValue(((CFRuntimeBase*)timer)->_cfinfo[CF_INFO_BITS], 3, 3,
- valid);
+ if (settimertolerance_function_ptr)
+ settimertolerance_function_ptr(timer, tolerance);
}
-#endif // !defined(OS_IOS)
} // namespace
@@ -135,47 +119,6 @@ class MessagePumpScopedAutoreleasePool {
DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool);
};
-#if !defined(OS_IOS)
-// This function uses private API to modify a test timer's valid state and
-// uses public API to confirm that the private API changed the correct bit.
-// static
-bool MessagePumpCFRunLoopBase::CanInvalidateCFRunLoopTimers() {
- CFRunLoopTimerContext timer_context = CFRunLoopTimerContext();
- timer_context.info = nullptr;
- ScopedCFTypeRef<CFRunLoopTimerRef> test_timer(
- CFRunLoopTimerCreate(NULL, // allocator
- kCFTimeIntervalMax, // fire time
- kCFTimeIntervalMax, // interval
- 0, // flags
- 0, // priority
- nullptr, &timer_context));
- // Should be valid from the start.
- if (!CFRunLoopTimerIsValid(test_timer)) {
- return false;
- }
- // Confirm that the private API can mark the timer invalid.
- __ChromeCFRunLoopTimerSetValid(test_timer, false);
- if (CFRunLoopTimerIsValid(test_timer)) {
- return false;
- }
- // Confirm that the private API can mark the timer valid.
- __ChromeCFRunLoopTimerSetValid(test_timer, true);
- return CFRunLoopTimerIsValid(test_timer);
-}
-#endif // !defined(OS_IOS)
-
-// static
-void MessagePumpCFRunLoopBase::ChromeCFRunLoopTimerSetValid(
- CFRunLoopTimerRef timer,
- bool valid) {
-#if !defined(OS_IOS)
- static bool can_invalidate_timers = CanInvalidateCFRunLoopTimers();
- if (can_invalidate_timers) {
- __ChromeCFRunLoopTimerSetValid(timer, valid);
- }
-#endif // !defined(OS_IOS)
-}
-
// Must be called on the run loop thread.
MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase()
: delegate_(NULL),
@@ -325,22 +268,11 @@ void MessagePumpCFRunLoopBase::ScheduleDelayedWork(
const TimeTicks& delayed_work_time) {
TimeDelta delta = delayed_work_time - TimeTicks::Now();
delayed_work_fire_time_ = CFAbsoluteTimeGetCurrent() + delta.InSecondsF();
-
- // Flip the timer's validation bit just before setting the new fire time. Do
- // this now because CFRunLoopTimerSetNextFireDate() likely checks the validity
- // of a timer before proceeding to set its fire date. Making the timer valid
- // now won't have any side effects (such as a premature firing of the timer)
- // because we're only flipping a bit.
- //
- // Please see the comment in RunDelayedWorkTimer() for more info on the whys
- // of invalidation.
- ChromeCFRunLoopTimerSetValid(delayed_work_timer_, true);
-
CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_);
if (timer_slack_ == TIMER_SLACK_MAXIMUM) {
- CFRunLoopTimerSetTolerance(delayed_work_timer_, delta.InSecondsF() * 0.5);
+ SetTimerTolerance(delayed_work_timer_, delta.InSecondsF() * 0.5);
} else {
- CFRunLoopTimerSetTolerance(delayed_work_timer_, 0);
+ SetTimerTolerance(delayed_work_timer_, 0);
}
}
@@ -358,31 +290,6 @@ void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(
// The timer won't fire again until it's reset.
self->delayed_work_fire_time_ = kCFTimeIntervalMax;
- // The message pump's timer needs to fire at changing and unpredictable
- // intervals. Creating a new timer for each firing time is very expensive, so
- // the message pump instead uses a repeating timer with a very large repeat
- // rate. After each firing of the timer, the run loop sets the timer's next
- // firing time to the distant future, essentially pausing the timer until the
- // pump sets the next firing time. This is the solution recommended by Apple.
- //
- // It turns out, however, that scheduling timers is also quite expensive, and
- // that every one of the message pump's timer firings incurs two
- // reschedulings. The first rescheduling occurs in ScheduleDelayedWork(),
- // which sets the desired next firing time. The second comes after exiting
- // this method (the timer's callback method), when the run loop sets the
- // timer's next firing time to far in the future.
- //
- // The code in __CFRunLoopDoTimer() inside CFRunLoop.c calls the timer's
- // callback, confirms that the timer is valid, and then sets its future
- // firing time based on its repeat frequency. Flipping the valid bit here
- // causes the __CFRunLoopDoTimer() to skip setting the future firing time.
- // Note that there's public API to invalidate a timer but it goes beyond
- // flipping the valid bit, making the timer unusable in the future.
- //
- // ScheduleDelayedWork() flips the valid bit back just before setting the
- // timer's new firing time.
- ChromeCFRunLoopTimerSetValid(self->delayed_work_timer_, false);
-
// CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources.
// In order to establish the proper priority in which work and delayed work
// are processed one for one, the timer used to schedule delayed work must
@@ -394,9 +301,7 @@ void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(
// static
void MessagePumpCFRunLoopBase::RunWorkSource(void* info) {
MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
- base::mac::CallWithEHFrame(^{
- self->RunWork();
- });
+ self->RunWork();
}
// Called by MessagePumpCFRunLoopBase::RunWorkSource.
diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc
index 6b38d55bbd..600b94ed48 100644
--- a/base/metrics/field_trial.cc
+++ b/base/metrics/field_trial.cc
@@ -5,26 +5,15 @@
#include "base/metrics/field_trial.h"
#include <algorithm>
-#include <utility>
-#include "base/base_switches.h"
#include "base/build_time.h"
-#include "base/command_line.h"
-#include "base/debug/activity_tracker.h"
#include "base/logging.h"
-#include "base/metrics/field_trial_param_associator.h"
-#include "base/process/memory.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
-// On POSIX, the fd is shared using the mapping in GlobalDescriptors.
-#if defined(OS_POSIX) && !defined(OS_NACL)
-#include "base/posix/global_descriptors.h"
-#endif
-
namespace base {
namespace {
@@ -38,62 +27,6 @@ const char kPersistentStringSeparator = '/'; // Currently a slash.
// command line which forces its activation.
const char kActivationMarker = '*';
-// Use shared memory to communicate field trial (experiment) state. Set to false
-// for now while the implementation is fleshed out (e.g. data format, single
-// shared memory segment). See https://codereview.chromium.org/2365273004/ and
-// crbug.com/653874
-// The browser is the only process that has write access to the shared memory.
-// This is safe from race conditions because MakeIterable is a release operation
-// and GetNextOfType is an acquire operation, so memory writes before
-// MakeIterable happen before memory reads after GetNextOfType.
-const bool kUseSharedMemoryForFieldTrials = true;
-
-// Constants for the field trial allocator.
-const char kAllocatorName[] = "FieldTrialAllocator";
-
-// We allocate 128 KiB to hold all the field trial data. This should be enough,
-// as most people use 3 - 25 KiB for field trials (as of 11/25/2016).
-// This also doesn't allocate all 128 KiB at once -- the pages only get mapped
-// to physical memory when they are touched. If the size of the allocated field
-// trials does get larger than 128 KiB, then we will drop some field trials in
-// child processes, leading to an inconsistent view between browser and child
-// processes and possibly causing crashes (see crbug.com/661617).
-const size_t kFieldTrialAllocationSize = 128 << 10; // 128 KiB
-
-// Writes out string1 and then string2 to pickle.
-bool WriteStringPair(Pickle* pickle,
- const StringPiece& string1,
- const StringPiece& string2) {
- if (!pickle->WriteString(string1))
- return false;
- if (!pickle->WriteString(string2))
- return false;
- return true;
-}
-
-// Writes out the field trial's contents (via trial_state) to the pickle. The
-// format of the pickle looks like:
-// TrialName, GroupName, ParamKey1, ParamValue1, ParamKey2, ParamValue2, ...
-// If there are no parameters, then it just ends at GroupName.
-bool PickleFieldTrial(const FieldTrial::State& trial_state, Pickle* pickle) {
- if (!WriteStringPair(pickle, *trial_state.trial_name,
- *trial_state.group_name)) {
- return false;
- }
-
- // Get field trial params.
- std::map<std::string, std::string> params;
- FieldTrialParamAssociator::GetInstance()->GetFieldTrialParamsWithoutFallback(
- *trial_state.trial_name, *trial_state.group_name, &params);
-
- // Write params to pickle.
- for (const auto& param : params) {
- if (!WriteStringPair(pickle, param.first, param.second))
- return false;
- }
- return true;
-}
-
// Created a time value based on |year|, |month| and |day_of_month| parameters.
Time CreateTimeFromParams(int year, int month, int day_of_month) {
DCHECK_GT(year, 1970);
@@ -140,18 +73,11 @@ FieldTrial::Probability GetGroupBoundaryValue(
return std::min(result, divisor - 1);
}
-// Separate type from FieldTrial::State so that it can use StringPieces.
-struct FieldTrialStringEntry {
- StringPiece trial_name;
- StringPiece group_name;
- bool activated = false;
-};
-
// Parses the --force-fieldtrials string |trials_string| into |entries|.
// Returns true if the string was parsed correctly. On failure, the |entries|
// array may end up being partially filled.
bool ParseFieldTrialsString(const std::string& trials_string,
- std::vector<FieldTrialStringEntry>* entries) {
+ std::vector<FieldTrial::State>* entries) {
const StringPiece trials_string_piece(trials_string);
size_t next_item = 0;
@@ -166,7 +92,7 @@ bool ParseFieldTrialsString(const std::string& trials_string,
if (group_name_end == trials_string.npos)
group_name_end = trials_string.length();
- FieldTrialStringEntry entry;
+ FieldTrial::State entry;
// Verify if the trial should be activated or not.
if (trials_string[next_item] == kActivationMarker) {
// Name cannot be only the indicator.
@@ -181,61 +107,11 @@ bool ParseFieldTrialsString(const std::string& trials_string,
trials_string_piece.substr(name_end + 1, group_name_end - name_end - 1);
next_item = group_name_end + 1;
- entries->push_back(std::move(entry));
+ entries->push_back(entry);
}
return true;
}
-void AddFeatureAndFieldTrialFlags(const char* enable_features_switch,
- const char* disable_features_switch,
- CommandLine* cmd_line) {
- std::string enabled_features;
- std::string disabled_features;
- FeatureList::GetInstance()->GetFeatureOverrides(&enabled_features,
- &disabled_features);
-
- if (!enabled_features.empty())
- cmd_line->AppendSwitchASCII(enable_features_switch, enabled_features);
- if (!disabled_features.empty())
- cmd_line->AppendSwitchASCII(disable_features_switch, disabled_features);
-
- std::string field_trial_states;
- FieldTrialList::AllStatesToString(&field_trial_states);
- if (!field_trial_states.empty()) {
- cmd_line->AppendSwitchASCII(switches::kForceFieldTrials,
- field_trial_states);
- }
-}
-
-#if defined(OS_WIN)
-HANDLE CreateReadOnlyHandle(FieldTrialList::FieldTrialAllocator* allocator) {
- HANDLE src = allocator->shared_memory()->handle().GetHandle();
- ProcessHandle process = GetCurrentProcess();
- DWORD access = SECTION_MAP_READ | SECTION_QUERY;
- HANDLE dst;
- if (!::DuplicateHandle(process, src, process, &dst, access, true, 0))
- return kInvalidPlatformFile;
- return dst;
-}
-#endif
-
-#if defined(OS_POSIX) && !defined(OS_NACL)
-int CreateReadOnlyHandle(FieldTrialList::FieldTrialAllocator* allocator) {
- SharedMemoryHandle new_handle;
- allocator->shared_memory()->ShareReadOnlyToProcess(GetCurrentProcessHandle(),
- &new_handle);
- return SharedMemory::GetFdFromSharedMemoryHandle(new_handle);
-}
-#endif
-
-void OnOutOfMemory(size_t size) {
-#if defined(OS_NACL)
- NOTREACHED();
-#else
- TerminateBecauseOutOfMemory(size);
-#endif
-}
-
} // namespace
// statics
@@ -251,55 +127,12 @@ int FieldTrialList::kNoExpirationYear = 0;
FieldTrial::EntropyProvider::~EntropyProvider() {
}
-FieldTrial::State::State() {}
+FieldTrial::State::State() : activated(false) {}
FieldTrial::State::State(const State& other) = default;
FieldTrial::State::~State() {}
-bool FieldTrial::FieldTrialEntry::GetTrialAndGroupName(
- StringPiece* trial_name,
- StringPiece* group_name) const {
- PickleIterator iter = GetPickleIterator();
- return ReadStringPair(&iter, trial_name, group_name);
-}
-
-bool FieldTrial::FieldTrialEntry::GetParams(
- std::map<std::string, std::string>* params) const {
- PickleIterator iter = GetPickleIterator();
- StringPiece tmp;
- // Skip reading trial and group name.
- if (!ReadStringPair(&iter, &tmp, &tmp))
- return false;
-
- while (true) {
- StringPiece key;
- StringPiece value;
- if (!ReadStringPair(&iter, &key, &value))
- return key.empty(); // Non-empty is bad: got one of a pair.
- (*params)[key.as_string()] = value.as_string();
- }
-}
-
-PickleIterator FieldTrial::FieldTrialEntry::GetPickleIterator() const {
- const char* src =
- reinterpret_cast<const char*>(this) + sizeof(FieldTrialEntry);
-
- Pickle pickle(src, pickle_size);
- return PickleIterator(pickle);
-}
-
-bool FieldTrial::FieldTrialEntry::ReadStringPair(
- PickleIterator* iter,
- StringPiece* trial_name,
- StringPiece* group_name) const {
- if (!iter->ReadStringPiece(trial_name))
- return false;
- if (!iter->ReadStringPiece(group_name))
- return false;
- return true;
-}
-
void FieldTrial::Disable() {
DCHECK(!group_reported_);
enable_field_trial_ = false;
@@ -410,8 +243,7 @@ FieldTrial::FieldTrial(const std::string& trial_name,
enable_field_trial_(true),
forced_(false),
group_reported_(false),
- trial_registered_(false),
- ref_(FieldTrialList::FieldTrialAllocator::kReferenceNull) {
+ trial_registered_(false) {
DCHECK_GT(total_probability, 0);
DCHECK(!trial_name_.empty());
DCHECK(!default_group_name_.empty());
@@ -435,10 +267,6 @@ void FieldTrial::SetGroupChoice(const std::string& group_name, int number) {
}
void FieldTrial::FinalizeGroupChoice() {
- FinalizeGroupChoiceImpl(false);
-}
-
-void FieldTrial::FinalizeGroupChoiceImpl(bool is_locked) {
if (group_ != kNotFinalized)
return;
accumulated_group_probability_ = divisor_;
@@ -446,10 +274,6 @@ void FieldTrial::FinalizeGroupChoiceImpl(bool is_locked) {
// finalized.
DCHECK(!forced_);
SetGroupChoice(default_group_name_, kDefaultGroupNumber);
-
- // Add the field trial to shared memory.
- if (kUseSharedMemoryForFieldTrials && trial_registered_)
- FieldTrialList::OnGroupFinalized(is_locked, this);
}
bool FieldTrial::GetActiveGroup(ActiveGroup* active_group) const {
@@ -465,18 +289,8 @@ bool FieldTrial::GetState(State* field_trial_state) {
if (!enable_field_trial_)
return false;
FinalizeGroupChoice();
- field_trial_state->trial_name = &trial_name_;
- field_trial_state->group_name = &group_name_;
- field_trial_state->activated = group_reported_;
- return true;
-}
-
-bool FieldTrial::GetStateWhileLocked(State* field_trial_state) {
- if (!enable_field_trial_)
- return false;
- FinalizeGroupChoiceImpl(true);
- field_trial_state->trial_name = &trial_name_;
- field_trial_state->group_name = &group_name_;
+ field_trial_state->trial_name = trial_name_;
+ field_trial_state->group_name = group_name_;
field_trial_state->activated = group_reported_;
return true;
}
@@ -494,8 +308,8 @@ FieldTrialList::Observer::~Observer() {
}
FieldTrialList::FieldTrialList(
- std::unique_ptr<const FieldTrial::EntropyProvider> entropy_provider)
- : entropy_provider_(std::move(entropy_provider)),
+ const FieldTrial::EntropyProvider* entropy_provider)
+ : entropy_provider_(entropy_provider),
observer_list_(new ObserverListThreadSafe<FieldTrialList::Observer>(
ObserverListBase<FieldTrialList::Observer>::NOTIFY_EXISTING_ONLY)) {
DCHECK(!global_);
@@ -661,17 +475,17 @@ void FieldTrialList::AllStatesToString(std::string* output) {
for (const auto& registered : global_->registered_) {
FieldTrial::State trial;
- if (!registered.second->GetStateWhileLocked(&trial))
+ if (!registered.second->GetState(&trial))
continue;
DCHECK_EQ(std::string::npos,
- trial.trial_name->find(kPersistentStringSeparator));
+ trial.trial_name.find(kPersistentStringSeparator));
DCHECK_EQ(std::string::npos,
- trial.group_name->find(kPersistentStringSeparator));
+ trial.group_name.find(kPersistentStringSeparator));
if (trial.activated)
output->append(1, kActivationMarker);
- output->append(*trial.trial_name);
+ trial.trial_name.AppendToString(output);
output->append(1, kPersistentStringSeparator);
- output->append(*trial.group_name);
+ trial.group_name.AppendToString(output);
output->append(1, kPersistentStringSeparator);
}
}
@@ -696,7 +510,7 @@ void FieldTrialList::GetActiveFieldTrialGroups(
void FieldTrialList::GetActiveFieldTrialGroupsFromString(
const std::string& trials_string,
FieldTrial::ActiveGroups* active_groups) {
- std::vector<FieldTrialStringEntry> entries;
+ std::vector<FieldTrial::State> entries;
if (!ParseFieldTrialsString(trials_string, &entries))
return;
@@ -711,36 +525,6 @@ void FieldTrialList::GetActiveFieldTrialGroupsFromString(
}
// static
-void FieldTrialList::GetInitiallyActiveFieldTrials(
- const base::CommandLine& command_line,
- FieldTrial::ActiveGroups* active_groups) {
- DCHECK(global_->create_trials_from_command_line_called_);
-
- if (!global_->field_trial_allocator_) {
- GetActiveFieldTrialGroupsFromString(
- command_line.GetSwitchValueASCII(switches::kForceFieldTrials),
- active_groups);
- return;
- }
-
- FieldTrialAllocator* allocator = global_->field_trial_allocator_.get();
- FieldTrialAllocator::Iterator mem_iter(allocator);
- const FieldTrial::FieldTrialEntry* entry;
- while ((entry = mem_iter.GetNextOfObject<FieldTrial::FieldTrialEntry>()) !=
- nullptr) {
- StringPiece trial_name;
- StringPiece group_name;
- if (subtle::NoBarrier_Load(&entry->activated) &&
- entry->GetTrialAndGroupName(&trial_name, &group_name)) {
- FieldTrial::ActiveGroup group;
- group.trial_name = trial_name.as_string();
- group.group_name = group_name.as_string();
- active_groups->push_back(group);
- }
- }
-}
-
-// static
bool FieldTrialList::CreateTrialsFromString(
const std::string& trials_string,
const std::set<std::string>& ignored_trial_names) {
@@ -748,7 +532,7 @@ bool FieldTrialList::CreateTrialsFromString(
if (trials_string.empty() || !global_)
return true;
- std::vector<FieldTrialStringEntry> entries;
+ std::vector<FieldTrial::State> entries;
if (!ParseFieldTrialsString(trials_string, &entries))
return false;
@@ -773,145 +557,6 @@ bool FieldTrialList::CreateTrialsFromString(
}
// static
-void FieldTrialList::CreateTrialsFromCommandLine(
- const CommandLine& cmd_line,
- const char* field_trial_handle_switch,
- int fd_key) {
- global_->create_trials_from_command_line_called_ = true;
-
-#if defined(OS_WIN)
- if (cmd_line.HasSwitch(field_trial_handle_switch)) {
- std::string handle_switch =
- cmd_line.GetSwitchValueASCII(field_trial_handle_switch);
- bool result = CreateTrialsFromHandleSwitch(handle_switch);
- DCHECK(result);
- }
-#endif
-
-#if defined(OS_POSIX) && !defined(OS_NACL)
- // On POSIX, we check if the handle is valid by seeing if the browser process
- // sent over the switch (we don't care about the value). Invalid handles
- // occur in some browser tests which don't initialize the allocator.
- if (cmd_line.HasSwitch(field_trial_handle_switch)) {
- bool result = CreateTrialsFromDescriptor(fd_key);
- DCHECK(result);
- }
-#endif
-
- if (cmd_line.HasSwitch(switches::kForceFieldTrials)) {
- bool result = FieldTrialList::CreateTrialsFromString(
- cmd_line.GetSwitchValueASCII(switches::kForceFieldTrials),
- std::set<std::string>());
- DCHECK(result);
- }
-}
-
-// static
-void FieldTrialList::CreateFeaturesFromCommandLine(
- const base::CommandLine& command_line,
- const char* enable_features_switch,
- const char* disable_features_switch,
- FeatureList* feature_list) {
- // Fallback to command line if not using shared memory.
- if (!kUseSharedMemoryForFieldTrials ||
- !global_->field_trial_allocator_.get()) {
- return feature_list->InitializeFromCommandLine(
- command_line.GetSwitchValueASCII(enable_features_switch),
- command_line.GetSwitchValueASCII(disable_features_switch));
- }
-
- feature_list->InitializeFromSharedMemory(
- global_->field_trial_allocator_.get());
-}
-
-#if defined(OS_WIN)
-// static
-void FieldTrialList::AppendFieldTrialHandleIfNeeded(
- HandlesToInheritVector* handles) {
- if (!global_)
- return;
- if (kUseSharedMemoryForFieldTrials) {
- InstantiateFieldTrialAllocatorIfNeeded();
- if (global_->readonly_allocator_handle_)
- handles->push_back(global_->readonly_allocator_handle_);
- }
-}
-#endif
-
-#if defined(OS_POSIX) && !defined(OS_NACL)
-// static
-int FieldTrialList::GetFieldTrialHandle() {
- if (global_ && kUseSharedMemoryForFieldTrials) {
- InstantiateFieldTrialAllocatorIfNeeded();
- // We check for an invalid handle where this gets called.
- return global_->readonly_allocator_handle_;
- }
- return kInvalidPlatformFile;
-}
-#endif
-
-// static
-void FieldTrialList::CopyFieldTrialStateToFlags(
- const char* field_trial_handle_switch,
- const char* enable_features_switch,
- const char* disable_features_switch,
- CommandLine* cmd_line) {
- // TODO(lawrencewu): Ideally, having the global would be guaranteed. However,
- // content browser tests currently don't create a FieldTrialList because they
- // don't run ChromeBrowserMainParts code where it's done for Chrome.
- // Some tests depend on the enable and disable features flag switch, though,
- // so we can still add those even though AllStatesToString() will be a no-op.
- if (!global_) {
- AddFeatureAndFieldTrialFlags(enable_features_switch,
- disable_features_switch, cmd_line);
- return;
- }
-
- // Use shared memory to pass the state if the feature is enabled, otherwise
- // fallback to passing it via the command line as a string.
- if (kUseSharedMemoryForFieldTrials) {
- InstantiateFieldTrialAllocatorIfNeeded();
- // If the readonly handle didn't get duplicated properly, then fallback to
- // original behavior.
- if (global_->readonly_allocator_handle_ == kInvalidPlatformFile) {
- AddFeatureAndFieldTrialFlags(enable_features_switch,
- disable_features_switch, cmd_line);
- return;
- }
-
- global_->field_trial_allocator_->UpdateTrackingHistograms();
-
-#if defined(OS_WIN)
- // We need to pass a named anonymous handle to shared memory over the
- // command line on Windows, since the child doesn't know which of the
- // handles it inherited it should open.
- // PlatformFile is typedef'd to HANDLE which is typedef'd to void *. We
- // basically cast the handle into an int (uintptr_t, to be exact), stringify
- // the int, and pass it as a command-line flag. The child process will do
- // the reverse conversions to retrieve the handle. See
- // http://stackoverflow.com/a/153077
- auto uintptr_handle =
- reinterpret_cast<uintptr_t>(global_->readonly_allocator_handle_);
- std::string field_trial_handle = std::to_string(uintptr_handle);
- cmd_line->AppendSwitchASCII(field_trial_handle_switch, field_trial_handle);
-#elif defined(OS_POSIX)
- // On POSIX, we dup the fd into a fixed fd kFieldTrialDescriptor, so we
- // don't have to pass over the handle (it's not even the right handle
- // anyways). But some browser tests don't create the allocator, so we need
- // to be able to distinguish valid and invalid handles. We do that by just
- // checking that the flag is set with a dummy value.
- cmd_line->AppendSwitchASCII(field_trial_handle_switch, "1");
-#else
-#error Unsupported OS
-#endif
- return;
- }
-
- AddFeatureAndFieldTrialFlags(enable_features_switch, disable_features_switch,
- cmd_line);
-}
-
-// static
FieldTrial* FieldTrialList::CreateFieldTrial(
const std::string& name,
const std::string& group_name) {
@@ -952,20 +597,6 @@ void FieldTrialList::RemoveObserver(Observer* observer) {
}
// static
-void FieldTrialList::OnGroupFinalized(bool is_locked, FieldTrial* field_trial) {
- if (!global_)
- return;
- if (is_locked) {
- AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(),
- field_trial);
- } else {
- AutoLock auto_lock(global_->lock_);
- AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(),
- field_trial);
- }
-}
-
-// static
void FieldTrialList::NotifyFieldTrialGroupSelection(FieldTrial* field_trial) {
if (!global_)
return;
@@ -975,22 +606,10 @@ void FieldTrialList::NotifyFieldTrialGroupSelection(FieldTrial* field_trial) {
if (field_trial->group_reported_)
return;
field_trial->group_reported_ = true;
-
- if (!field_trial->enable_field_trial_)
- return;
-
- if (kUseSharedMemoryForFieldTrials)
- ActivateFieldTrialEntryWhileLocked(field_trial);
}
- // Recording for stability debugging has to be done inline as a task posted
- // to an observer may not get executed before a crash.
- base::debug::GlobalActivityTracker* tracker =
- base::debug::GlobalActivityTracker::Get();
- if (tracker) {
- tracker->RecordFieldTrial(field_trial->trial_name(),
- field_trial->group_name_internal());
- }
+ if (!field_trial->enable_field_trial_)
+ return;
global_->observer_list_->Notify(
FROM_HERE, &FieldTrialList::Observer::OnFieldTrialGroupFinalized,
@@ -1006,334 +625,6 @@ size_t FieldTrialList::GetFieldTrialCount() {
}
// static
-bool FieldTrialList::GetParamsFromSharedMemory(
- FieldTrial* field_trial,
- std::map<std::string, std::string>* params) {
- DCHECK(global_);
- // If the field trial allocator is not set up yet, then there are several
- // cases:
- // - We are in the browser process and the allocator has not been set up
- // yet. If we got here, then we couldn't find the params in
- // FieldTrialParamAssociator, so it's definitely not here. Return false.
- // - Using shared memory for field trials is not enabled. If we got here,
- // then there's nothing in shared memory. Return false.
- // - We are in the child process and the allocator has not been set up yet.
- // If this is the case, then you are calling this too early. The field trial
- // allocator should get set up very early in the lifecycle. Try to see if
- // you can call it after it's been set up.
- AutoLock auto_lock(global_->lock_);
- if (!global_->field_trial_allocator_)
- return false;
-
- // If ref_ isn't set, then the field trial data can't be in shared memory.
- if (!field_trial->ref_)
- return false;
-
- const FieldTrial::FieldTrialEntry* entry =
- global_->field_trial_allocator_->GetAsObject<FieldTrial::FieldTrialEntry>(
- field_trial->ref_);
-
- size_t allocated_size =
- global_->field_trial_allocator_->GetAllocSize(field_trial->ref_);
- size_t actual_size = sizeof(FieldTrial::FieldTrialEntry) + entry->pickle_size;
- if (allocated_size < actual_size)
- return false;
-
- return entry->GetParams(params);
-}
-
-// static
-void FieldTrialList::ClearParamsFromSharedMemoryForTesting() {
- if (!global_)
- return;
-
- AutoLock auto_lock(global_->lock_);
- if (!global_->field_trial_allocator_)
- return;
-
- // To clear the params, we iterate through every item in the allocator, copy
- // just the trial and group name into a newly-allocated segment and then clear
- // the existing item.
- FieldTrialAllocator* allocator = global_->field_trial_allocator_.get();
- FieldTrialAllocator::Iterator mem_iter(allocator);
-
- // List of refs to eventually be made iterable. We can't make it in the loop,
- // since it would go on forever.
- std::vector<FieldTrial::FieldTrialRef> new_refs;
-
- FieldTrial::FieldTrialRef prev_ref;
- while ((prev_ref = mem_iter.GetNextOfType<FieldTrial::FieldTrialEntry>()) !=
- FieldTrialAllocator::kReferenceNull) {
- // Get the existing field trial entry in shared memory.
- const FieldTrial::FieldTrialEntry* prev_entry =
- allocator->GetAsObject<FieldTrial::FieldTrialEntry>(prev_ref);
- StringPiece trial_name;
- StringPiece group_name;
- if (!prev_entry->GetTrialAndGroupName(&trial_name, &group_name))
- continue;
-
- // Write a new entry, minus the params.
- Pickle pickle;
- pickle.WriteString(trial_name);
- pickle.WriteString(group_name);
- size_t total_size = sizeof(FieldTrial::FieldTrialEntry) + pickle.size();
- FieldTrial::FieldTrialEntry* new_entry =
- allocator->New<FieldTrial::FieldTrialEntry>(total_size);
- subtle::NoBarrier_Store(&new_entry->activated,
- subtle::NoBarrier_Load(&prev_entry->activated));
- new_entry->pickle_size = pickle.size();
-
- // TODO(lawrencewu): Modify base::Pickle to be able to write over a section
- // in memory, so we can avoid this memcpy.
- char* dst = reinterpret_cast<char*>(new_entry) +
- sizeof(FieldTrial::FieldTrialEntry);
- memcpy(dst, pickle.data(), pickle.size());
-
- // Update the ref on the field trial and add it to the list to be made
- // iterable.
- FieldTrial::FieldTrialRef new_ref = allocator->GetAsReference(new_entry);
- FieldTrial* trial = global_->PreLockedFind(trial_name.as_string());
- trial->ref_ = new_ref;
- new_refs.push_back(new_ref);
-
- // Mark the existing entry as unused.
- allocator->ChangeType(prev_ref, 0,
- FieldTrial::FieldTrialEntry::kPersistentTypeId,
- /*clear=*/false);
- }
-
- for (const auto& ref : new_refs) {
- allocator->MakeIterable(ref);
- }
-}
-
-// static
-void FieldTrialList::DumpAllFieldTrialsToPersistentAllocator(
- PersistentMemoryAllocator* allocator) {
- if (!global_)
- return;
- AutoLock auto_lock(global_->lock_);
- for (const auto& registered : global_->registered_) {
- AddToAllocatorWhileLocked(allocator, registered.second);
- }
-}
-
-// static
-std::vector<const FieldTrial::FieldTrialEntry*>
-FieldTrialList::GetAllFieldTrialsFromPersistentAllocator(
- PersistentMemoryAllocator const& allocator) {
- std::vector<const FieldTrial::FieldTrialEntry*> entries;
- FieldTrialAllocator::Iterator iter(&allocator);
- const FieldTrial::FieldTrialEntry* entry;
- while ((entry = iter.GetNextOfObject<FieldTrial::FieldTrialEntry>()) !=
- nullptr) {
- entries.push_back(entry);
- }
- return entries;
-}
-
-#if defined(OS_WIN)
-// static
-bool FieldTrialList::CreateTrialsFromHandleSwitch(
- const std::string& handle_switch) {
- int field_trial_handle = std::stoi(handle_switch);
- HANDLE handle = reinterpret_cast<HANDLE>(field_trial_handle);
- SharedMemoryHandle shm_handle(handle, GetCurrentProcId());
- return FieldTrialList::CreateTrialsFromSharedMemoryHandle(shm_handle);
-}
-#endif
-
-#if defined(OS_POSIX) && !defined(OS_NACL)
-// static
-bool FieldTrialList::CreateTrialsFromDescriptor(int fd_key) {
- if (!kUseSharedMemoryForFieldTrials)
- return false;
-
- if (fd_key == -1)
- return false;
-
- int fd = GlobalDescriptors::GetInstance()->MaybeGet(fd_key);
- if (fd == -1)
- return false;
-
-#if defined(OS_MACOSX) && !defined(OS_IOS)
- SharedMemoryHandle shm_handle(FileDescriptor(fd, true));
-#else
- SharedMemoryHandle shm_handle(fd, true);
-#endif
-
- bool result = FieldTrialList::CreateTrialsFromSharedMemoryHandle(shm_handle);
- DCHECK(result);
- return true;
-}
-#endif
-
-// static
-bool FieldTrialList::CreateTrialsFromSharedMemoryHandle(
- SharedMemoryHandle shm_handle) {
- // shm gets deleted when it gets out of scope, but that's OK because we need
- // it only for the duration of this method.
- std::unique_ptr<SharedMemory> shm(new SharedMemory(shm_handle, true));
- if (!shm.get()->Map(kFieldTrialAllocationSize))
- OnOutOfMemory(kFieldTrialAllocationSize);
-
- return FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm));
-}
-
-// static
-bool FieldTrialList::CreateTrialsFromSharedMemory(
- std::unique_ptr<SharedMemory> shm) {
- global_->field_trial_allocator_.reset(
- new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, true));
- FieldTrialAllocator* shalloc = global_->field_trial_allocator_.get();
- FieldTrialAllocator::Iterator mem_iter(shalloc);
-
- const FieldTrial::FieldTrialEntry* entry;
- while ((entry = mem_iter.GetNextOfObject<FieldTrial::FieldTrialEntry>()) !=
- nullptr) {
- StringPiece trial_name;
- StringPiece group_name;
- if (!entry->GetTrialAndGroupName(&trial_name, &group_name))
- return false;
-
- // TODO(lawrencewu): Convert the API for CreateFieldTrial to take
- // StringPieces.
- FieldTrial* trial =
- CreateFieldTrial(trial_name.as_string(), group_name.as_string());
-
- trial->ref_ = mem_iter.GetAsReference(entry);
- if (subtle::NoBarrier_Load(&entry->activated)) {
- // Call |group()| to mark the trial as "used" and notify observers, if
- // any. This is useful to ensure that field trials created in child
- // processes are properly reported in crash reports.
- trial->group();
- }
- }
- return true;
-}
-
-// static
-void FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded() {
- if (!global_)
- return;
- AutoLock auto_lock(global_->lock_);
- // Create the allocator if not already created and add all existing trials.
- if (global_->field_trial_allocator_ != nullptr)
- return;
-
- SharedMemoryCreateOptions options;
- options.size = kFieldTrialAllocationSize;
- options.share_read_only = true;
-#if defined(OS_MACOSX) && !defined(OS_IOS)
- options.type = SharedMemoryHandle::POSIX;
-#endif
-
- std::unique_ptr<SharedMemory> shm(new SharedMemory());
- if (!shm->Create(options))
- OnOutOfMemory(kFieldTrialAllocationSize);
-
- if (!shm->Map(kFieldTrialAllocationSize))
- OnOutOfMemory(kFieldTrialAllocationSize);
-
- global_->field_trial_allocator_.reset(
- new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, false));
- global_->field_trial_allocator_->CreateTrackingHistograms(kAllocatorName);
-
- // Add all existing field trials.
- for (const auto& registered : global_->registered_) {
- AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(),
- registered.second);
- }
-
- // Add all existing features.
- FeatureList::GetInstance()->AddFeaturesToAllocator(
- global_->field_trial_allocator_.get());
-
-#if !defined(OS_NACL)
- // Set |readonly_allocator_handle_| so we can pass it to be inherited and
- // via the command line.
- global_->readonly_allocator_handle_ =
- CreateReadOnlyHandle(global_->field_trial_allocator_.get());
-#endif
-}
-
-// static
-void FieldTrialList::AddToAllocatorWhileLocked(
- PersistentMemoryAllocator* allocator,
- FieldTrial* field_trial) {
- // Don't do anything if the allocator hasn't been instantiated yet.
- if (allocator == nullptr)
- return;
-
- // Or if the allocator is read only, which means we are in a child process and
- // shouldn't be writing to it.
- if (allocator->IsReadonly())
- return;
-
- FieldTrial::State trial_state;
- if (!field_trial->GetStateWhileLocked(&trial_state))
- return;
-
- // Or if we've already added it. We must check after GetState since it can
- // also add to the allocator.
- if (field_trial->ref_)
- return;
-
- Pickle pickle;
- if (!PickleFieldTrial(trial_state, &pickle)) {
- NOTREACHED();
- return;
- }
-
- size_t total_size = sizeof(FieldTrial::FieldTrialEntry) + pickle.size();
- FieldTrial::FieldTrialRef ref = allocator->Allocate(
- total_size, FieldTrial::FieldTrialEntry::kPersistentTypeId);
- if (ref == FieldTrialAllocator::kReferenceNull) {
- NOTREACHED();
- return;
- }
-
- FieldTrial::FieldTrialEntry* entry =
- allocator->GetAsObject<FieldTrial::FieldTrialEntry>(ref);
- subtle::NoBarrier_Store(&entry->activated, trial_state.activated);
- entry->pickle_size = pickle.size();
-
- // TODO(lawrencewu): Modify base::Pickle to be able to write over a section in
- // memory, so we can avoid this memcpy.
- char* dst =
- reinterpret_cast<char*>(entry) + sizeof(FieldTrial::FieldTrialEntry);
- memcpy(dst, pickle.data(), pickle.size());
-
- allocator->MakeIterable(ref);
- field_trial->ref_ = ref;
-}
-
-// static
-void FieldTrialList::ActivateFieldTrialEntryWhileLocked(
- FieldTrial* field_trial) {
- FieldTrialAllocator* allocator = global_->field_trial_allocator_.get();
-
- // Check if we're in the child process and return early if so.
- if (allocator && allocator->IsReadonly())
- return;
-
- FieldTrial::FieldTrialRef ref = field_trial->ref_;
- if (ref == FieldTrialAllocator::kReferenceNull) {
- // It's fine to do this even if the allocator hasn't been instantiated
- // yet -- it'll just return early.
- AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(),
- field_trial);
- } else {
- // It's also okay to do this even though the callee doesn't have a lock --
- // the only thing that happens on a stale read here is a slight performance
- // hit from the child re-synchronizing activation state.
- FieldTrial::FieldTrialEntry* entry =
- allocator->GetAsObject<FieldTrial::FieldTrialEntry>(ref);
- subtle::NoBarrier_Store(&entry->activated, 1);
- }
-}
-
-// static
const FieldTrial::EntropyProvider*
FieldTrialList::GetEntropyProviderForOneTimeRandomization() {
if (!global_) {
diff --git a/base/metrics/field_trial.h b/base/metrics/field_trial.h
index 60a6592ce6..28a4606a88 100644
--- a/base/metrics/field_trial.h
+++ b/base/metrics/field_trial.h
@@ -58,24 +58,15 @@
#include <stdint.h>
#include <map>
-#include <memory>
#include <set>
#include <string>
#include <vector>
-#include "base/atomicops.h"
#include "base/base_export.h"
-#include "base/command_line.h"
-#include "base/feature_list.h"
-#include "base/files/file.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
-#include "base/memory/shared_memory.h"
-#include "base/metrics/persistent_memory_allocator.h"
#include "base/observer_list_threadsafe.h"
-#include "base/pickle.h"
-#include "base/process/launch.h"
#include "base/strings/string_piece.h"
#include "base/synchronization/lock.h"
#include "base/time/time.h"
@@ -88,9 +79,6 @@ class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> {
public:
typedef int Probability; // Probability type for being selected in a trial.
- // TODO(665129): Make private again after crash has been resolved.
- typedef SharedPersistentMemoryAllocator::Reference FieldTrialRef;
-
// Specifies the persistence of the field trial group choice.
enum RandomizationType {
// One time randomized trials will persist the group choice between
@@ -124,61 +112,17 @@ class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> {
};
// A triplet representing a FieldTrial, its selected group and whether it's
- // active. String members are pointers to the underlying strings owned by the
- // FieldTrial object. Does not use StringPiece to avoid conversions back to
- // std::string.
+ // active.
struct BASE_EXPORT State {
- const std::string* trial_name = nullptr;
- const std::string* group_name = nullptr;
- bool activated = false;
+ StringPiece trial_name;
+ StringPiece group_name;
+ bool activated;
State();
State(const State& other);
~State();
};
- // We create one FieldTrialEntry per field trial in shared memory, via
- // AddToAllocatorWhileLocked. The FieldTrialEntry is followed by a
- // base::Pickle object that we unpickle and read from.
- struct BASE_EXPORT FieldTrialEntry {
- // SHA1(FieldTrialEntry): Increment this if structure changes!
- static constexpr uint32_t kPersistentTypeId = 0xABA17E13 + 2;
-
- // Expected size for 32/64-bit check.
- static constexpr size_t kExpectedInstanceSize = 8;
-
- // Whether or not this field trial is activated. This is really just a
- // boolean but using a 32 bit value for portability reasons. It should be
- // accessed via NoBarrier_Load()/NoBarrier_Store() to prevent the compiler
- // from doing unexpected optimizations because it thinks that only one
- // thread is accessing the memory location.
- subtle::Atomic32 activated;
-
- // Size of the pickled structure, NOT the total size of this entry.
- uint32_t pickle_size;
-
- // Calling this is only valid when the entry is initialized. That is, it
- // resides in shared memory and has a pickle containing the trial name and
- // group name following it.
- bool GetTrialAndGroupName(StringPiece* trial_name,
- StringPiece* group_name) const;
-
- // Calling this is only valid when the entry is initialized as well. Reads
- // the parameters following the trial and group name and stores them as
- // key-value mappings in |params|.
- bool GetParams(std::map<std::string, std::string>* params) const;
-
- private:
- // Returns an iterator over the data containing names and params.
- PickleIterator GetPickleIterator() const;
-
- // Takes the iterator and writes out the first two items into |trial_name|
- // and |group_name|.
- bool ReadStringPair(PickleIterator* iter,
- StringPiece* trial_name,
- StringPiece* group_name) const;
- };
-
typedef std::vector<ActiveGroup> ActiveGroups;
// A return value to indicate that a given instance has not yet had a group
@@ -269,9 +213,6 @@ class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> {
FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedChangeDefault_NonDefault);
FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, FloatBoundariesGiveEqualGroupSizes);
FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, DoesNotSurpassTotalProbability);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest,
- DoNotAddSimulatedFieldTrialsToAllocator);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, ClearParamsFromSharedMemory);
friend class base::FieldTrialList;
@@ -305,10 +246,6 @@ class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> {
// status.
void FinalizeGroupChoice();
- // Implements FinalizeGroupChoice() with the added flexibility of being
- // deadlock-free if |is_locked| is true and the caller is holding a lock.
- void FinalizeGroupChoiceImpl(bool is_locked);
-
// Returns the trial name and selected group name for this field trial via
// the output parameter |active_group|, but only if the group has already
// been chosen and has been externally observed via |group()| and the trial
@@ -324,10 +261,6 @@ class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> {
// untouched.
bool GetState(State* field_trial_state);
- // Does the same thing as above, but is deadlock-free if the caller is holding
- // a lock.
- bool GetStateWhileLocked(State* field_trial_state);
-
// Returns the group_name. A winner need not have been chosen.
std::string group_name_internal() const { return group_name_; }
@@ -375,9 +308,6 @@ class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> {
// should notify it when its group is queried.
bool trial_registered_;
- // Reference to related field trial struct and data in shared memory.
- FieldTrialRef ref_;
-
// When benchmarking is enabled, field trials all revert to the 'default'
// group.
static bool enable_benchmarking_;
@@ -391,8 +321,6 @@ class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> {
// Only one instance of this class exists.
class BASE_EXPORT FieldTrialList {
public:
- typedef SharedPersistentMemoryAllocator FieldTrialAllocator;
-
// Year that is guaranteed to not be expired when instantiating a field trial
// via |FactoryGetFieldTrial()|. Set to two years from the build date.
static int kNoExpirationYear;
@@ -410,12 +338,11 @@ class BASE_EXPORT FieldTrialList {
// This singleton holds the global list of registered FieldTrials.
//
- // To support one-time randomized field trials, specify a non-null
+ // To support one-time randomized field trials, specify a non-NULL
// |entropy_provider| which should be a source of uniformly distributed
- // entropy values. If one time randomization is not desired, pass in null for
- // |entropy_provider|.
- explicit FieldTrialList(
- std::unique_ptr<const FieldTrial::EntropyProvider> entropy_provider);
+ // entropy values. Takes ownership of |entropy_provider|. If one time
+ // randomization is not desired, pass in NULL for |entropy_provider|.
+ explicit FieldTrialList(const FieldTrial::EntropyProvider* entropy_provider);
// Destructor Release()'s references to all registered FieldTrial instances.
~FieldTrialList();
@@ -457,7 +384,7 @@ class BASE_EXPORT FieldTrialList {
// PermutedEntropyProvider (which is used when UMA is not enabled). If
// |override_entropy_provider| is not null, then it will be used for
// randomization instead of the provider given when the FieldTrialList was
- // instantiated.
+ // instanciated.
static FieldTrial* FactoryGetFieldTrialWithRandomizationSeed(
const std::string& trial_name,
FieldTrial::Probability total_probability,
@@ -522,14 +449,6 @@ class BASE_EXPORT FieldTrialList {
const std::string& trials_string,
FieldTrial::ActiveGroups* active_groups);
- // Returns the field trials that were active when the process was
- // created. Either parses the field trial string or the shared memory
- // holding field trial information.
- // Must be called only after a call to CreateTrialsFromCommandLine().
- static void GetInitiallyActiveFieldTrials(
- const base::CommandLine& command_line,
- FieldTrial::ActiveGroups* active_groups);
-
// Use a state string (re: StatesToString()) to augment the current list of
// field trials to include the supplied trials, and using a 100% probability
// for each trial, force them to have the same group string. This is commonly
@@ -543,51 +462,6 @@ class BASE_EXPORT FieldTrialList {
const std::string& trials_string,
const std::set<std::string>& ignored_trial_names);
- // Achieves the same thing as CreateTrialsFromString, except wraps the logic
- // by taking in the trials from the command line, either via shared memory
- // handle or command line argument. A bit of a misnomer since on POSIX we
- // simply get the trials from opening |fd_key| if using shared memory. On
- // Windows, we expect the |cmd_line| switch for |field_trial_handle_switch| to
- // contain the shared memory handle that contains the field trial allocator.
- // We need the |field_trial_handle_switch| and |fd_key| arguments to be passed
- // in since base/ can't depend on content/.
- static void CreateTrialsFromCommandLine(const base::CommandLine& cmd_line,
- const char* field_trial_handle_switch,
- int fd_key);
-
- // Creates base::Feature overrides from the command line by first trying to
- // use shared memory and then falling back to the command line if it fails.
- static void CreateFeaturesFromCommandLine(
- const base::CommandLine& command_line,
- const char* enable_features_switch,
- const char* disable_features_switch,
- FeatureList* feature_list);
-
-#if defined(OS_WIN)
- // On Windows, we need to explicitly pass down any handles to be inherited.
- // This function adds the shared memory handle to field trial state to the
- // list of handles to be inherited.
- static void AppendFieldTrialHandleIfNeeded(
- base::HandlesToInheritVector* handles);
-#endif
-
-#if defined(OS_POSIX) && !defined(OS_NACL)
- // On POSIX, we also need to explicitly pass down this file descriptor that
- // should be shared with the child process. Returns kInvalidPlatformFile if no
- // handle exists or was not initialized properly.
- static PlatformFile GetFieldTrialHandle();
-#endif
-
- // Adds a switch to the command line containing the field trial state as a
- // string (if not using shared memory to share field trial state), or the
- // shared memory handle + length.
- // Needs the |field_trial_handle_switch| argument to be passed in since base/
- // can't depend on content/.
- static void CopyFieldTrialStateToFlags(const char* field_trial_handle_switch,
- const char* enable_features_switch,
- const char* disable_features_switch,
- base::CommandLine* cmd_line);
-
// Create a FieldTrial with the given |name| and using 100% probability for
// the FieldTrial, force FieldTrial to have the same group string as
// |group_name|. This is commonly used in a non-browser process, to carry
@@ -605,86 +479,13 @@ class BASE_EXPORT FieldTrialList {
// Remove an observer.
static void RemoveObserver(Observer* observer);
- // Grabs the lock if necessary and adds the field trial to the allocator. This
- // should only be called from FinalizeGroupChoice().
- static void OnGroupFinalized(bool is_locked, FieldTrial* field_trial);
-
// Notify all observers that a group has been finalized for |field_trial|.
static void NotifyFieldTrialGroupSelection(FieldTrial* field_trial);
// Return the number of active field trials.
static size_t GetFieldTrialCount();
- // Gets the parameters for |field_trial| from shared memory and stores them in
- // |params|. This is only exposed for use by FieldTrialParamAssociator and
- // shouldn't be used by anything else.
- static bool GetParamsFromSharedMemory(
- FieldTrial* field_trial,
- std::map<std::string, std::string>* params);
-
- // Clears all the params in the allocator.
- static void ClearParamsFromSharedMemoryForTesting();
-
- // Dumps field trial state to an allocator so that it can be analyzed after a
- // crash.
- static void DumpAllFieldTrialsToPersistentAllocator(
- PersistentMemoryAllocator* allocator);
-
- // Retrieves field trial state from an allocator so that it can be analyzed
- // after a crash. The pointers in the returned vector are into the persistent
- // memory segment and so are only valid as long as the allocator is valid.
- static std::vector<const FieldTrial::FieldTrialEntry*>
- GetAllFieldTrialsFromPersistentAllocator(
- PersistentMemoryAllocator const& allocator);
-
private:
- // Allow tests to access our innards for testing purposes.
- FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, InstantiateAllocator);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, AddTrialsToAllocator);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest,
- DoNotAddSimulatedFieldTrialsToAllocator);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, AssociateFieldTrialParams);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, ClearParamsFromSharedMemory);
-
-#if defined(OS_WIN)
- // Takes in |handle_switch| from the command line which represents the shared
- // memory handle for field trials, parses it, and creates the field trials.
- // Returns true on success, false on failure.
- static bool CreateTrialsFromHandleSwitch(const std::string& handle_switch);
-#endif
-
-#if defined(OS_POSIX) && !defined(OS_NACL)
- // On POSIX systems that use the zygote, we look up the correct fd that backs
- // the shared memory segment containing the field trials by looking it up via
- // an fd key in GlobalDescriptors. Returns true on success, false on failure.
- static bool CreateTrialsFromDescriptor(int fd_key);
-#endif
-
- // Takes an unmapped SharedMemoryHandle, creates a SharedMemory object from it
- // and maps it with the correct size.
- static bool CreateTrialsFromSharedMemoryHandle(SharedMemoryHandle shm_handle);
-
- // Expects a mapped piece of shared memory |shm| that was created from the
- // browser process's field_trial_allocator and shared via the command line.
- // This function recreates the allocator, iterates through all the field
- // trials in it, and creates them via CreateFieldTrial(). Returns true if
- // successful and false otherwise.
- static bool CreateTrialsFromSharedMemory(
- std::unique_ptr<base::SharedMemory> shm);
-
- // Instantiate the field trial allocator, add all existing field trials to it,
- // and duplicates its handle to a read-only handle, which gets stored in
- // |readonly_allocator_handle|.
- static void InstantiateFieldTrialAllocatorIfNeeded();
-
- // Adds the field trial to the allocator. Caller must hold a lock before
- // calling this.
- static void AddToAllocatorWhileLocked(PersistentMemoryAllocator* allocator,
- FieldTrial* field_trial);
-
- // Activate the corresponding field trial entry struct in shared memory.
- static void ActivateFieldTrialEntryWhileLocked(FieldTrial* field_trial);
-
// A map from FieldTrial names to the actual instances.
typedef std::map<std::string, FieldTrial*> RegistrationMap;
@@ -709,8 +510,8 @@ class BASE_EXPORT FieldTrialList {
// FieldTrialList is created after that.
static bool used_without_global_;
- // Lock for access to registered_ and field_trial_allocator_.
- Lock lock_;
+ // Lock for access to registered_.
+ base::Lock lock_;
RegistrationMap registered_;
std::map<std::string, std::string> seen_states_;
@@ -722,20 +523,6 @@ class BASE_EXPORT FieldTrialList {
// List of observers to be notified when a group is selected for a FieldTrial.
scoped_refptr<ObserverListThreadSafe<Observer> > observer_list_;
- // Allocator in shared memory containing field trial data. Used in both
- // browser and child processes, but readonly in the child.
- // In the future, we may want to move this to a more generic place if we want
- // to start passing more data other than field trials.
- std::unique_ptr<FieldTrialAllocator> field_trial_allocator_ = nullptr;
-
- // Readonly copy of the handle to the allocator. Needs to be a member variable
- // because it's needed from both CopyFieldTrialStateToFlags() and
- // AppendFieldTrialHandleIfNeeded().
- PlatformFile readonly_allocator_handle_ = kInvalidPlatformFile;
-
- // Tracks whether CreateTrialsFromCommandLine() has been called.
- bool create_trials_from_command_line_called_ = false;
-
DISALLOW_COPY_AND_ASSIGN(FieldTrialList);
};
diff --git a/base/metrics/field_trial_param_associator.cc b/base/metrics/field_trial_param_associator.cc
deleted file mode 100644
index 3bac18d6a9..0000000000
--- a/base/metrics/field_trial_param_associator.cc
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2016 The Chromium 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/metrics/field_trial_param_associator.h"
-
-#include "base/metrics/field_trial.h"
-
-namespace base {
-
-FieldTrialParamAssociator::FieldTrialParamAssociator() {}
-FieldTrialParamAssociator::~FieldTrialParamAssociator() {}
-
-// static
-FieldTrialParamAssociator* FieldTrialParamAssociator::GetInstance() {
- return Singleton<FieldTrialParamAssociator,
- LeakySingletonTraits<FieldTrialParamAssociator>>::get();
-}
-
-bool FieldTrialParamAssociator::AssociateFieldTrialParams(
- const std::string& trial_name,
- const std::string& group_name,
- const FieldTrialParams& params) {
- if (FieldTrialList::IsTrialActive(trial_name))
- return false;
-
- AutoLock scoped_lock(lock_);
- const FieldTrialKey key(trial_name, group_name);
- if (ContainsKey(field_trial_params_, key))
- return false;
-
- field_trial_params_[key] = params;
- return true;
-}
-
-bool FieldTrialParamAssociator::GetFieldTrialParams(
- const std::string& trial_name,
- FieldTrialParams* params) {
- FieldTrial* field_trial = FieldTrialList::Find(trial_name);
- if (!field_trial)
- return false;
-
- // First try the local map, falling back to getting it from shared memory.
- if (GetFieldTrialParamsWithoutFallback(trial_name, field_trial->group_name(),
- params)) {
- return true;
- }
-
- // TODO(lawrencewu): add the params to field_trial_params_ for next time.
- return FieldTrialList::GetParamsFromSharedMemory(field_trial, params);
-}
-
-bool FieldTrialParamAssociator::GetFieldTrialParamsWithoutFallback(
- const std::string& trial_name,
- const std::string& group_name,
- FieldTrialParams* params) {
- AutoLock scoped_lock(lock_);
-
- const FieldTrialKey key(trial_name, group_name);
- if (!ContainsKey(field_trial_params_, key))
- return false;
-
- *params = field_trial_params_[key];
- return true;
-}
-
-void FieldTrialParamAssociator::ClearAllParamsForTesting() {
- {
- AutoLock scoped_lock(lock_);
- field_trial_params_.clear();
- }
- FieldTrialList::ClearParamsFromSharedMemoryForTesting();
-}
-
-void FieldTrialParamAssociator::ClearAllCachedParamsForTesting() {
- AutoLock scoped_lock(lock_);
- field_trial_params_.clear();
-}
-
-} // namespace base
diff --git a/base/metrics/field_trial_param_associator.h b/base/metrics/field_trial_param_associator.h
deleted file mode 100644
index b19c66661c..0000000000
--- a/base/metrics/field_trial_param_associator.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2016 The Chromium 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_METRICS_FIELD_TRIAL_PARAM_ASSOCIATOR_H_
-#define BASE_METRICS_FIELD_TRIAL_PARAM_ASSOCIATOR_H_
-
-#include <map>
-#include <string>
-#include <utility>
-
-#include "base/base_export.h"
-#include "base/memory/singleton.h"
-#include "base/metrics/field_trial.h"
-#include "base/synchronization/lock.h"
-
-namespace base {
-
-// Keeps track of the parameters of all field trials and ensures access to them
-// is thread-safe.
-class BASE_EXPORT FieldTrialParamAssociator {
- public:
- FieldTrialParamAssociator();
- ~FieldTrialParamAssociator();
-
- // Key-value mapping type for field trial parameters.
- typedef std::map<std::string, std::string> FieldTrialParams;
-
- // Retrieve the singleton.
- static FieldTrialParamAssociator* GetInstance();
-
- // Sets parameters for the given field trial name and group.
- bool AssociateFieldTrialParams(const std::string& trial_name,
- const std::string& group_name,
- const FieldTrialParams& params);
-
- // Gets the parameters for a field trial and its chosen group. If not found in
- // field_trial_params_, then tries to looks it up in shared memory.
- bool GetFieldTrialParams(const std::string& trial_name,
- FieldTrialParams* params);
-
- // Gets the parameters for a field trial and its chosen group. Does not
- // fallback to looking it up in shared memory. This should only be used if you
- // know for sure the params are in the mapping, like if you're in the browser
- // process, and even then you should probably just use GetFieldTrialParams().
- bool GetFieldTrialParamsWithoutFallback(const std::string& trial_name,
- const std::string& group_name,
- FieldTrialParams* params);
-
- // Clears the internal field_trial_params_ mapping, plus removes all params in
- // shared memory.
- void ClearAllParamsForTesting();
-
- // Clears the internal field_trial_params_ mapping.
- void ClearAllCachedParamsForTesting();
-
- private:
- friend struct DefaultSingletonTraits<FieldTrialParamAssociator>;
-
- // (field_trial_name, field_trial_group)
- typedef std::pair<std::string, std::string> FieldTrialKey;
-
- Lock lock_;
- std::map<FieldTrialKey, FieldTrialParams> field_trial_params_;
-
- DISALLOW_COPY_AND_ASSIGN(FieldTrialParamAssociator);
-};
-
-} // namespace base
-
-#endif // BASE_METRICS_FIELD_TRIAL_PARAM_ASSOCIATOR_H_
diff --git a/base/metrics/field_trial_unittest.cc b/base/metrics/field_trial_unittest.cc
index 54672e63d5..00f351fa2c 100644
--- a/base/metrics/field_trial_unittest.cc
+++ b/base/metrics/field_trial_unittest.cc
@@ -6,20 +6,13 @@
#include <stddef.h>
-#include "base/base_switches.h"
#include "base/build_time.h"
-#include "base/feature_list.h"
#include "base/macros.h"
-#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
-#include "base/metrics/field_trial_param_associator.h"
#include "base/rand_util.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
-#include "base/test/gtest_util.h"
-#include "base/test/mock_entropy_provider.h"
-#include "base/test/scoped_feature_list.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
@@ -342,12 +335,12 @@ TEST_F(FieldTrialTest, AllGroups) {
std::string winner("Winner");
trial->AppendGroup(winner, 10);
EXPECT_TRUE(trial->GetState(&field_trial_state));
- EXPECT_EQ(one_winner, *field_trial_state.trial_name);
- EXPECT_EQ(winner, *field_trial_state.group_name);
+ EXPECT_EQ(one_winner, field_trial_state.trial_name);
+ EXPECT_EQ(winner, field_trial_state.group_name);
trial->group();
EXPECT_TRUE(trial->GetState(&field_trial_state));
- EXPECT_EQ(one_winner, *field_trial_state.trial_name);
- EXPECT_EQ(winner, *field_trial_state.group_name);
+ EXPECT_EQ(one_winner, field_trial_state.trial_name);
+ EXPECT_EQ(winner, field_trial_state.group_name);
std::string multi_group("MultiGroup");
scoped_refptr<FieldTrial> multi_group_trial =
@@ -360,8 +353,8 @@ TEST_F(FieldTrialTest, AllGroups) {
// Finalize the group selection by accessing the selected group.
multi_group_trial->group();
EXPECT_TRUE(multi_group_trial->GetState(&field_trial_state));
- EXPECT_EQ(multi_group, *field_trial_state.trial_name);
- EXPECT_EQ(multi_group_trial->group_name(), *field_trial_state.group_name);
+ EXPECT_EQ(multi_group, field_trial_state.trial_name);
+ EXPECT_EQ(multi_group_trial->group_name(), field_trial_state.group_name);
}
TEST_F(FieldTrialTest, ActiveGroupsNotFinalized) {
@@ -1127,246 +1120,15 @@ TEST(FieldTrialTestWithoutList, StatesStringFormat) {
EXPECT_TRUE(field_trial_list.TrialExists("zzz"));
}
+#if GTEST_HAS_DEATH_TEST
TEST(FieldTrialDeathTest, OneTimeRandomizedTrialWithoutFieldTrialList) {
// Trying to instantiate a one-time randomized field trial before the
// FieldTrialList is created should crash.
- EXPECT_DEATH_IF_SUPPORTED(
- FieldTrialList::FactoryGetFieldTrial(
- "OneTimeRandomizedTrialWithoutFieldTrialList", 100, kDefaultGroupName,
- base::FieldTrialList::kNoExpirationYear, 1, 1,
- base::FieldTrial::ONE_TIME_RANDOMIZED, NULL),
- "");
-}
-
-#if defined(OS_WIN)
-TEST(FieldTrialListTest, TestCopyFieldTrialStateToFlags) {
- base::FieldTrialList field_trial_list(
- base::MakeUnique<base::MockEntropyProvider>());
- base::FieldTrialList::CreateFieldTrial("Trial1", "Group1");
- base::FilePath test_file_path = base::FilePath(FILE_PATH_LITERAL("Program"));
- base::CommandLine cmd_line = base::CommandLine(test_file_path);
- const char field_trial_handle[] = "test-field-trial-handle";
- const char enable_features_switch[] = "test-enable-features";
- const char disable_features_switch[] = "test-disable-features";
-
- base::FieldTrialList::CopyFieldTrialStateToFlags(
- field_trial_handle, enable_features_switch, disable_features_switch,
- &cmd_line);
- EXPECT_TRUE(cmd_line.HasSwitch(field_trial_handle) ||
- cmd_line.HasSwitch(switches::kForceFieldTrials));
+ EXPECT_DEATH(FieldTrialList::FactoryGetFieldTrial(
+ "OneTimeRandomizedTrialWithoutFieldTrialList", 100, kDefaultGroupName,
+ base::FieldTrialList::kNoExpirationYear, 1, 1,
+ base::FieldTrial::ONE_TIME_RANDOMIZED, NULL), "");
}
#endif
-TEST(FieldTrialListTest, InstantiateAllocator) {
- test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.Init();
-
- FieldTrialList field_trial_list(nullptr);
- FieldTrialList::CreateFieldTrial("Trial1", "Group1");
-
- FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded();
- void* memory = field_trial_list.field_trial_allocator_->shared_memory();
- size_t used = field_trial_list.field_trial_allocator_->used();
-
- // Ensure that the function is idempotent.
- FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded();
- void* new_memory = field_trial_list.field_trial_allocator_->shared_memory();
- size_t new_used = field_trial_list.field_trial_allocator_->used();
- EXPECT_EQ(memory, new_memory);
- EXPECT_EQ(used, new_used);
-}
-
-TEST(FieldTrialListTest, AddTrialsToAllocator) {
- std::string save_string;
- base::SharedMemoryHandle handle;
-
- // Scoping the first FieldTrialList, as we need another one to test that it
- // matches.
- {
- test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.Init();
-
- FieldTrialList field_trial_list(nullptr);
- FieldTrialList::CreateFieldTrial("Trial1", "Group1");
- FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded();
- FieldTrialList::AllStatesToString(&save_string);
- handle = base::SharedMemory::DuplicateHandle(
- field_trial_list.field_trial_allocator_->shared_memory()->handle());
- }
-
- FieldTrialList field_trial_list2(nullptr);
- std::unique_ptr<base::SharedMemory> shm(new SharedMemory(handle, true));
- // 4 KiB is enough to hold the trials only created for this test.
- shm.get()->Map(4 << 10);
- FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm));
- std::string check_string;
- FieldTrialList::AllStatesToString(&check_string);
- EXPECT_EQ(save_string, check_string);
-}
-
-TEST(FieldTrialListTest, DoNotAddSimulatedFieldTrialsToAllocator) {
- constexpr char kTrialName[] = "trial";
- base::SharedMemoryHandle handle;
- {
- test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.Init();
-
- // Create a simulated trial and a real trial and call group() on them, which
- // should only add the real trial to the field trial allocator.
- FieldTrialList field_trial_list(nullptr);
- FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded();
-
- // This shouldn't add to the allocator.
- scoped_refptr<FieldTrial> simulated_trial =
- FieldTrial::CreateSimulatedFieldTrial(kTrialName, 100, "Simulated",
- 0.95);
- simulated_trial->group();
-
- // This should add to the allocator.
- FieldTrial* real_trial =
- FieldTrialList::CreateFieldTrial(kTrialName, "Real");
- real_trial->group();
-
- handle = base::SharedMemory::DuplicateHandle(
- field_trial_list.field_trial_allocator_->shared_memory()->handle());
- }
-
- // Check that there's only one entry in the allocator.
- FieldTrialList field_trial_list2(nullptr);
- std::unique_ptr<base::SharedMemory> shm(new SharedMemory(handle, true));
- // 4 KiB is enough to hold the trials only created for this test.
- shm.get()->Map(4 << 10);
- FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm));
- std::string check_string;
- FieldTrialList::AllStatesToString(&check_string);
- ASSERT_EQ(check_string.find("Simulated"), std::string::npos);
-}
-
-TEST(FieldTrialListTest, AssociateFieldTrialParams) {
- test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.Init();
-
- std::string trial_name("Trial1");
- std::string group_name("Group1");
-
- // Create a field trial with some params.
- FieldTrialList field_trial_list(nullptr);
- FieldTrialList::CreateFieldTrial(trial_name, group_name);
- std::map<std::string, std::string> params;
- params["key1"] = "value1";
- params["key2"] = "value2";
- FieldTrialParamAssociator::GetInstance()->AssociateFieldTrialParams(
- trial_name, group_name, params);
- FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded();
-
- // Clear all cached params from the associator.
- FieldTrialParamAssociator::GetInstance()->ClearAllCachedParamsForTesting();
- // Check that the params have been cleared from the cache.
- std::map<std::string, std::string> cached_params;
- FieldTrialParamAssociator::GetInstance()->GetFieldTrialParamsWithoutFallback(
- trial_name, group_name, &cached_params);
- EXPECT_EQ(0U, cached_params.size());
-
- // Check that we fetch the param from shared memory properly.
- std::map<std::string, std::string> new_params;
- FieldTrialParamAssociator::GetInstance()->GetFieldTrialParams(trial_name,
- &new_params);
- EXPECT_EQ("value1", new_params["key1"]);
- EXPECT_EQ("value2", new_params["key2"]);
- EXPECT_EQ(2U, new_params.size());
-}
-
-TEST(FieldTrialListTest, ClearParamsFromSharedMemory) {
- std::string trial_name("Trial1");
- std::string group_name("Group1");
-
- base::SharedMemoryHandle handle;
- {
- test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.Init();
-
- // Create a field trial with some params.
- FieldTrialList field_trial_list(nullptr);
- FieldTrial* trial =
- FieldTrialList::CreateFieldTrial(trial_name, group_name);
- std::map<std::string, std::string> params;
- params["key1"] = "value1";
- params["key2"] = "value2";
- FieldTrialParamAssociator::GetInstance()->AssociateFieldTrialParams(
- trial_name, group_name, params);
- FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded();
-
- // Clear all params from the associator AND shared memory. The allocated
- // segments should be different.
- FieldTrial::FieldTrialRef old_ref = trial->ref_;
- FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting();
- FieldTrial::FieldTrialRef new_ref = trial->ref_;
- EXPECT_NE(old_ref, new_ref);
-
- // Check that there are no params associated with the field trial anymore.
- std::map<std::string, std::string> new_params;
- FieldTrialParamAssociator::GetInstance()->GetFieldTrialParams(trial_name,
- &new_params);
- EXPECT_EQ(0U, new_params.size());
-
- // Now duplicate the handle so we can easily check that the trial is still
- // in shared memory via AllStatesToString.
- handle = base::SharedMemory::DuplicateHandle(
- field_trial_list.field_trial_allocator_->shared_memory()->handle());
- }
-
- // Check that we have the trial.
- FieldTrialList field_trial_list2(nullptr);
- std::unique_ptr<base::SharedMemory> shm(new SharedMemory(handle, true));
- // 4 KiB is enough to hold the trials only created for this test.
- shm.get()->Map(4 << 10);
- FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm));
- std::string check_string;
- FieldTrialList::AllStatesToString(&check_string);
- EXPECT_EQ("*Trial1/Group1/", check_string);
-}
-
-TEST(FieldTrialListTest, DumpAndFetchFromSharedMemory) {
- std::string trial_name("Trial1");
- std::string group_name("Group1");
-
- // Create a field trial with some params.
- FieldTrialList field_trial_list(nullptr);
- FieldTrialList::CreateFieldTrial(trial_name, group_name);
- std::map<std::string, std::string> params;
- params["key1"] = "value1";
- params["key2"] = "value2";
- FieldTrialParamAssociator::GetInstance()->AssociateFieldTrialParams(
- trial_name, group_name, params);
-
- std::unique_ptr<base::SharedMemory> shm(new SharedMemory());
- // 4 KiB is enough to hold the trials only created for this test.
- shm.get()->CreateAndMapAnonymous(4 << 10);
- // We _could_ use PersistentMemoryAllocator, this just has less params.
- SharedPersistentMemoryAllocator allocator(std::move(shm), 1, "", false);
-
- // Dump and subsequently retrieve the field trial to |allocator|.
- FieldTrialList::DumpAllFieldTrialsToPersistentAllocator(&allocator);
- std::vector<const FieldTrial::FieldTrialEntry*> entries =
- FieldTrialList::GetAllFieldTrialsFromPersistentAllocator(allocator);
-
- // Check that we have the entry we put in.
- EXPECT_EQ(1u, entries.size());
- const FieldTrial::FieldTrialEntry* entry = entries[0];
-
- // Check that the trial and group names match.
- StringPiece shm_trial_name;
- StringPiece shm_group_name;
- entry->GetTrialAndGroupName(&shm_trial_name, &shm_group_name);
- EXPECT_EQ(trial_name, shm_trial_name);
- EXPECT_EQ(group_name, shm_group_name);
-
- // Check that the params match.
- std::map<std::string, std::string> shm_params;
- entry->GetParams(&shm_params);
- EXPECT_EQ(2u, shm_params.size());
- EXPECT_EQ("value1", shm_params["key1"]);
- EXPECT_EQ("value2", shm_params["key2"]);
-}
-
} // namespace base
diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc
index de2ac336d3..0d6287c0b1 100644
--- a/base/metrics/histogram.cc
+++ b/base/metrics/histogram.cc
@@ -217,7 +217,7 @@ HistogramBase* Histogram::Factory::Build() {
ReportHistogramActivity(*histogram, HISTOGRAM_LOOKUP);
}
- CHECK_EQ(histogram_type_, histogram->GetHistogramType()) << name_;
+ DCHECK_EQ(histogram_type_, histogram->GetHistogramType()) << name_;
if (bucket_count_ != 0 &&
!histogram->HasConstructionArguments(minimum_, maximum_, bucket_count_)) {
// The construction arguments do not match the existing histogram. This can
@@ -533,8 +533,7 @@ Histogram::Histogram(const std::string& name,
Histogram::~Histogram() {
}
-bool Histogram::PrintEmptyBucket(uint32_t index) const {
- ALLOW_UNUSED_PARAM(index);
+bool Histogram::PrintEmptyBucket(uint32_t /*index*/) const {
return true;
}
@@ -675,14 +674,15 @@ void Histogram::WriteAsciiHeader(const SampleVector& samples,
"Histogram: %s recorded %d samples",
histogram_name().c_str(),
sample_count);
- if (sample_count == 0) {
+ if (0 == sample_count) {
DCHECK_EQ(samples.sum(), 0);
} else {
- double mean = static_cast<float>(samples.sum()) / sample_count;
- StringAppendF(output, ", mean = %.1f", mean);
+ double average = static_cast<float>(samples.sum()) / sample_count;
+
+ StringAppendF(output, ", average = %.1f", average);
}
- if (flags())
- StringAppendF(output, " (flags = 0x%x)", flags());
+ if (flags() & ~kHexRangePrintingFlag)
+ StringAppendF(output, " (flags = 0x%x)", flags() & ~kHexRangePrintingFlag);
}
void Histogram::WriteAsciiBucketContext(const int64_t past,
@@ -754,7 +754,8 @@ class LinearHistogram::Factory : public Histogram::Factory {
std::unique_ptr<HistogramBase> HeapAlloc(
const BucketRanges* ranges) override {
- return WrapUnique(new LinearHistogram(name_, minimum_, maximum_, ranges));
+ return WrapUnique(
+ new LinearHistogram(name_, minimum_, maximum_, ranges));
}
void FillHistogram(HistogramBase* base_histogram) override {
@@ -1138,11 +1139,8 @@ bool CustomHistogram::SerializeInfoImpl(Pickle* pickle) const {
return true;
}
-double CustomHistogram::GetBucketSize(Count current, uint32_t i) const {
- ALLOW_UNUSED_PARAM(i);
- // If this is a histogram of enum values, normalizing the bucket count
- // by the bucket range is not helpful, so just return the bucket count.
- return current;
+double CustomHistogram::GetBucketSize(Count /*current*/, uint32_t /*i*/) const {
+ return 1;
}
// static
diff --git a/base/metrics/histogram.h b/base/metrics/histogram.h
index a76dd63226..2283a4d80f 100644
--- a/base/metrics/histogram.h
+++ b/base/metrics/histogram.h
@@ -9,11 +9,13 @@
// It supports calls to accumulate either time intervals (which are processed
// as integral number of milliseconds), or arbitrary integral units.
-// For Histogram (exponential histogram), LinearHistogram and CustomHistogram,
+// For Histogram(exponential histogram), LinearHistogram and CustomHistogram,
// the minimum for a declared range is 1 (instead of 0), while the maximum is
-// (HistogramBase::kSampleType_MAX - 1). However, there will always be underflow
-// and overflow buckets added automatically, so a 0 bucket will always exist
-// even when a minimum value of 1 is specified.
+// (HistogramBase::kSampleType_MAX - 1). Currently you can declare histograms
+// with ranges exceeding those limits (e.g. 0 as minimal or
+// HistogramBase::kSampleType_MAX as maximal), but those excesses will be
+// silently clamped to those limits (for backwards compatibility with existing
+// code). Best practice is to not exceed the limits.
// Each use of a histogram with the same name will reference the same underlying
// data, so it is safe to record to the same histogram from multiple locations
@@ -39,7 +41,7 @@
// are also counted by the constructor in the user supplied "bucket_count"
// argument.
// The above example has an exponential ratio of 2 (doubling the bucket width
-// in each consecutive bucket). The Histogram class automatically calculates
+// in each consecutive bucket. The Histogram class automatically calculates
// the smallest ratio that it can use to construct the number of buckets
// selected in the constructor. An another example, if you had 50 buckets,
// and millisecond time values from 1 to 10000, then the ratio between
@@ -79,6 +81,8 @@
#include "base/macros.h"
#include "base/metrics/bucket_ranges.h"
#include "base/metrics/histogram_base.h"
+// TODO(asvitkine): Migrate callers to to include this directly and remove this.
+#include "base/metrics/histogram_macros.h"
#include "base/metrics/histogram_samples.h"
#include "base/time/time.h"
@@ -88,6 +92,7 @@ class BooleanHistogram;
class CustomHistogram;
class Histogram;
class LinearHistogram;
+class PersistentMemoryAllocator;
class Pickle;
class PickleIterator;
class SampleVector;
diff --git a/base/metrics/histogram_base.cc b/base/metrics/histogram_base.cc
index 396f29739a..8c4f1eca18 100644
--- a/base/metrics/histogram_base.cc
+++ b/base/metrics/histogram_base.cc
@@ -97,7 +97,8 @@ bool HistogramBase::SerializeInfo(Pickle* pickle) const {
return SerializeInfoImpl(pickle);
}
-uint32_t HistogramBase::FindCorruption(const HistogramSamples& /* samples */) const {
+uint32_t HistogramBase::FindCorruption(
+ const HistogramSamples& /*samples*/) const {
// Not supported by default.
return NO_INCONSISTENCIES;
}
@@ -118,16 +119,14 @@ void HistogramBase::WriteJSON(std::string* output) const {
root.SetInteger("flags", flags());
root.Set("params", std::move(parameters));
root.Set("buckets", std::move(buckets));
- root.SetInteger("pid", GetUniqueIdForProcess());
+ root.SetInteger("pid", GetCurrentProcId());
serializer.Serialize(root);
}
// static
void HistogramBase::EnableActivityReportHistogram(
const std::string& process_type) {
- if (report_histogram_)
- return;
-
+ DCHECK(!report_histogram_);
size_t existing = StatisticsRecorder::GetHistogramCount();
if (existing != 0) {
DVLOG(1) << existing
@@ -175,7 +174,12 @@ void HistogramBase::WriteAsciiBucketGraph(double current_size,
const std::string HistogramBase::GetSimpleAsciiBucketRange(
Sample sample) const {
- return StringPrintf("%d", sample);
+ std::string result;
+ if (kHexRangePrintingFlag & flags())
+ StringAppendF(&result, "%#x", sample);
+ else
+ StringAppendF(&result, "%d", sample);
+ return result;
}
void HistogramBase::WriteAsciiBucketValue(Count current,
diff --git a/base/metrics/histogram_base.h b/base/metrics/histogram_base.h
index 4f5ba049bc..d240099110 100644
--- a/base/metrics/histogram_base.h
+++ b/base/metrics/histogram_base.h
@@ -21,6 +21,7 @@
namespace base {
+class BucketRanges;
class DictionaryValue;
class HistogramBase;
class HistogramSamples;
@@ -91,7 +92,7 @@ class BASE_EXPORT HistogramBase {
static const Sample kSampleType_MAX; // INT_MAX
enum Flags {
- kNoFlags = 0x0,
+ kNoFlags = 0,
// Histogram should be UMA uploaded.
kUmaTargetedHistogramFlag = 0x1,
@@ -120,6 +121,9 @@ class BASE_EXPORT HistogramBase {
// MemoryAllocator, and that loaded into the Histogram module before this
// histogram is created.
kIsPersistent = 0x40,
+
+ // Only for Histogram and its sub classes: fancy bucket-naming support.
+ kHexRangePrintingFlag = 0x8000,
};
// Histogram data inconsistency types.
diff --git a/base/metrics/histogram_macros.h b/base/metrics/histogram_macros.h
index 78473761dd..ce1811a5a7 100644
--- a/base/metrics/histogram_macros.h
+++ b/base/metrics/histogram_macros.h
@@ -5,311 +5,294 @@
#ifndef BASE_METRICS_HISTOGRAM_MACROS_H_
#define BASE_METRICS_HISTOGRAM_MACROS_H_
+#include "base/atomicops.h"
+#include "base/logging.h"
#include "base/metrics/histogram.h"
-#include "base/metrics/histogram_macros_internal.h"
-#include "base/metrics/histogram_macros_local.h"
#include "base/time/time.h"
-
-// Macros for efficient use of histograms.
+// Macros for efficient use of histograms. See documentation in histogram.h.
//
-// For best practices on deciding when to emit to a histogram and what form
-// the histogram should take, see
-// https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histograms/README.md
-
-// TODO(rkaplow): Link to proper documentation on metric creation once we have
-// it in a good state.
-
-// All of these macros must be called with |name| as a runtime constant - it
-// doesn't have to literally be a constant, but it must be the same string on
-// all calls from a particular call site. If this rule is violated, it is
-// possible the data will be written to the wrong histogram.
-
-//------------------------------------------------------------------------------
-// Enumeration histograms.
-
-// These macros create histograms for enumerated data. Ideally, the data should
-// be of the form of "event occurs, log the result". We recommended not putting
-// related but not directly connected data as enums within the same histogram.
-// You should be defining an associated Enum, and the input sample should be
-// an element of the Enum.
-// All of these macros must be called with |name| as a runtime constant.
-
-// Sample usage:
-// UMA_HISTOGRAM_ENUMERATION("My.Enumeration", VALUE, EVENT_MAX_VALUE);
-// New Enum values can be added, but existing enums must never be renumbered or
-// delete and reused. The value in |sample| must be strictly less than
-// |enum_max|.
-
-#define UMA_HISTOGRAM_ENUMERATION(name, sample, enum_max) \
- INTERNAL_HISTOGRAM_ENUMERATION_WITH_FLAG( \
- name, sample, enum_max, \
- base::HistogramBase::kUmaTargetedHistogramFlag)
-
-// Histogram for boolean values.
-
-// Sample usage:
-// UMA_HISTOGRAM_BOOLEAN("Histogram.Boolean", bool);
-#define UMA_HISTOGRAM_BOOLEAN(name, sample) \
- STATIC_HISTOGRAM_POINTER_BLOCK(name, AddBoolean(sample), \
- base::BooleanHistogram::FactoryGet(name, \
- base::HistogramBase::kUmaTargetedHistogramFlag))
+// UMA_HISTOGRAM_SPARSE_SLOWLY is defined in sparse_histogram.h as it has
+// different #include dependencies.
//------------------------------------------------------------------------------
-// Linear histograms.
-
-// All of these macros must be called with |name| as a runtime constant.
-
-// Used for capturing integer data with a linear bucketing scheme. This can be
-// used when you want the exact value of some small numeric count, with a max of
-// 100 or less. If you need to capture a range of greater than 100, we recommend
-// the use of the COUNT histograms below.
-
-// Sample usage:
-// UMA_HISTOGRAM_EXACT_LINEAR("Histogram.Linear", count, 10);
-#define UMA_HISTOGRAM_EXACT_LINEAR(name, sample, value_max) \
- UMA_HISTOGRAM_ENUMERATION(name, sample, value_max)
-
-// Used for capturing basic percentages. This will be 100 buckets of size 1.
+// Histograms are often put in areas where they are called many many times, and
+// performance is critical. As a result, they are designed to have a very low
+// recurring cost of executing (adding additional samples). Toward that end,
+// the macros declare a static pointer to the histogram in question, and only
+// take a "slow path" to construct (or find) the histogram on the first run
+// through the macro. We leak the histograms at shutdown time so that we don't
+// have to validate using the pointers at any time during the running of the
+// process.
+
+// The following code is generally what a thread-safe static pointer
+// initialization looks like for a histogram (after a macro is expanded). This
+// sample is an expansion (with comments) of the code for
+// LOCAL_HISTOGRAM_CUSTOM_COUNTS().
+
+/*
+ do {
+ // The pointer's presence indicates the initialization is complete.
+ // Initialization is idempotent, so it can safely be atomically repeated.
+ static base::subtle::AtomicWord atomic_histogram_pointer = 0;
+
+ // Acquire_Load() ensures that we acquire visibility to the pointed-to data
+ // in the histogram.
+ base::Histogram* histogram_pointer(reinterpret_cast<base::Histogram*>(
+ base::subtle::Acquire_Load(&atomic_histogram_pointer)));
+
+ if (!histogram_pointer) {
+ // This is the slow path, which will construct OR find the matching
+ // histogram. FactoryGet includes locks on a global histogram name map
+ // and is completely thread safe.
+ histogram_pointer = base::Histogram::FactoryGet(
+ name, min, max, bucket_count, base::HistogramBase::kNoFlags);
+
+ // Use Release_Store to ensure that the histogram data is made available
+ // globally before we make the pointer visible.
+ // Several threads may perform this store, but the same value will be
+ // stored in all cases (for a given named/spec'ed histogram).
+ // We could do this without any barrier, since FactoryGet entered and
+ // exited a lock after construction, but this barrier makes things clear.
+ base::subtle::Release_Store(&atomic_histogram_pointer,
+ reinterpret_cast<base::subtle::AtomicWord>(histogram_pointer));
+ }
+
+ // Ensure calling contract is upheld, and the name does NOT vary.
+ DCHECK(histogram_pointer->histogram_name() == constant_histogram_name);
+
+ histogram_pointer->Add(sample);
+ } while (0);
+*/
+
+// The above pattern is repeated in several macros. The only elements that
+// vary are the invocation of the Add(sample) vs AddTime(sample), and the choice
+// of which FactoryGet method to use. The different FactoryGet methods have
+// various argument lists, so the function with its argument list is provided as
+// a macro argument here. The name is only used in a DCHECK, to assure that
+// callers don't try to vary the name of the histogram (which would tend to be
+// ignored by the one-time initialization of the histogtram_pointer).
+
+// In some cases (integration into 3rd party code), it's useful to seperate the
+// definition of |atomic_histogram_poiner| from its use. To achieve this we
+// define HISTOGRAM_POINTER_USE, which uses an |atomic_histogram_pointer|, and
+// STATIC_HISTOGRAM_POINTER_BLOCK, which defines an |atomic_histogram_pointer|
+// and forwards to HISTOGRAM_POINTER_USE.
+#define HISTOGRAM_POINTER_USE(atomic_histogram_pointer, \
+ constant_histogram_name, \
+ histogram_add_method_invocation, \
+ histogram_factory_get_invocation) \
+ do { \
+ base::HistogramBase* histogram_pointer( \
+ reinterpret_cast<base::HistogramBase*>( \
+ base::subtle::Acquire_Load(atomic_histogram_pointer))); \
+ if (!histogram_pointer) { \
+ histogram_pointer = histogram_factory_get_invocation; \
+ base::subtle::Release_Store( \
+ atomic_histogram_pointer, \
+ reinterpret_cast<base::subtle::AtomicWord>(histogram_pointer)); \
+ } \
+ if (DCHECK_IS_ON()) \
+ histogram_pointer->CheckName(constant_histogram_name); \
+ histogram_pointer->histogram_add_method_invocation; \
+ } while (0)
-// Sample usage:
-// UMA_HISTOGRAM_PERCENTAGE("Histogram.Percent", percent_as_int);
-#define UMA_HISTOGRAM_PERCENTAGE(name, percent_as_int) \
- UMA_HISTOGRAM_ENUMERATION(name, percent_as_int, 101)
+// Defines the static |atomic_histogram_pointer| and forwards to
+// HISTOGRAM_POINTER_USE.
+#define STATIC_HISTOGRAM_POINTER_BLOCK(constant_histogram_name, \
+ histogram_add_method_invocation, \
+ histogram_factory_get_invocation) \
+ do { \
+ static base::subtle::AtomicWord atomic_histogram_pointer = 0; \
+ HISTOGRAM_POINTER_USE(&atomic_histogram_pointer, constant_histogram_name, \
+ histogram_add_method_invocation, \
+ histogram_factory_get_invocation); \
+ } while (0)
//------------------------------------------------------------------------------
-// Count histograms. These are used for collecting numeric data. Note that we
-// have macros for more specialized use cases below (memory, time, percentages).
-
-// The number suffixes here refer to the max size of the sample, i.e. COUNT_1000
-// will be able to collect samples of counts up to 1000. The default number of
-// buckets in all default macros is 50. We recommend erring on the side of too
-// large a range versus too short a range.
-// These macros default to exponential histograms - i.e. the lengths of the
-// bucket ranges exponentially increase as the sample range increases.
-// These should *not* be used if you are interested in exact counts, i.e. a
-// bucket range of 1. In these cases, you should use the ENUMERATION macros
-// defined later. These should also not be used to capture the number of some
-// event, i.e. "button X was clicked N times". In this cases, an enum should be
-// used, ideally with an appropriate baseline enum entry included.
-// All of these macros must be called with |name| as a runtime constant.
-
-// Sample usage:
-// UMA_HISTOGRAM_COUNTS_1M("My.Histogram", sample);
-
-#define UMA_HISTOGRAM_COUNTS_100(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
- name, sample, 1, 100, 50)
-
-#define UMA_HISTOGRAM_COUNTS_1000(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
- name, sample, 1, 1000, 50)
+// Provide easy general purpose histogram in a macro, just like stats counters.
+// Most of these macros use 50 buckets, but check the definition for details.
+//
+// All of these macros must be called with |name| as a runtime constant --- it
+// doesn't have to literally be a constant, but it must be the same string on
+// all calls from a particular call site. If this rule is violated,
+// STATIC_HISTOGRAM_POINTER_BLOCK will DCHECK, and if DCHECKS are disabled, the
+// data will be written to the wrong histogram.
-#define UMA_HISTOGRAM_COUNTS_10000(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
- name, sample, 1, 10000, 50)
+#define LOCAL_HISTOGRAM_TIMES(name, sample) LOCAL_HISTOGRAM_CUSTOM_TIMES( \
+ name, sample, base::TimeDelta::FromMilliseconds(1), \
+ base::TimeDelta::FromSeconds(10), 50)
-#define UMA_HISTOGRAM_COUNTS_100000(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
- name, sample, 1, 100000, 50)
+// For folks that need real specific times, use this to select a precise range
+// of times you want plotted, and the number of buckets you want used.
+#define LOCAL_HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \
+ STATIC_HISTOGRAM_POINTER_BLOCK(name, AddTime(sample), \
+ base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \
+ base::HistogramBase::kNoFlags))
-#define UMA_HISTOGRAM_COUNTS_1M(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
+#define LOCAL_HISTOGRAM_COUNTS(name, sample) LOCAL_HISTOGRAM_CUSTOM_COUNTS( \
name, sample, 1, 1000000, 50)
-#define UMA_HISTOGRAM_COUNTS_10M(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
- name, sample, 1, 10000000, 50)
-
-// This can be used when the default ranges are not sufficient. This macro lets
-// the metric developer customize the min and max of the sampled range, as well
-// as the number of buckets recorded.
-// Any data outside the range here will be put in underflow and overflow
-// buckets. Min values should be >=1 as emitted 0s will still go into the
-// underflow bucket.
-
-// Sample usage:
-// UMA_HISTOGRAM_CUSTOM_COUNTS("My.Histogram", 1, 100000000, 100);
-#define UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \
- INTERNAL_HISTOGRAM_CUSTOM_COUNTS_WITH_FLAG( \
- name, sample, min, max, bucket_count, \
- base::HistogramBase::kUmaTargetedHistogramFlag)
+#define LOCAL_HISTOGRAM_COUNTS_100(name, sample) \
+ LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 100, 50)
+
+#define LOCAL_HISTOGRAM_COUNTS_10000(name, sample) \
+ LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 10000, 50)
+
+#define LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \
+ STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \
+ base::Histogram::FactoryGet(name, min, max, bucket_count, \
+ base::HistogramBase::kNoFlags))
+
+// This is a helper macro used by other macros and shouldn't be used directly.
+#define HISTOGRAM_ENUMERATION_WITH_FLAG(name, sample, boundary, flag) \
+ STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \
+ base::LinearHistogram::FactoryGet(name, 1, boundary, boundary + 1, \
+ flag))
+
+#define LOCAL_HISTOGRAM_PERCENTAGE(name, under_one_hundred) \
+ LOCAL_HISTOGRAM_ENUMERATION(name, under_one_hundred, 101)
+
+#define LOCAL_HISTOGRAM_BOOLEAN(name, sample) \
+ STATIC_HISTOGRAM_POINTER_BLOCK(name, AddBoolean(sample), \
+ base::BooleanHistogram::FactoryGet(name, base::Histogram::kNoFlags))
+
+// Support histograming of an enumerated value. The samples should always be
+// strictly less than |boundary_value| -- this prevents you from running into
+// problems down the line if you add additional buckets to the histogram. Note
+// also that, despite explicitly setting the minimum bucket value to |1| below,
+// it is fine for enumerated histograms to be 0-indexed -- this is because
+// enumerated histograms should never have underflow.
+#define LOCAL_HISTOGRAM_ENUMERATION(name, sample, boundary_value) \
+ STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \
+ base::LinearHistogram::FactoryGet(name, 1, boundary_value, \
+ boundary_value + 1, base::HistogramBase::kNoFlags))
+
+// Support histograming of an enumerated value. Samples should be one of the
+// std::vector<int> list provided via |custom_ranges|. See comments above
+// CustomRanges::FactoryGet about the requirement of |custom_ranges|.
+// You can use the helper function CustomHistogram::ArrayToCustomRanges to
+// transform a C-style array of valid sample values to a std::vector<int>.
+#define LOCAL_HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) \
+ STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \
+ base::CustomHistogram::FactoryGet(name, custom_ranges, \
+ base::HistogramBase::kNoFlags))
+
+#define LOCAL_HISTOGRAM_MEMORY_KB(name, sample) LOCAL_HISTOGRAM_CUSTOM_COUNTS( \
+ name, sample, 1000, 500000, 50)
//------------------------------------------------------------------------------
-// Timing histograms. These are used for collecting timing data (generally
-// latencies).
-
-// These macros create exponentially sized histograms (lengths of the bucket
-// ranges exponentially increase as the sample range increases). The input
-// sample is a base::TimeDelta. The output data is measured in ms granularity.
-// All of these macros must be called with |name| as a runtime constant.
+// The following macros provide typical usage scenarios for callers that wish
+// to record histogram data, and have the data submitted/uploaded via UMA.
+// Not all systems support such UMA, but if they do, the following macros
+// should work with the service.
-// Sample usage:
-// UMA_HISTOGRAM_TIMES("My.Timing.Histogram", time_delta);
-
-// Short timings - up to 10 seconds.
-#define UMA_HISTOGRAM_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \
- name, sample, base::TimeDelta::FromMilliseconds(1), \
+#define UMA_HISTOGRAM_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \
+ name, sample, base::TimeDelta::FromMilliseconds(1), \
base::TimeDelta::FromSeconds(10), 50)
-// Medium timings - up to 3 minutes. Note this starts at 10ms (no good reason,
-// but not worth changing).
-#define UMA_HISTOGRAM_MEDIUM_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \
- name, sample, base::TimeDelta::FromMilliseconds(10), \
+#define UMA_HISTOGRAM_MEDIUM_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \
+ name, sample, base::TimeDelta::FromMilliseconds(10), \
base::TimeDelta::FromMinutes(3), 50)
-// Long timings - up to an hour.
-#define UMA_HISTOGRAM_LONG_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \
- name, sample, base::TimeDelta::FromMilliseconds(1), \
+// Use this macro when times can routinely be much longer than 10 seconds.
+#define UMA_HISTOGRAM_LONG_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \
+ name, sample, base::TimeDelta::FromMilliseconds(1), \
base::TimeDelta::FromHours(1), 50)
-// Long timings with higher granularity - up to an hour with 100 buckets.
+// Use this macro when times can routinely be much longer than 10 seconds and
+// you want 100 buckets.
#define UMA_HISTOGRAM_LONG_TIMES_100(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \
- name, sample, base::TimeDelta::FromMilliseconds(1), \
+ name, sample, base::TimeDelta::FromMilliseconds(1), \
base::TimeDelta::FromHours(1), 100)
-// This can be used when the default ranges are not sufficient. This macro lets
-// the metric developer customize the min and max of the sampled range, as well
-// as the number of buckets recorded.
-
-// Sample usage:
-// UMA_HISTOGRAM_CUSTOM_TIMES("Very.Long.Timing.Histogram", duration_in_ms,
-// base::TimeDelta::FromSeconds(1), base::TimeDelta::FromDays(1), 100);
-#define UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \
- STATIC_HISTOGRAM_POINTER_BLOCK(name, AddTime(sample), \
- base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \
+#define UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \
+ STATIC_HISTOGRAM_POINTER_BLOCK(name, AddTime(sample), \
+ base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \
base::HistogramBase::kUmaTargetedHistogramFlag))
-// Scoped class which logs its time on this earth as a UMA statistic. This is
-// recommended for when you want a histogram which measures the time it takes
-// for a method to execute. This measures up to 10 seconds. It uses
-// UMA_HISTOGRAM_TIMES under the hood.
-
-// Sample usage:
-// void Function() {
-// SCOPED_UMA_HISTOGRAM_TIMER("Component.FunctionTime");
-// ...
-// }
-#define SCOPED_UMA_HISTOGRAM_TIMER(name) \
- INTERNAL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(name, false, __COUNTER__)
+#define UMA_HISTOGRAM_COUNTS(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
+ name, sample, 1, 1000000, 50)
-// Similar scoped histogram timer, but this uses UMA_HISTOGRAM_LONG_TIMES_100,
-// which measures up to an hour, and uses 100 buckets. This is more expensive
-// to store, so only use if this often takes >10 seconds.
-#define SCOPED_UMA_HISTOGRAM_LONG_TIMER(name) \
- INTERNAL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(name, true, __COUNTER__)
+#define UMA_HISTOGRAM_COUNTS_100(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
+ name, sample, 1, 100, 50)
+#define UMA_HISTOGRAM_COUNTS_1000(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
+ name, sample, 1, 1000, 50)
-//------------------------------------------------------------------------------
-// Memory histograms.
+#define UMA_HISTOGRAM_COUNTS_10000(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
+ name, sample, 1, 10000, 50)
-// These macros create exponentially sized histograms (lengths of the bucket
-// ranges exponentially increase as the sample range increases). The input
-// sample must be a number measured in kilobytes.
-// All of these macros must be called with |name| as a runtime constant.
+#define UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \
+ STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \
+ base::Histogram::FactoryGet(name, min, max, bucket_count, \
+ base::HistogramBase::kUmaTargetedHistogramFlag))
-// Sample usage:
-// UMA_HISTOGRAM_MEMORY_KB("My.Memory.Histogram", memory_in_kb);
+#define UMA_HISTOGRAM_MEMORY_KB(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
+ name, sample, 1000, 500000, 50)
-// Used to measure common KB-granularity memory stats. Range is up to 500000KB -
-// approximately 500M.
-#define UMA_HISTOGRAM_MEMORY_KB(name, sample) \
- UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1000, 500000, 50)
+#define UMA_HISTOGRAM_MEMORY_MB(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
+ name, sample, 1, 1000, 50)
-// Used to measure common MB-granularity memory stats. Range is up to ~64G.
-#define UMA_HISTOGRAM_MEMORY_LARGE_MB(name, sample) \
+#define UMA_HISTOGRAM_MEMORY_LARGE_MB(name, sample) \
UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 64000, 100)
+#define UMA_HISTOGRAM_PERCENTAGE(name, under_one_hundred) \
+ UMA_HISTOGRAM_ENUMERATION(name, under_one_hundred, 101)
-//------------------------------------------------------------------------------
-// Stability-specific histograms.
-
-// Histograms logged in as stability histograms will be included in the initial
-// stability log. See comments by declaration of
-// MetricsService::PrepareInitialStabilityLog().
-// All of these macros must be called with |name| as a runtime constant.
-
-// For details on usage, see the documentation on the non-stability equivalents.
-
-#define UMA_STABILITY_HISTOGRAM_COUNTS_100(name, sample) \
- UMA_STABILITY_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 100, 50)
+#define UMA_HISTOGRAM_BOOLEAN(name, sample) \
+ STATIC_HISTOGRAM_POINTER_BLOCK(name, AddBoolean(sample), \
+ base::BooleanHistogram::FactoryGet(name, \
+ base::HistogramBase::kUmaTargetedHistogramFlag))
-#define UMA_STABILITY_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, \
- bucket_count) \
- INTERNAL_HISTOGRAM_CUSTOM_COUNTS_WITH_FLAG( \
- name, sample, min, max, bucket_count, \
- base::HistogramBase::kUmaStabilityHistogramFlag)
+// The samples should always be strictly less than |boundary_value|. For more
+// details, see the comment for the |LOCAL_HISTOGRAM_ENUMERATION| macro, above.
+#define UMA_HISTOGRAM_ENUMERATION(name, sample, boundary_value) \
+ HISTOGRAM_ENUMERATION_WITH_FLAG(name, sample, boundary_value, \
+ base::HistogramBase::kUmaTargetedHistogramFlag)
-#define UMA_STABILITY_HISTOGRAM_ENUMERATION(name, sample, enum_max) \
- INTERNAL_HISTOGRAM_ENUMERATION_WITH_FLAG( \
- name, sample, enum_max, \
+// Similar to UMA_HISTOGRAM_ENUMERATION, but used for recording stability
+// histograms. Use this if recording a histogram that should be part of the
+// initial stability log.
+#define UMA_STABILITY_HISTOGRAM_ENUMERATION(name, sample, boundary_value) \
+ HISTOGRAM_ENUMERATION_WITH_FLAG(name, sample, boundary_value, \
base::HistogramBase::kUmaStabilityHistogramFlag)
-//------------------------------------------------------------------------------
-// Sparse histograms.
-
-// Sparse histograms are well suited for recording counts of exact sample values
-// that are sparsely distributed over a large range.
-//
-// UMA_HISTOGRAM_SPARSE_SLOWLY is good for sparsely distributed and/or
-// infrequently recorded values since the implementation is slower
-// and takes more memory.
-//
-// For instance, Sqlite.Version.* are sparse because for any given database,
-// there's going to be exactly one version logged.
-// The |sample| can be a negative or non-negative number.
-#define UMA_HISTOGRAM_SPARSE_SLOWLY(name, sample) \
- INTERNAL_HISTOGRAM_SPARSE_SLOWLY(name, sample)
-
-//------------------------------------------------------------------------------
-// Histogram instantiation helpers.
-
-// Support a collection of histograms, perhaps one for each entry in an
-// enumeration. This macro manages a block of pointers, adding to a specific
-// one by its index.
-//
-// A typical instantiation looks something like this:
-// STATIC_HISTOGRAM_POINTER_GROUP(
-// GetHistogramNameForIndex(histogram_index),
-// histogram_index, MAXIMUM_HISTOGRAM_INDEX, Add(some_delta),
-// base::Histogram::FactoryGet(
-// GetHistogramNameForIndex(histogram_index),
-// MINIMUM_SAMPLE, MAXIMUM_SAMPLE, BUCKET_COUNT,
-// base::HistogramBase::kUmaTargetedHistogramFlag));
-//
-// Though it seems inefficient to generate the name twice, the first
-// instance will be used only for DCHECK builds and the second will
-// execute only during the first access to the given index, after which
-// the pointer is cached and the name never needed again.
-#define STATIC_HISTOGRAM_POINTER_GROUP(constant_histogram_name, index, \
- constant_maximum, \
- histogram_add_method_invocation, \
- histogram_factory_get_invocation) \
- do { \
- static base::subtle::AtomicWord atomic_histograms[constant_maximum]; \
- DCHECK_LE(0, index); \
- DCHECK_LT(index, constant_maximum); \
- HISTOGRAM_POINTER_USE(&atomic_histograms[index], constant_histogram_name, \
- histogram_add_method_invocation, \
- histogram_factory_get_invocation); \
- } while (0)
-
-//------------------------------------------------------------------------------
-// Deprecated histogram macros. Not recommended for current use.
+#define UMA_HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) \
+ STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \
+ base::CustomHistogram::FactoryGet(name, custom_ranges, \
+ base::HistogramBase::kUmaTargetedHistogramFlag))
-// Legacy name for UMA_HISTOGRAM_COUNTS_1M. Suggest using explicit naming
-// and not using this macro going forward.
-#define UMA_HISTOGRAM_COUNTS(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
- name, sample, 1, 1000000, 50)
+// Scoped class which logs its time on this earth as a UMA statistic. This is
+// recommended for when you want a histogram which measures the time it takes
+// for a method to execute. This measures up to 10 seconds.
+#define SCOPED_UMA_HISTOGRAM_TIMER(name) \
+ SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(name, false, __COUNTER__)
-// MB-granularity memory metric. This has a short max (1G).
-#define UMA_HISTOGRAM_MEMORY_MB(name, sample) \
- UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 1000, 50)
-
-// For an enum with customized range. In general, sparse histograms should be
-// used instead.
-// Samples should be one of the std::vector<int> list provided via
-// |custom_ranges|. See comments above CustomRanges::FactoryGet about the
-// requirement of |custom_ranges|. You can use the helper function
-// CustomHistogram::ArrayToCustomRanges to transform a C-style array of valid
-// sample values to a std::vector<int>.
-#define UMA_HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) \
- STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \
- base::CustomHistogram::FactoryGet(name, custom_ranges, \
- base::HistogramBase::kUmaTargetedHistogramFlag))
+// Similar scoped histogram timer, but this uses UMA_HISTOGRAM_LONG_TIMES_100,
+// which measures up to an hour, and uses 100 buckets. This is more expensive
+// to store, so only use if this often takes >10 seconds.
+#define SCOPED_UMA_HISTOGRAM_LONG_TIMER(name) \
+ SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(name, true, __COUNTER__)
+
+// This nested macro is necessary to expand __COUNTER__ to an actual value.
+#define SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(name, is_long, key) \
+ SCOPED_UMA_HISTOGRAM_TIMER_UNIQUE(name, is_long, key)
+
+#define SCOPED_UMA_HISTOGRAM_TIMER_UNIQUE(name, is_long, key) \
+ class ScopedHistogramTimer##key { \
+ public: \
+ ScopedHistogramTimer##key() : constructed_(base::TimeTicks::Now()) {} \
+ ~ScopedHistogramTimer##key() { \
+ base::TimeDelta elapsed = base::TimeTicks::Now() - constructed_; \
+ if (is_long) { \
+ UMA_HISTOGRAM_LONG_TIMES_100(name, elapsed); \
+ } else { \
+ UMA_HISTOGRAM_TIMES(name, elapsed); \
+ } \
+ } \
+ private: \
+ base::TimeTicks constructed_; \
+ } scoped_histogram_timer_##key
#endif // BASE_METRICS_HISTOGRAM_MACROS_H_
diff --git a/base/metrics/histogram_macros_internal.h b/base/metrics/histogram_macros_internal.h
deleted file mode 100644
index 53e4f11b75..0000000000
--- a/base/metrics/histogram_macros_internal.h
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright 2016 The Chromium 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_METRICS_HISTOGRAM_MACROS_INTERNAL_H_
-#define BASE_METRICS_HISTOGRAM_MACROS_INTERNAL_H_
-
-#include "base/atomicops.h"
-#include "base/logging.h"
-#include "base/metrics/histogram.h"
-#include "base/metrics/sparse_histogram.h"
-#include "base/time/time.h"
-
-// This is for macros internal to base/metrics. They should not be used outside
-// of this directory. For writing to UMA histograms, see histogram_macros.h.
-
-// TODO(rkaplow): Improve commenting of these methods.
-
-//------------------------------------------------------------------------------
-// Histograms are often put in areas where they are called many many times, and
-// performance is critical. As a result, they are designed to have a very low
-// recurring cost of executing (adding additional samples). Toward that end,
-// the macros declare a static pointer to the histogram in question, and only
-// take a "slow path" to construct (or find) the histogram on the first run
-// through the macro. We leak the histograms at shutdown time so that we don't
-// have to validate using the pointers at any time during the running of the
-// process.
-
-
-// In some cases (integration into 3rd party code), it's useful to separate the
-// definition of |atomic_histogram_pointer| from its use. To achieve this we
-// define HISTOGRAM_POINTER_USE, which uses an |atomic_histogram_pointer|, and
-// STATIC_HISTOGRAM_POINTER_BLOCK, which defines an |atomic_histogram_pointer|
-// and forwards to HISTOGRAM_POINTER_USE.
-#define HISTOGRAM_POINTER_USE(atomic_histogram_pointer, \
- constant_histogram_name, \
- histogram_add_method_invocation, \
- histogram_factory_get_invocation) \
- do { \
- /* \
- * Acquire_Load() ensures that we acquire visibility to the \
- * pointed-to data in the histogram. \
- */ \
- base::HistogramBase* histogram_pointer( \
- reinterpret_cast<base::HistogramBase*>( \
- base::subtle::Acquire_Load(atomic_histogram_pointer))); \
- if (!histogram_pointer) { \
- /* \
- * This is the slow path, which will construct OR find the \
- * matching histogram. histogram_factory_get_invocation includes \
- * locks on a global histogram name map and is completely thread \
- * safe. \
- */ \
- histogram_pointer = histogram_factory_get_invocation; \
- \
- /* \
- * Use Release_Store to ensure that the histogram data is made \
- * available globally before we make the pointer visible. Several \
- * threads may perform this store, but the same value will be \
- * stored in all cases (for a given named/spec'ed histogram). \
- * We could do this without any barrier, since FactoryGet entered \
- * and exited a lock after construction, but this barrier makes \
- * things clear. \
- */ \
- base::subtle::Release_Store( \
- atomic_histogram_pointer, \
- reinterpret_cast<base::subtle::AtomicWord>(histogram_pointer)); \
- } \
- if (DCHECK_IS_ON()) \
- histogram_pointer->CheckName(constant_histogram_name); \
- histogram_pointer->histogram_add_method_invocation; \
- } while (0)
-
-// This is a helper macro used by other macros and shouldn't be used directly.
-// Defines the static |atomic_histogram_pointer| and forwards to
-// HISTOGRAM_POINTER_USE.
-#define STATIC_HISTOGRAM_POINTER_BLOCK(constant_histogram_name, \
- histogram_add_method_invocation, \
- histogram_factory_get_invocation) \
- do { \
- /* \
- * The pointer's presence indicates that the initialization is complete. \
- * Initialization is idempotent, so it can safely be atomically repeated. \
- */ \
- static base::subtle::AtomicWord atomic_histogram_pointer = 0; \
- HISTOGRAM_POINTER_USE(&atomic_histogram_pointer, constant_histogram_name, \
- histogram_add_method_invocation, \
- histogram_factory_get_invocation); \
- } while (0)
-
-// This is a helper macro used by other macros and shouldn't be used directly.
-#define INTERNAL_HISTOGRAM_CUSTOM_COUNTS_WITH_FLAG(name, sample, min, max, \
- bucket_count, flag) \
- STATIC_HISTOGRAM_POINTER_BLOCK( \
- name, Add(sample), \
- base::Histogram::FactoryGet(name, min, max, bucket_count, flag))
-
-// This is a helper macro used by other macros and shouldn't be used directly.
-// For an enumeration with N items, recording values in the range [0, N - 1],
-// this macro creates a linear histogram with N + 1 buckets:
-// [0, 1), [1, 2), ..., [N - 1, N), and an overflow bucket [N, infinity).
-// Code should never emit to the overflow bucket; only to the other N buckets.
-// This allows future versions of Chrome to safely append new entries to the
-// enumeration. Otherwise, the histogram would have [N - 1, infinity) as its
-// overflow bucket, and so the maximal value (N - 1) would be emitted to this
-// overflow bucket. But, if an additional enumerated value were later added, the
-// bucket label for the value (N - 1) would change to [N - 1, N), which would
-// result in different versions of Chrome using different bucket labels for
-// identical data.
-#define INTERNAL_HISTOGRAM_ENUMERATION_WITH_FLAG(name, sample, boundary, flag) \
- do { \
- static_assert( \
- !std::is_enum<decltype(sample)>::value || \
- !std::is_enum<decltype(boundary)>::value || \
- std::is_same<std::remove_const<decltype(sample)>::type, \
- std::remove_const<decltype(boundary)>::type>::value, \
- "|sample| and |boundary| shouldn't be of different enums"); \
- STATIC_HISTOGRAM_POINTER_BLOCK( \
- name, Add(sample), base::LinearHistogram::FactoryGet( \
- name, 1, boundary, boundary + 1, flag)); \
- } while (0)
-
-// This is a helper macro used by other macros and shouldn't be used directly.
-// This is necessary to expand __COUNTER__ to an actual value.
-#define INTERNAL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(name, is_long, key) \
- INTERNAL_SCOPED_UMA_HISTOGRAM_TIMER_UNIQUE(name, is_long, key)
-
-// This is a helper macro used by other macros and shouldn't be used directly.
-#define INTERNAL_SCOPED_UMA_HISTOGRAM_TIMER_UNIQUE(name, is_long, key) \
- class ScopedHistogramTimer##key { \
- public: \
- ScopedHistogramTimer##key() : constructed_(base::TimeTicks::Now()) {} \
- ~ScopedHistogramTimer##key() { \
- base::TimeDelta elapsed = base::TimeTicks::Now() - constructed_; \
- if (is_long) { \
- UMA_HISTOGRAM_LONG_TIMES_100(name, elapsed); \
- } else { \
- UMA_HISTOGRAM_TIMES(name, elapsed); \
- } \
- } \
- private: \
- base::TimeTicks constructed_; \
- } scoped_histogram_timer_##key
-
-// Macro for sparse histogram.
-// The implementation is more costly to add values to, and each value
-// stored has more overhead, compared to the other histogram types. However it
-// may be more efficient in memory if the total number of sample values is small
-// compared to the range of their values.
-#define INTERNAL_HISTOGRAM_SPARSE_SLOWLY(name, sample) \
- do { \
- base::HistogramBase* histogram = base::SparseHistogram::FactoryGet( \
- name, base::HistogramBase::kUmaTargetedHistogramFlag); \
- histogram->Add(sample); \
- } while (0)
-
-#endif // BASE_METRICS_HISTOGRAM_MACROS_INTERNAL_H_
diff --git a/base/metrics/histogram_macros_local.h b/base/metrics/histogram_macros_local.h
deleted file mode 100644
index 7571a9c4ad..0000000000
--- a/base/metrics/histogram_macros_local.h
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2016 The Chromium 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_METRICS_HISTOGRAM_MACROS_LOCAL_H_
-#define BASE_METRICS_HISTOGRAM_MACROS_LOCAL_H_
-
-#include "base/logging.h"
-#include "base/metrics/histogram.h"
-#include "base/metrics/histogram_macros_internal.h"
-#include "base/time/time.h"
-
-// TODO(rkaplow): Migrate all LOCAL_* usage within Chromium to include this
-// file instead of the histogram_macros.h file.
-
-//------------------------------------------------------------------------------
-// Enumeration histograms.
-//
-// For usage details, see the equivalents in histogram_macros.h.
-
-#define LOCAL_HISTOGRAM_ENUMERATION(name, sample, enum_max) \
- INTERNAL_HISTOGRAM_ENUMERATION_WITH_FLAG( \
- name, sample, enum_max, \
- base::HistogramBase::kNoFlags)
-
-#define LOCAL_HISTOGRAM_BOOLEAN(name, sample) \
- STATIC_HISTOGRAM_POINTER_BLOCK(name, AddBoolean(sample), \
- base::BooleanHistogram::FactoryGet(name, base::Histogram::kNoFlags))
-
-//------------------------------------------------------------------------------
-// Percentage histograms.
-//
-// For usage details, see the equivalents in histogram_macros.h
-
-#define LOCAL_HISTOGRAM_PERCENTAGE(name, under_one_hundred) \
- LOCAL_HISTOGRAM_ENUMERATION(name, under_one_hundred, 101)
-
-//------------------------------------------------------------------------------
-// Count histograms. These are used for collecting numeric data. Note that we
-// have macros for more specialized use cases below (memory, time, percentages).
-// For usage details, see the equivalents in histogram_macros.h.
-
-#define LOCAL_HISTOGRAM_COUNTS_100(name, sample) \
- LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 100, 50)
-
-#define LOCAL_HISTOGRAM_COUNTS_10000(name, sample) \
- LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 10000, 50)
-
-#define LOCAL_HISTOGRAM_COUNTS_1000000(name, sample) \
- LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 1000000, 50)
-
-#define LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \
- INTERNAL_HISTOGRAM_CUSTOM_COUNTS_WITH_FLAG( \
- name, sample, min, max, bucket_count, base::HistogramBase::kNoFlags)
-
-//------------------------------------------------------------------------------
-// Timing histograms. These are used for collecting timing data (generally
-// latencies).
-//
-// For usage details, see the equivalents in histogram_macros.h.
-
-#define LOCAL_HISTOGRAM_TIMES(name, sample) LOCAL_HISTOGRAM_CUSTOM_TIMES( \
- name, sample, base::TimeDelta::FromMilliseconds(1), \
- base::TimeDelta::FromSeconds(10), 50)
-
-#define LOCAL_HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \
- STATIC_HISTOGRAM_POINTER_BLOCK(name, AddTime(sample), \
- base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \
- base::HistogramBase::kNoFlags))
-
-//------------------------------------------------------------------------------
-// Memory histograms.
-//
-// For usage details, see the equivalents in histogram_macros.h.
-
-#define LOCAL_HISTOGRAM_MEMORY_KB(name, sample) LOCAL_HISTOGRAM_CUSTOM_COUNTS( \
- name, sample, 1000, 500000, 50)
-
-//------------------------------------------------------------------------------
-// Deprecated histograms. Not recommended for current use.
-
-// TODO(rkaplow): See if we can clean up this macro and usage.
-// Legacy non-explicit version. We suggest using LOCAL_HISTOGRAM_COUNTS_1000000
-// instead.
-#define LOCAL_HISTOGRAM_COUNTS(name, sample) \
- LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 1000000, 50)
-
-#endif // BASE_METRICS_HISTOGRAM_MACROS_LOCAL_H_
diff --git a/base/metrics/histogram_samples.h b/base/metrics/histogram_samples.h
index 93f6d21c8a..e28573fa7e 100644
--- a/base/metrics/histogram_samples.h
+++ b/base/metrics/histogram_samples.h
@@ -27,9 +27,6 @@ class SampleCountIterator;
class BASE_EXPORT HistogramSamples {
public:
struct Metadata {
- // Expected size for 32/64-bit check.
- static constexpr size_t kExpectedInstanceSize = 24;
-
// Initialized when the sample-set is first created with a value provided
// by the caller. It is generally used to identify the sample-set across
// threads and processes, though not necessarily uniquely as it is possible
@@ -58,21 +55,7 @@ class BASE_EXPORT HistogramSamples {
// might mismatch even when no memory corruption has happened.
HistogramBase::AtomicCount redundant_count;
- // 4 bytes of padding to explicitly extend this structure to a multiple of
- // 64-bits. This is required to ensure the structure is the same size on
- // both 32-bit and 64-bit builds.
- char padding[4];
- };
-
- // Because sturctures held in persistent memory must be POD, there can be no
- // default constructor to clear the fields. This derived class exists just
- // to clear them when being allocated on the heap.
- struct LocalMetadata : Metadata {
- LocalMetadata() {
- id = 0;
- sum = 0;
- redundant_count = 0;
- }
+ Metadata() : id(0), sum(0), redundant_count(0) {}
};
explicit HistogramSamples(uint64_t id);
@@ -119,7 +102,7 @@ class BASE_EXPORT HistogramSamples {
// In order to support histograms shared through an external memory segment,
// meta values may be the local storage or external storage depending on the
// wishes of the derived class.
- LocalMetadata local_meta_;
+ Metadata local_meta_;
Metadata* meta_;
DISALLOW_COPY_AND_ASSIGN(HistogramSamples);
diff --git a/base/metrics/histogram_snapshot_manager.cc b/base/metrics/histogram_snapshot_manager.cc
index a774ea6177..340505e519 100644
--- a/base/metrics/histogram_snapshot_manager.cc
+++ b/base/metrics/histogram_snapshot_manager.cc
@@ -53,8 +53,6 @@ void HistogramSnapshotManager::PrepareSamples(
for (size_t i = 0; i < ranges->size(); ++i)
ranges_copy.push_back(ranges->range(i));
HistogramBase::Sample* ranges_ptr = &ranges_copy[0];
- uint32_t ranges_checksum = ranges->checksum();
- uint32_t ranges_calc_checksum = ranges->CalculateChecksum();
const char* histogram_name = histogram->histogram_name().c_str();
int32_t flags = histogram->flags();
// The checksum should have caught this, so crash separately if it didn't.
@@ -63,8 +61,6 @@ void HistogramSnapshotManager::PrepareSamples(
// Ensure that compiler keeps around pointers to |histogram| and its
// internal |bucket_ranges_| for any minidumps.
base::debug::Alias(&ranges_ptr);
- base::debug::Alias(&ranges_checksum);
- base::debug::Alias(&ranges_calc_checksum);
base::debug::Alias(&histogram_name);
base::debug::Alias(&flags);
}
diff --git a/base/metrics/histogram_snapshot_manager.h b/base/metrics/histogram_snapshot_manager.h
index de4a2e195a..26fb93fd20 100644
--- a/base/metrics/histogram_snapshot_manager.h
+++ b/base/metrics/histogram_snapshot_manager.h
@@ -24,7 +24,7 @@ class HistogramFlattener;
// histograms for recording either to disk or for transmission (such as from
// renderer to browser, or from browser to UMA upload). Since histograms can sit
// in memory for an extended period of time, and are vulnerable to memory
-// corruption, this class also validates as much redundancy as it can before
+// corruption, this class also validates as much rendundancy as it can before
// calling for the marginal change (a.k.a., delta) in a histogram to be
// recorded.
class BASE_EXPORT HistogramSnapshotManager {
diff --git a/base/metrics/histogram_unittest.cc b/base/metrics/histogram_unittest.cc
index 02ed93ba0e..5c2ca6883a 100644
--- a/base/metrics/histogram_unittest.cc
+++ b/base/metrics/histogram_unittest.cc
@@ -22,7 +22,6 @@
#include "base/metrics/statistics_recorder.h"
#include "base/pickle.h"
#include "base/strings/stringprintf.h"
-#include "base/test/gtest_util.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -702,6 +701,7 @@ TEST_P(HistogramTest, FactoryTime) {
<< "ns each.";
}
+#if GTEST_HAS_DEATH_TEST
// For Histogram, LinearHistogram and CustomHistogram, the minimum for a
// declared range is 1, while the maximum is (HistogramBase::kSampleType_MAX -
// 1). But we accept ranges exceeding those limits, and silently clamped to
@@ -735,18 +735,17 @@ TEST(HistogramDeathTest, BadRangesTest) {
// CustomHistogram does not accepts kSampleType_MAX as range.
custom_ranges.push_back(HistogramBase::kSampleType_MAX);
- EXPECT_DEATH_IF_SUPPORTED(
- CustomHistogram::FactoryGet("BadRangesCustom2", custom_ranges,
- HistogramBase::kNoFlags),
+ EXPECT_DEATH(CustomHistogram::FactoryGet("BadRangesCustom2", custom_ranges,
+ HistogramBase::kNoFlags),
"");
// CustomHistogram needs at least 1 valid range.
custom_ranges.clear();
custom_ranges.push_back(0);
- EXPECT_DEATH_IF_SUPPORTED(
- CustomHistogram::FactoryGet("BadRangesCustom3", custom_ranges,
- HistogramBase::kNoFlags),
+ EXPECT_DEATH(CustomHistogram::FactoryGet("BadRangesCustom3", custom_ranges,
+ HistogramBase::kNoFlags),
"");
}
+#endif
} // namespace base
diff --git a/base/metrics/persistent_histogram_allocator.cc b/base/metrics/persistent_histogram_allocator.cc
index 29910036c7..5af3486645 100644
--- a/base/metrics/persistent_histogram_allocator.cc
+++ b/base/metrics/persistent_histogram_allocator.cc
@@ -6,7 +6,6 @@
#include <memory>
-#include "base/atomicops.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/important_file_writer.h"
@@ -36,6 +35,7 @@ const char kResultHistogram[] = "UMA.CreatePersistentHistogram.Result";
// so that, if the structure of that object changes, stored older versions
// will be safely ignored.
enum : uint32_t {
+ kTypeIdHistogram = 0xF1645910 + 2, // SHA1(Histogram) v2
kTypeIdRangesArray = 0xBCEA225A + 1, // SHA1(RangesArray) v1
kTypeIdCountsArray = 0x53215530 + 1, // SHA1(CountsArray) v1
};
@@ -45,10 +45,8 @@ enum : uint32_t {
// but that's best since PersistentMemoryAllocator objects (that underlie
// GlobalHistogramAllocator objects) are explicitly forbidden from doing
// anything essential at exit anyway due to the fact that they depend on data
-// managed elsewhere and which could be destructed first. An AtomicWord is
-// used instead of std::atomic because the latter can create global ctors
-// and dtors.
-subtle::AtomicWord g_allocator = 0;
+// managed elsewhere and which could be destructed first.
+GlobalHistogramAllocator* g_allocator = nullptr;
// Take an array of range boundaries and create a proper BucketRanges object
// which is returned to the caller. A return of nullptr indicates that the
@@ -119,7 +117,7 @@ PersistentSparseHistogramDataManager::GetSampleMapRecordsWhileLocked(
return found->second.get();
std::unique_ptr<PersistentSampleMapRecords>& samples = sample_records_[id];
- samples = MakeUnique<PersistentSampleMapRecords>(this, id);
+ samples = WrapUnique(new PersistentSampleMapRecords(this, id));
return samples.get();
}
@@ -226,13 +224,6 @@ PersistentMemoryAllocator::Reference PersistentSampleMapRecords::CreateNew(
// This data will be held in persistent memory in order for processes to
// locate and use histograms created elsewhere.
struct PersistentHistogramAllocator::PersistentHistogramData {
- // SHA1(Histogram): Increment this if structure changes!
- static constexpr uint32_t kPersistentTypeId = 0xF1645910 + 3;
-
- // Expected size for 32/64-bit check.
- static constexpr size_t kExpectedInstanceSize =
- 40 + 2 * HistogramSamples::Metadata::kExpectedInstanceSize;
-
int32_t histogram_type;
int32_t flags;
int32_t minimum;
@@ -247,7 +238,7 @@ struct PersistentHistogramAllocator::PersistentHistogramData {
// Space for the histogram name will be added during the actual allocation
// request. This must be the last field of the structure. A zero-size array
// or a "flexible" array would be preferred but is not (yet) valid C++.
- char name[sizeof(uint64_t)]; // Force 64-bit alignment on 32-bit builds.
+ char name[1];
};
PersistentHistogramAllocator::Iterator::Iterator(
@@ -257,7 +248,7 @@ PersistentHistogramAllocator::Iterator::Iterator(
std::unique_ptr<HistogramBase>
PersistentHistogramAllocator::Iterator::GetNextWithIgnore(Reference ignore) {
PersistentMemoryAllocator::Reference ref;
- while ((ref = memory_iter_.GetNextOfType<PersistentHistogramData>()) != 0) {
+ while ((ref = memory_iter_.GetNextOfType(kTypeIdHistogram)) != 0) {
if (ref != ignore)
return allocator_->GetHistogram(ref);
}
@@ -280,17 +271,11 @@ std::unique_ptr<HistogramBase> PersistentHistogramAllocator::GetHistogram(
// add it to the local list of known histograms (while these may be simple
// references to histograms in other processes).
PersistentHistogramData* histogram_data =
- memory_allocator_->GetAsObject<PersistentHistogramData>(ref);
+ memory_allocator_->GetAsObject<PersistentHistogramData>(
+ ref, kTypeIdHistogram);
size_t length = memory_allocator_->GetAllocSize(ref);
-
- // Check that metadata is reasonable: name is NUL terminated and non-empty,
- // ID fields have been loaded with a hash of the name (0 is considered
- // unset/invalid).
if (!histogram_data ||
- reinterpret_cast<char*>(histogram_data)[length - 1] != '\0' ||
- histogram_data->name[0] == '\0' ||
- histogram_data->samples_metadata.id == 0 ||
- histogram_data->logged_metadata.id == 0) {
+ reinterpret_cast<char*>(histogram_data)[length - 1] != '\0') {
RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_METADATA);
NOTREACHED();
return nullptr;
@@ -317,13 +302,14 @@ std::unique_ptr<HistogramBase> PersistentHistogramAllocator::AllocateHistogram(
// Create the metadata necessary for a persistent sparse histogram. This
// is done first because it is a small subset of what is required for
- // other histograms. The type is "under construction" so that a crash
- // during the datafill doesn't leave a bad record around that could cause
- // confusion by another process trying to read it. It will be corrected
- // once histogram construction is complete.
+ // other histograms.
+ PersistentMemoryAllocator::Reference histogram_ref =
+ memory_allocator_->Allocate(
+ offsetof(PersistentHistogramData, name) + name.length() + 1,
+ kTypeIdHistogram);
PersistentHistogramData* histogram_data =
- memory_allocator_->New<PersistentHistogramData>(
- offsetof(PersistentHistogramData, name) + name.length() + 1);
+ memory_allocator_->GetAsObject<PersistentHistogramData>(histogram_ref,
+ kTypeIdHistogram);
if (histogram_data) {
memcpy(histogram_data->name, name.c_str(), name.size() + 1);
histogram_data->histogram_type = histogram_type;
@@ -340,15 +326,14 @@ std::unique_ptr<HistogramBase> PersistentHistogramAllocator::AllocateHistogram(
return nullptr;
}
- size_t ranges_count = bucket_count + 1;
- size_t ranges_bytes = ranges_count * sizeof(HistogramBase::Sample);
+ size_t ranges_bytes = (bucket_count + 1) * sizeof(HistogramBase::Sample);
PersistentMemoryAllocator::Reference counts_ref =
memory_allocator_->Allocate(counts_bytes, kTypeIdCountsArray);
PersistentMemoryAllocator::Reference ranges_ref =
memory_allocator_->Allocate(ranges_bytes, kTypeIdRangesArray);
HistogramBase::Sample* ranges_data =
- memory_allocator_->GetAsArray<HistogramBase::Sample>(
- ranges_ref, kTypeIdRangesArray, ranges_count);
+ memory_allocator_->GetAsObject<HistogramBase::Sample>(
+ ranges_ref, kTypeIdRangesArray);
// Only continue here if all allocations were successful. If they weren't,
// there is no way to free the space but that's not really a problem since
@@ -380,11 +365,6 @@ std::unique_ptr<HistogramBase> PersistentHistogramAllocator::AllocateHistogram(
// correct before commiting the new histogram to persistent space.
std::unique_ptr<HistogramBase> histogram = CreateHistogram(histogram_data);
DCHECK(histogram);
- DCHECK_NE(0U, histogram_data->samples_metadata.id);
- DCHECK_NE(0U, histogram_data->logged_metadata.id);
-
- PersistentMemoryAllocator::Reference histogram_ref =
- memory_allocator_->GetAsReference(histogram_data);
if (ref_ptr != nullptr)
*ref_ptr = histogram_ref;
@@ -406,31 +386,22 @@ std::unique_ptr<HistogramBase> PersistentHistogramAllocator::AllocateHistogram(
result = CREATE_HISTOGRAM_ALLOCATOR_ERROR;
}
RecordCreateHistogramResult(result);
-
- // Crash for failures caused by internal bugs but not "full" which is
- // dependent on outside code.
- if (result != CREATE_HISTOGRAM_ALLOCATOR_FULL)
- NOTREACHED() << memory_allocator_->Name() << ", error=" << result;
+ NOTREACHED() << "error=" << result;
return nullptr;
}
void PersistentHistogramAllocator::FinalizeHistogram(Reference ref,
bool registered) {
- if (registered) {
- // If the created persistent histogram was registered then it needs to
- // be marked as "iterable" in order to be found by other processes. This
- // happens only after the histogram is fully formed so it's impossible for
- // code iterating through the allocator to read a partially created record.
+ // If the created persistent histogram was registered then it needs to
+ // be marked as "iterable" in order to be found by other processes.
+ if (registered)
memory_allocator_->MakeIterable(ref);
- } else {
- // If it wasn't registered then a race condition must have caused two to
- // be created. The allocator does not support releasing the acquired memory
- // so just change the type to be empty.
- memory_allocator_->ChangeType(ref, 0,
- PersistentHistogramData::kPersistentTypeId,
- /*clear=*/false);
- }
+ // If it wasn't registered then a race condition must have caused
+ // two to be created. The allocator does not support releasing the
+ // acquired memory so just change the type to be empty.
+ else
+ memory_allocator_->ChangeType(ref, 0, kTypeIdHistogram);
}
void PersistentHistogramAllocator::MergeHistogramDeltaToStatisticsRecorder(
@@ -506,10 +477,15 @@ PersistentHistogramAllocator::GetCreateHistogramResultHistogram() {
static bool initialized = false;
if (!initialized) {
initialized = true;
- if (GlobalHistogramAllocator::Get()) {
- DVLOG(1) << "Creating the results-histogram inside persistent"
- << " memory can cause future allocations to crash if"
- << " that memory is ever released (for testing).";
+ if (g_allocator) {
+// Don't log in release-with-asserts builds, otherwise the test_installer step
+// fails because this code writes to a log file before the installer code had a
+// chance to set the log file's location.
+#if !defined(DCHECK_ALWAYS_ON)
+ DLOG(WARNING) << "Creating the results-histogram inside persistent"
+ << " memory can cause future allocations to crash if"
+ << " that memory is ever released (for testing).";
+#endif
}
histogram_pointer = LinearHistogram::FactoryGet(
@@ -551,9 +527,8 @@ std::unique_ptr<HistogramBase> PersistentHistogramAllocator::CreateHistogram(
PersistentHistogramData histogram_data = *histogram_data_ptr;
HistogramBase::Sample* ranges_data =
- memory_allocator_->GetAsArray<HistogramBase::Sample>(
- histogram_data.ranges_ref, kTypeIdRangesArray,
- PersistentMemoryAllocator::kSizeAny);
+ memory_allocator_->GetAsObject<HistogramBase::Sample>(
+ histogram_data.ranges_ref, kTypeIdRangesArray);
const uint32_t max_buckets =
std::numeric_limits<uint32_t>::max() / sizeof(HistogramBase::Sample);
@@ -582,9 +557,8 @@ std::unique_ptr<HistogramBase> PersistentHistogramAllocator::CreateHistogram(
created_ranges.release());
HistogramBase::AtomicCount* counts_data =
- memory_allocator_->GetAsArray<HistogramBase::AtomicCount>(
- histogram_data.counts_ref, kTypeIdCountsArray,
- PersistentMemoryAllocator::kSizeAny);
+ memory_allocator_->GetAsObject<HistogramBase::AtomicCount>(
+ histogram_data.counts_ref, kTypeIdCountsArray);
size_t counts_bytes =
CalculateRequiredCountsBytes(histogram_data.bucket_count);
if (!counts_data || counts_bytes == 0 ||
@@ -654,7 +628,7 @@ PersistentHistogramAllocator::GetOrCreateStatisticsRecorderHistogram(
const HistogramBase* histogram) {
// This should never be called on the global histogram allocator as objects
// created there are already within the global statistics recorder.
- DCHECK_NE(GlobalHistogramAllocator::Get(), this);
+ DCHECK_NE(g_allocator, this);
DCHECK(histogram);
HistogramBase* existing =
@@ -664,9 +638,7 @@ PersistentHistogramAllocator::GetOrCreateStatisticsRecorderHistogram(
// Adding the passed histogram to the SR would cause a problem if the
// allocator that holds it eventually goes away. Instead, create a new
- // one from a serialized version. Deserialization calls the appropriate
- // FactoryGet() which will create the histogram in the global persistent-
- // histogram allocator if such is set.
+ // one from a serialized version.
base::Pickle pickle;
if (!histogram->SerializeInfo(&pickle))
return nullptr;
@@ -698,9 +670,9 @@ void GlobalHistogramAllocator::CreateWithPersistentMemory(
size_t page_size,
uint64_t id,
StringPiece name) {
- Set(WrapUnique(
- new GlobalHistogramAllocator(MakeUnique<PersistentMemoryAllocator>(
- base, size, page_size, id, name, false))));
+ Set(WrapUnique(new GlobalHistogramAllocator(
+ WrapUnique(new PersistentMemoryAllocator(
+ base, size, page_size, id, name, false)))));
}
// static
@@ -709,12 +681,12 @@ void GlobalHistogramAllocator::CreateWithLocalMemory(
uint64_t id,
StringPiece name) {
Set(WrapUnique(new GlobalHistogramAllocator(
- MakeUnique<LocalPersistentMemoryAllocator>(size, id, name))));
+ WrapUnique(new LocalPersistentMemoryAllocator(size, id, name)))));
}
#if !defined(OS_NACL)
// static
-bool GlobalHistogramAllocator::CreateWithFile(
+void GlobalHistogramAllocator::CreateWithFile(
const FilePath& file_path,
size_t size,
uint64_t id,
@@ -734,55 +706,14 @@ bool GlobalHistogramAllocator::CreateWithFile(
if (!mmfile->IsValid() ||
!FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, true)) {
NOTREACHED();
- return false;
+ return;
}
- Set(WrapUnique(
- new GlobalHistogramAllocator(MakeUnique<FilePersistentMemoryAllocator>(
- std::move(mmfile), size, id, name, false))));
- Get()->SetPersistentLocation(file_path);
- return true;
-}
-
-// static
-bool GlobalHistogramAllocator::CreateWithActiveFile(const FilePath& base_path,
- const FilePath& active_path,
- size_t size,
- uint64_t id,
- StringPiece name) {
- if (!base::ReplaceFile(active_path, base_path, nullptr))
- base::DeleteFile(base_path, /*recursive=*/false);
-
- return base::GlobalHistogramAllocator::CreateWithFile(active_path, size, id,
- name);
-}
-
-// static
-bool GlobalHistogramAllocator::CreateWithActiveFileInDir(const FilePath& dir,
- size_t size,
- uint64_t id,
- StringPiece name) {
- FilePath base_path, active_path;
- ConstructFilePaths(dir, name, &base_path, &active_path);
- return CreateWithActiveFile(base_path, active_path, size, id, name);
-}
-
-// static
-void GlobalHistogramAllocator::ConstructFilePaths(const FilePath& dir,
- StringPiece name,
- FilePath* out_base_path,
- FilePath* out_active_path) {
- if (out_base_path) {
- *out_base_path = dir.AppendASCII(name).AddExtension(
- PersistentMemoryAllocator::kFileExtension);
- }
- if (out_active_path) {
- *out_active_path =
- dir.AppendASCII(name.as_string() + std::string("-active"))
- .AddExtension(PersistentMemoryAllocator::kFileExtension);
- }
+ Set(WrapUnique(new GlobalHistogramAllocator(
+ WrapUnique(new FilePersistentMemoryAllocator(
+ std::move(mmfile), size, id, name, false)))));
}
-#endif // !defined(OS_NACL)
+#endif
// static
void GlobalHistogramAllocator::CreateWithSharedMemory(
@@ -797,9 +728,9 @@ void GlobalHistogramAllocator::CreateWithSharedMemory(
}
DCHECK_LE(memory->mapped_size(), size);
- Set(WrapUnique(
- new GlobalHistogramAllocator(MakeUnique<SharedPersistentMemoryAllocator>(
- std::move(memory), 0, StringPiece(), /*readonly=*/false))));
+ Set(WrapUnique(new GlobalHistogramAllocator(
+ WrapUnique(new SharedPersistentMemoryAllocator(
+ std::move(memory), 0, StringPiece(), /*readonly=*/false)))));
}
// static
@@ -814,9 +745,9 @@ void GlobalHistogramAllocator::CreateWithSharedMemoryHandle(
return;
}
- Set(WrapUnique(
- new GlobalHistogramAllocator(MakeUnique<SharedPersistentMemoryAllocator>(
- std::move(shm), 0, StringPiece(), /*readonly=*/false))));
+ Set(WrapUnique(new GlobalHistogramAllocator(
+ WrapUnique(new SharedPersistentMemoryAllocator(
+ std::move(shm), 0, StringPiece(), /*readonly=*/false)))));
}
// static
@@ -825,9 +756,8 @@ void GlobalHistogramAllocator::Set(
// Releasing or changing an allocator is extremely dangerous because it
// likely has histograms stored within it. If the backing memory is also
// also released, future accesses to those histograms will seg-fault.
- CHECK(!subtle::NoBarrier_Load(&g_allocator));
- subtle::Release_Store(&g_allocator,
- reinterpret_cast<uintptr_t>(allocator.release()));
+ CHECK(!g_allocator);
+ g_allocator = allocator.release();
size_t existing = StatisticsRecorder::GetHistogramCount();
DVLOG_IF(1, existing)
@@ -836,14 +766,13 @@ void GlobalHistogramAllocator::Set(
// static
GlobalHistogramAllocator* GlobalHistogramAllocator::Get() {
- return reinterpret_cast<GlobalHistogramAllocator*>(
- subtle::Acquire_Load(&g_allocator));
+ return g_allocator;
}
// static
std::unique_ptr<GlobalHistogramAllocator>
GlobalHistogramAllocator::ReleaseForTesting() {
- GlobalHistogramAllocator* histogram_allocator = Get();
+ GlobalHistogramAllocator* histogram_allocator = g_allocator;
if (!histogram_allocator)
return nullptr;
PersistentMemoryAllocator* memory_allocator =
@@ -853,9 +782,13 @@ GlobalHistogramAllocator::ReleaseForTesting() {
// Recorder forget about the histograms contained therein; otherwise,
// some operations will try to access them and the released memory.
PersistentMemoryAllocator::Iterator iter(memory_allocator);
- const PersistentHistogramData* data;
- while ((data = iter.GetNextOfObject<PersistentHistogramData>()) != nullptr) {
- StatisticsRecorder::ForgetHistogramForTesting(data->name);
+ PersistentMemoryAllocator::Reference ref;
+ while ((ref = iter.GetNextOfType(kTypeIdHistogram)) != 0) {
+ PersistentHistogramData* histogram_data =
+ memory_allocator->GetAsObject<PersistentHistogramData>(
+ ref, kTypeIdHistogram);
+ DCHECK(histogram_data);
+ StatisticsRecorder::ForgetHistogramForTesting(histogram_data->name);
// If a test breaks here then a memory region containing a histogram
// actively used by this code is being released back to the test.
@@ -864,10 +797,10 @@ GlobalHistogramAllocator::ReleaseForTesting() {
// the method GetCreateHistogramResultHistogram() *before* setting
// the (temporary) memory allocator via SetGlobalAllocator() so that
// histogram is instead allocated from the process heap.
- DCHECK_NE(kResultHistogram, data->name);
+ DCHECK_NE(kResultHistogram, histogram_data->name);
}
- subtle::Release_Store(&g_allocator, 0);
+ g_allocator = nullptr;
return WrapUnique(histogram_allocator);
};
@@ -904,30 +837,10 @@ bool GlobalHistogramAllocator::WriteToPersistentLocation() {
#endif
}
-void GlobalHistogramAllocator::DeletePersistentLocation() {
-#if defined(OS_NACL)
- NOTREACHED();
-#else
- if (persistent_location_.empty())
- return;
-
- // Open (with delete) and then immediately close the file by going out of
- // scope. This is the only cross-platform safe way to delete a file that may
- // be open elsewhere. Open handles will continue to operate normally but
- // new opens will not be possible.
- File file(persistent_location_,
- File::FLAG_OPEN | File::FLAG_READ | File::FLAG_DELETE_ON_CLOSE);
-#endif
-}
-
GlobalHistogramAllocator::GlobalHistogramAllocator(
std::unique_ptr<PersistentMemoryAllocator> memory)
: PersistentHistogramAllocator(std::move(memory)),
- import_iterator_(this) {
- // Make sure the StatisticsRecorder is initialized to prevent duplicate
- // histograms from being created. It's safe to call this multiple times.
- StatisticsRecorder::Initialize();
-}
+ import_iterator_(this) {}
void GlobalHistogramAllocator::ImportHistogramsToStatisticsRecorder() {
// Skip the import if it's the histogram that was last created. Should a
diff --git a/base/metrics/persistent_histogram_allocator.h b/base/metrics/persistent_histogram_allocator.h
index 2eb28dfaf5..ee1fba5f62 100644
--- a/base/metrics/persistent_histogram_allocator.h
+++ b/base/metrics/persistent_histogram_allocator.h
@@ -19,7 +19,6 @@
namespace base {
-class BucketRanges;
class FilePath;
class PersistentSampleMapRecords;
class PersistentSparseHistogramDataManager;
@@ -56,8 +55,8 @@ class BASE_EXPORT PersistentSparseHistogramDataManager {
// Convenience method that gets the object for a given reference so callers
// don't have to also keep their own pointer to the appropriate allocator.
template <typename T>
- T* GetAsObject(PersistentMemoryAllocator::Reference ref) {
- return allocator_->GetAsObject<T>(ref);
+ T* GetAsObject(PersistentMemoryAllocator::Reference ref, uint32_t type_id) {
+ return allocator_->GetAsObject<T>(ref, type_id);
}
private:
@@ -131,8 +130,8 @@ class BASE_EXPORT PersistentSampleMapRecords {
// cleanliness of the interface), a template is defined that will be
// resolved when used inside that file.
template <typename T>
- T* GetAsObject(PersistentMemoryAllocator::Reference ref) {
- return data_manager_->GetAsObject<T>(ref);
+ T* GetAsObject(PersistentMemoryAllocator::Reference ref, uint32_t type_id) {
+ return data_manager_->GetAsObject<T>(ref, type_id);
}
private:
@@ -395,40 +394,11 @@ class BASE_EXPORT GlobalHistogramAllocator
// Create a global allocator by memory-mapping a |file|. If the file does
// not exist, it will be created with the specified |size|. If the file does
// exist, the allocator will use and add to its contents, ignoring the passed
- // size in favor of the existing size. Returns whether the global allocator
- // was set.
- static bool CreateWithFile(const FilePath& file_path,
+ // size in favor of the existing size.
+ static void CreateWithFile(const FilePath& file_path,
size_t size,
uint64_t id,
StringPiece name);
-
- // Creates a new file at |active_path|. If it already exists, it will first be
- // moved to |base_path|. In all cases, any old file at |base_path| will be
- // removed. The file will be created using the given size, id, and name.
- // Returns whether the global allocator was set.
- static bool CreateWithActiveFile(const FilePath& base_path,
- const FilePath& active_path,
- size_t size,
- uint64_t id,
- StringPiece name);
-
- // Uses ConstructBaseActivePairFilePaths() to build a pair of file names which
- // are then used for CreateWithActiveFile(). |name| is used for both the
- // internal name for the allocator and also for the name of the file inside
- // |dir|.
- static bool CreateWithActiveFileInDir(const FilePath& dir,
- size_t size,
- uint64_t id,
- StringPiece name);
-
- // Constructs a pair of names in |dir| based on name that can be used for a
- // base + active persistent memory mapped location for CreateWithActiveFile().
- // |name| will be used as the basename of the file inside |dir|.
- // |out_base_path| or |out_active_path| may be null if not needed.
- static void ConstructFilePaths(const FilePath& dir,
- StringPiece name,
- FilePath* out_base_path,
- FilePath* out_active_path);
#endif
// Create a global allocator using a block of shared |memory| of the
@@ -479,10 +449,6 @@ class BASE_EXPORT GlobalHistogramAllocator
// indicates success.
bool WriteToPersistentLocation();
- // If there is a global metrics file being updated on disk, mark it to be
- // deleted when the process exits.
- void DeletePersistentLocation();
-
private:
friend class StatisticsRecorder;
diff --git a/base/metrics/persistent_histogram_allocator_unittest.cc b/base/metrics/persistent_histogram_allocator_unittest.cc
index df250a37b0..b680662250 100644
--- a/base/metrics/persistent_histogram_allocator_unittest.cc
+++ b/base/metrics/persistent_histogram_allocator_unittest.cc
@@ -102,8 +102,9 @@ TEST_F(PersistentHistogramAllocatorTest, CreateAndIterateTest) {
// Create a second allocator and have it access the memory of the first.
std::unique_ptr<HistogramBase> recovered;
- PersistentHistogramAllocator recovery(MakeUnique<PersistentMemoryAllocator>(
- allocator_memory_.get(), kAllocatorMemorySize, 0, 0, "", false));
+ PersistentHistogramAllocator recovery(
+ WrapUnique(new PersistentMemoryAllocator(
+ allocator_memory_.get(), kAllocatorMemorySize, 0, 0, "", false)));
PersistentHistogramAllocator::Iterator histogram_iter(&recovery);
recovered = histogram_iter.GetNext();
@@ -130,7 +131,7 @@ TEST_F(PersistentHistogramAllocatorTest, CreateWithFileTest) {
const char temp_name[] = "CreateWithFileTest";
ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath temp_file = temp_dir.GetPath().AppendASCII(temp_name);
+ FilePath temp_file = temp_dir.path().AppendASCII(temp_name);
const size_t temp_size = 64 << 10; // 64 KiB
// Test creation of a new file.
@@ -155,130 +156,54 @@ TEST_F(PersistentHistogramAllocatorTest, CreateWithFileTest) {
GlobalHistogramAllocator::ReleaseForTesting();
}
-TEST_F(PersistentHistogramAllocatorTest, StatisticsRecorderMergeTest) {
- const char LinearHistogramName[] = "SRTLinearHistogram";
- const char SparseHistogramName[] = "SRTSparseHistogram";
- const size_t starting_sr_count = StatisticsRecorder::GetHistogramCount();
+TEST_F(PersistentHistogramAllocatorTest, StatisticsRecorderTest) {
+ size_t starting_sr_count = StatisticsRecorder::GetHistogramCount();
// Create a local StatisticsRecorder in which the newly created histogram
- // will be recorded. The global allocator must be replaced after because the
- // act of releasing will cause the active SR to forget about all histograms
- // in the relased memory.
+ // will be recorded.
std::unique_ptr<StatisticsRecorder> local_sr =
StatisticsRecorder::CreateTemporaryForTesting();
EXPECT_EQ(0U, StatisticsRecorder::GetHistogramCount());
- std::unique_ptr<GlobalHistogramAllocator> old_allocator =
- GlobalHistogramAllocator::ReleaseForTesting();
- GlobalHistogramAllocator::CreateWithLocalMemory(kAllocatorMemorySize, 0, "");
- ASSERT_TRUE(GlobalHistogramAllocator::Get());
-
- // Create a linear histogram for merge testing.
- HistogramBase* histogram1 =
- LinearHistogram::FactoryGet(LinearHistogramName, 1, 10, 10, 0);
- ASSERT_TRUE(histogram1);
+
+ HistogramBase* histogram = LinearHistogram::FactoryGet(
+ "TestHistogram", 1, 10, 10, HistogramBase::kIsPersistent);
+ EXPECT_TRUE(histogram);
EXPECT_EQ(1U, StatisticsRecorder::GetHistogramCount());
- histogram1->Add(3);
- histogram1->Add(1);
- histogram1->Add(4);
- histogram1->AddCount(1, 4);
- histogram1->Add(6);
-
- // Create a sparse histogram for merge testing.
- HistogramBase* histogram2 =
- SparseHistogram::FactoryGet(SparseHistogramName, 0);
- ASSERT_TRUE(histogram2);
- EXPECT_EQ(2U, StatisticsRecorder::GetHistogramCount());
- histogram2->Add(3);
- histogram2->Add(1);
- histogram2->Add(4);
- histogram2->AddCount(1, 4);
- histogram2->Add(6);
-
- // Destroy the local SR and ensure that we're back to the initial state and
- // restore the global allocator. Histograms created in the local SR will
- // become unmanaged.
- std::unique_ptr<GlobalHistogramAllocator> new_allocator =
- GlobalHistogramAllocator::ReleaseForTesting();
+ histogram->Add(3);
+ histogram->Add(1);
+ histogram->Add(4);
+ histogram->Add(1);
+ histogram->Add(6);
+
+ // Destroy the local SR and ensure that we're back to the initial state.
local_sr.reset();
EXPECT_EQ(starting_sr_count, StatisticsRecorder::GetHistogramCount());
- GlobalHistogramAllocator::Set(std::move(old_allocator));
- // Create a "recovery" allocator using the same memory as the local one.
- PersistentHistogramAllocator recovery1(MakeUnique<PersistentMemoryAllocator>(
- const_cast<void*>(new_allocator->memory_allocator()->data()),
- new_allocator->memory_allocator()->size(), 0, 0, "", false));
- PersistentHistogramAllocator::Iterator histogram_iter1(&recovery1);
-
- // Get the histograms that were created locally (and forgotten) and merge
- // them into the global SR. New objects will be created.
+ // Create a second allocator and have it access the memory of the first.
std::unique_ptr<HistogramBase> recovered;
- while (true) {
- recovered = histogram_iter1.GetNext();
- if (!recovered)
- break;
-
- recovery1.MergeHistogramDeltaToStatisticsRecorder(recovered.get());
- HistogramBase* found =
- StatisticsRecorder::FindHistogram(recovered->histogram_name());
- EXPECT_NE(recovered.get(), found);
- };
- EXPECT_EQ(starting_sr_count + 2, StatisticsRecorder::GetHistogramCount());
-
- // Check the merged histograms for accuracy.
- HistogramBase* found = StatisticsRecorder::FindHistogram(LinearHistogramName);
- ASSERT_TRUE(found);
- std::unique_ptr<HistogramSamples> snapshot = found->SnapshotSamples();
- EXPECT_EQ(found->SnapshotSamples()->TotalCount(), snapshot->TotalCount());
- EXPECT_EQ(1, snapshot->GetCount(3));
- EXPECT_EQ(5, snapshot->GetCount(1));
- EXPECT_EQ(1, snapshot->GetCount(4));
- EXPECT_EQ(1, snapshot->GetCount(6));
+ PersistentHistogramAllocator recovery(
+ WrapUnique(new PersistentMemoryAllocator(
+ allocator_memory_.get(), kAllocatorMemorySize, 0, 0, "", false)));
+ PersistentHistogramAllocator::Iterator histogram_iter(&recovery);
- found = StatisticsRecorder::FindHistogram(SparseHistogramName);
- ASSERT_TRUE(found);
- snapshot = found->SnapshotSamples();
- EXPECT_EQ(found->SnapshotSamples()->TotalCount(), snapshot->TotalCount());
- EXPECT_EQ(1, snapshot->GetCount(3));
- EXPECT_EQ(5, snapshot->GetCount(1));
- EXPECT_EQ(1, snapshot->GetCount(4));
- EXPECT_EQ(1, snapshot->GetCount(6));
+ recovered = histogram_iter.GetNext();
+ ASSERT_TRUE(recovered);
- // Perform additional histogram increments.
- histogram1->AddCount(1, 3);
- histogram1->Add(6);
- histogram2->AddCount(1, 3);
- histogram2->Add(7);
-
- // Do another merge.
- PersistentHistogramAllocator recovery2(MakeUnique<PersistentMemoryAllocator>(
- const_cast<void*>(new_allocator->memory_allocator()->data()),
- new_allocator->memory_allocator()->size(), 0, 0, "", false));
- PersistentHistogramAllocator::Iterator histogram_iter2(&recovery2);
- while (true) {
- recovered = histogram_iter2.GetNext();
- if (!recovered)
- break;
- recovery2.MergeHistogramDeltaToStatisticsRecorder(recovered.get());
- };
- EXPECT_EQ(starting_sr_count + 2, StatisticsRecorder::GetHistogramCount());
-
- // And verify.
- found = StatisticsRecorder::FindHistogram(LinearHistogramName);
- snapshot = found->SnapshotSamples();
- EXPECT_EQ(found->SnapshotSamples()->TotalCount(), snapshot->TotalCount());
- EXPECT_EQ(1, snapshot->GetCount(3));
- EXPECT_EQ(8, snapshot->GetCount(1));
- EXPECT_EQ(1, snapshot->GetCount(4));
- EXPECT_EQ(2, snapshot->GetCount(6));
+ // Merge the recovered histogram to the SR. It will always be a new object.
+ recovery.MergeHistogramDeltaToStatisticsRecorder(recovered.get());
+ EXPECT_EQ(starting_sr_count + 1, StatisticsRecorder::GetHistogramCount());
+ HistogramBase* found =
+ StatisticsRecorder::FindHistogram(recovered->histogram_name());
+ ASSERT_TRUE(found);
+ EXPECT_NE(recovered.get(), found);
- found = StatisticsRecorder::FindHistogram(SparseHistogramName);
- snapshot = found->SnapshotSamples();
- EXPECT_EQ(found->SnapshotSamples()->TotalCount(), snapshot->TotalCount());
+ // Ensure that the data got merged, too.
+ std::unique_ptr<HistogramSamples> snapshot = found->SnapshotSamples();
+ EXPECT_EQ(recovered->SnapshotSamples()->TotalCount(), snapshot->TotalCount());
EXPECT_EQ(1, snapshot->GetCount(3));
- EXPECT_EQ(8, snapshot->GetCount(1));
+ EXPECT_EQ(2, snapshot->GetCount(1));
EXPECT_EQ(1, snapshot->GetCount(4));
EXPECT_EQ(1, snapshot->GetCount(6));
- EXPECT_EQ(1, snapshot->GetCount(7));
}
} // namespace base
diff --git a/base/metrics/persistent_memory_allocator.cc b/base/metrics/persistent_memory_allocator.cc
index f70b396917..dfa408f44d 100644
--- a/base/metrics/persistent_memory_allocator.cc
+++ b/base/metrics/persistent_memory_allocator.cc
@@ -17,7 +17,6 @@
#include "base/logging.h"
#include "base/memory/shared_memory.h"
#include "base/metrics/histogram_macros.h"
-#include "base/metrics/sparse_histogram.h"
namespace {
@@ -49,11 +48,6 @@ enum : int {
kFlagFull = 1 << 1
};
-// Errors that are logged in "errors" histogram.
-enum AllocatorError : int {
- kMemoryIsCorrupt = 1,
-};
-
bool CheckFlag(const volatile std::atomic<uint32_t>* flags, int flag) {
uint32_t loaded_flags = flags->load(std::memory_order_relaxed);
return (loaded_flags & flag) != 0;
@@ -64,13 +58,8 @@ void SetFlag(volatile std::atomic<uint32_t>* flags, int flag) {
for (;;) {
uint32_t new_flags = (loaded_flags & ~flag) | flag;
// In the failue case, actual "flags" value stored in loaded_flags.
- // These access are "relaxed" because they are completely independent
- // of all other values.
- if (flags->compare_exchange_weak(loaded_flags, new_flags,
- std::memory_order_relaxed,
- std::memory_order_relaxed)) {
+ if (flags->compare_exchange_weak(loaded_flags, new_flags))
break;
- }
}
}
@@ -143,19 +132,7 @@ PersistentMemoryAllocator::Iterator::Iterator(
PersistentMemoryAllocator::Iterator::Iterator(
const PersistentMemoryAllocator* allocator,
Reference starting_after)
- : allocator_(allocator), last_record_(0), record_count_(0) {
- Reset(starting_after);
-}
-
-void PersistentMemoryAllocator::Iterator::Reset() {
- last_record_.store(kReferenceQueue, std::memory_order_relaxed);
- record_count_.store(0, std::memory_order_relaxed);
-}
-
-void PersistentMemoryAllocator::Iterator::Reset(Reference starting_after) {
- last_record_.store(starting_after, std::memory_order_relaxed);
- record_count_.store(0, std::memory_order_relaxed);
-
+ : allocator_(allocator), last_record_(starting_after), record_count_(0) {
// Ensure that the starting point is a valid, iterable block (meaning it can
// be read and has a non-zero "next" pointer).
const volatile BlockHeader* block =
@@ -167,14 +144,6 @@ void PersistentMemoryAllocator::Iterator::Reset(Reference starting_after) {
}
PersistentMemoryAllocator::Reference
-PersistentMemoryAllocator::Iterator::GetLast() {
- Reference last = last_record_.load(std::memory_order_relaxed);
- if (last == kReferenceQueue)
- return kReferenceNull;
- return last;
-}
-
-PersistentMemoryAllocator::Reference
PersistentMemoryAllocator::Iterator::GetNext(uint32_t* type_return) {
// Make a copy of the existing count of found-records, acquiring all changes
// made to the allocator, notably "freeptr" (see comment in loop for why
@@ -224,8 +193,7 @@ PersistentMemoryAllocator::Iterator::GetNext(uint32_t* type_return) {
// is no need to do another such load when the while-loop restarts. A
// "strong" compare-exchange is used because failing unnecessarily would
// mean repeating some fairly costly validations above.
- if (last_record_.compare_exchange_strong(
- last, next, std::memory_order_acq_rel, std::memory_order_acquire)) {
+ if (last_record_.compare_exchange_strong(last, next)) {
*type_return = block->type_id.load(std::memory_order_relaxed);
break;
}
@@ -279,42 +247,20 @@ bool PersistentMemoryAllocator::IsMemoryAcceptable(const void* base,
(page_size == 0 || size % page_size == 0 || readonly));
}
-PersistentMemoryAllocator::PersistentMemoryAllocator(void* base,
- size_t size,
- size_t page_size,
- uint64_t id,
- base::StringPiece name,
- bool readonly)
- : PersistentMemoryAllocator(Memory(base, MEM_EXTERNAL),
- size,
- page_size,
- id,
- name,
- readonly) {}
-
-PersistentMemoryAllocator::PersistentMemoryAllocator(Memory memory,
- size_t size,
- size_t page_size,
- uint64_t id,
- base::StringPiece name,
- bool readonly)
- : mem_base_(static_cast<char*>(memory.base)),
- mem_type_(memory.type),
+PersistentMemoryAllocator::PersistentMemoryAllocator(
+ void* base,
+ size_t size,
+ size_t page_size,
+ uint64_t id,
+ base::StringPiece name,
+ bool readonly)
+ : mem_base_(static_cast<char*>(base)),
mem_size_(static_cast<uint32_t>(size)),
mem_page_(static_cast<uint32_t>((page_size ? page_size : size))),
readonly_(readonly),
corrupt_(0),
allocs_histogram_(nullptr),
- used_histogram_(nullptr),
- errors_histogram_(nullptr) {
- // These asserts ensure that the structures are 32/64-bit agnostic and meet
- // all the requirements of use within the allocator. They access private
- // definitions and so cannot be moved to the global scope.
- static_assert(sizeof(PersistentMemoryAllocator::BlockHeader) == 16,
- "struct is not portable across different natural word widths");
- static_assert(sizeof(PersistentMemoryAllocator::SharedMetadata) == 56,
- "struct is not portable across different natural word widths");
-
+ used_histogram_(nullptr) {
static_assert(sizeof(BlockHeader) % kAllocAlignment == 0,
"BlockHeader is not a multiple of kAllocAlignment");
static_assert(sizeof(SharedMetadata) % kAllocAlignment == 0,
@@ -323,7 +269,7 @@ PersistentMemoryAllocator::PersistentMemoryAllocator(Memory memory,
"\"queue\" is not aligned properly; must be at end of struct");
// Ensure that memory segment is of acceptable size.
- CHECK(IsMemoryAcceptable(memory.base, size, page_size, readonly));
+ CHECK(IsMemoryAcceptable(base, size, page_size, readonly));
// These atomics operate inter-process and so must be lock-free. The local
// casts are to make sure it can be evaluated at compile time to a constant.
@@ -380,7 +326,7 @@ PersistentMemoryAllocator::PersistentMemoryAllocator(Memory memory,
if (!name.empty()) {
const size_t name_length = name.length() + 1;
shared_meta()->name = Allocate(name_length, 0);
- char* name_cstr = GetAsArray<char>(shared_meta()->name, 0, name_length);
+ char* name_cstr = GetAsObject<char>(shared_meta()->name, 0);
if (name_cstr)
memcpy(name_cstr, name.data(), name.length());
}
@@ -409,7 +355,7 @@ PersistentMemoryAllocator::PersistentMemoryAllocator(Memory memory,
*const_cast<uint32_t*>(&mem_page_) = shared_meta()->page_size;
// Ensure that settings are still valid after the above adjustments.
- if (!IsMemoryAcceptable(memory.base, mem_size_, mem_page_, readonly))
+ if (!IsMemoryAcceptable(base, mem_size_, mem_page_, readonly))
SetCorrupt();
}
}
@@ -428,8 +374,7 @@ uint64_t PersistentMemoryAllocator::Id() const {
const char* PersistentMemoryAllocator::Name() const {
Reference name_ref = shared_meta()->name;
- const char* name_cstr =
- GetAsArray<char>(name_ref, 0, PersistentMemoryAllocator::kSizeAny);
+ const char* name_cstr = GetAsObject<char>(name_ref, 0);
if (!name_cstr)
return "";
@@ -447,26 +392,16 @@ void PersistentMemoryAllocator::CreateTrackingHistograms(
base::StringPiece name) {
if (name.empty() || readonly_)
return;
- std::string name_string = name.as_string();
-
-#if 0
- // This histogram wasn't being used so has been disabled. It is left here
- // in case development of a new use of the allocator could benefit from
- // recording (temporarily and locally) the allocation sizes.
- DCHECK(!allocs_histogram_);
- allocs_histogram_ = Histogram::FactoryGet(
- "UMA.PersistentAllocator." + name_string + ".Allocs", 1, 10000, 50,
- HistogramBase::kUmaTargetedHistogramFlag);
-#endif
+ std::string name_string = name.as_string();
DCHECK(!used_histogram_);
used_histogram_ = LinearHistogram::FactoryGet(
"UMA.PersistentAllocator." + name_string + ".UsedPct", 1, 101, 21,
HistogramBase::kUmaTargetedHistogramFlag);
- DCHECK(!errors_histogram_);
- errors_histogram_ = SparseHistogram::FactoryGet(
- "UMA.PersistentAllocator." + name_string + ".Errors",
+ DCHECK(!allocs_histogram_);
+ allocs_histogram_ = Histogram::FactoryGet(
+ "UMA.PersistentAllocator." + name_string + ".Allocs", 1, 10000, 50,
HistogramBase::kUmaTargetedHistogramFlag);
}
@@ -475,24 +410,6 @@ size_t PersistentMemoryAllocator::used() const {
mem_size_);
}
-PersistentMemoryAllocator::Reference PersistentMemoryAllocator::GetAsReference(
- const void* memory,
- uint32_t type_id) const {
- uintptr_t address = reinterpret_cast<uintptr_t>(memory);
- if (address < reinterpret_cast<uintptr_t>(mem_base_))
- return kReferenceNull;
-
- uintptr_t offset = address - reinterpret_cast<uintptr_t>(mem_base_);
- if (offset >= mem_size_ || offset < sizeof(BlockHeader))
- return kReferenceNull;
-
- Reference ref = static_cast<Reference>(offset) - sizeof(BlockHeader);
- if (!GetBlockData(ref, type_id, kSizeAny))
- return kReferenceNull;
-
- return ref;
-}
-
size_t PersistentMemoryAllocator::GetAllocSize(Reference ref) const {
const volatile BlockHeader* const block = GetBlock(ref, 0, 0, false, false);
if (!block)
@@ -516,62 +433,15 @@ uint32_t PersistentMemoryAllocator::GetType(Reference ref) const {
bool PersistentMemoryAllocator::ChangeType(Reference ref,
uint32_t to_type_id,
- uint32_t from_type_id,
- bool clear) {
+ uint32_t from_type_id) {
DCHECK(!readonly_);
volatile BlockHeader* const block = GetBlock(ref, 0, 0, false, false);
if (!block)
return false;
- // "Strong" exchanges are used below because there is no loop that can retry
- // in the wake of spurious failures possible with "weak" exchanges. It is,
- // in aggregate, an "acquire-release" operation so no memory accesses can be
- // reordered either before or after this method (since changes based on type
- // could happen on either side).
-
- if (clear) {
- // If clearing the memory, first change it to the "transitioning" type so
- // there can be no confusion by other threads. After the memory is cleared,
- // it can be changed to its final type.
- if (!block->type_id.compare_exchange_strong(
- from_type_id, kTypeIdTransitioning, std::memory_order_acquire,
- std::memory_order_acquire)) {
- // Existing type wasn't what was expected: fail (with no changes)
- return false;
- }
-
- // Clear the memory in an atomic manner. Using "release" stores force
- // every write to be done after the ones before it. This is better than
- // using memset because (a) it supports "volatile" and (b) it creates a
- // reliable pattern upon which other threads may rely.
- volatile std::atomic<int>* data =
- reinterpret_cast<volatile std::atomic<int>*>(
- reinterpret_cast<volatile char*>(block) + sizeof(BlockHeader));
- const uint32_t words = (block->size - sizeof(BlockHeader)) / sizeof(int);
- DCHECK_EQ(0U, (block->size - sizeof(BlockHeader)) % sizeof(int));
- for (uint32_t i = 0; i < words; ++i) {
- data->store(0, std::memory_order_release);
- ++data;
- }
-
- // If the destination type is "transitioning" then skip the final exchange.
- if (to_type_id == kTypeIdTransitioning)
- return true;
-
- // Finish the change to the desired type.
- from_type_id = kTypeIdTransitioning; // Exchange needs modifiable original.
- bool success = block->type_id.compare_exchange_strong(
- from_type_id, to_type_id, std::memory_order_release,
- std::memory_order_relaxed);
- DCHECK(success); // Should never fail.
- return success;
- }
-
- // One step change to the new type. Will return false if the existing value
- // doesn't match what is expected.
- return block->type_id.compare_exchange_strong(from_type_id, to_type_id,
- std::memory_order_acq_rel,
- std::memory_order_acquire);
+ // This is a "strong" exchange because there is no loop that can retry in
+ // the wake of spurious failures possible with "weak" exchanges.
+ return block->type_id.compare_exchange_strong(from_type_id, to_type_id);
}
PersistentMemoryAllocator::Reference PersistentMemoryAllocator::Allocate(
@@ -650,9 +520,8 @@ PersistentMemoryAllocator::Reference PersistentMemoryAllocator::AllocateImpl(
return kReferenceNull;
}
const uint32_t new_freeptr = freeptr + page_free;
- if (shared_meta()->freeptr.compare_exchange_strong(
- freeptr, new_freeptr, std::memory_order_acq_rel,
- std::memory_order_acquire)) {
+ if (shared_meta()->freeptr.compare_exchange_strong(freeptr,
+ new_freeptr)) {
block->size = page_free;
block->cookie = kBlockCookieWasted;
}
@@ -675,11 +544,8 @@ PersistentMemoryAllocator::Reference PersistentMemoryAllocator::AllocateImpl(
// while we were processing. A "weak" exchange would be permissable here
// because the code will just loop and try again but the above processing
// is significant so make the extra effort of a "strong" exchange.
- if (!shared_meta()->freeptr.compare_exchange_strong(
- freeptr, new_freeptr, std::memory_order_acq_rel,
- std::memory_order_acquire)) {
+ if (!shared_meta()->freeptr.compare_exchange_strong(freeptr, new_freeptr))
continue;
- }
// Given that all memory was zeroed before ever being given to an instance
// of this class and given that we only allocate in a monotomic fashion
@@ -695,10 +561,6 @@ PersistentMemoryAllocator::Reference PersistentMemoryAllocator::AllocateImpl(
return kReferenceNull;
}
- // Load information into the block header. There is no "release" of the
- // data here because this memory can, currently, be seen only by the thread
- // performing the allocation. When it comes time to share this, the thread
- // will call MakeIterable() which does the release operation.
block->size = size;
block->cookie = kBlockCookieAllocated;
block->type_id.store(type_id, std::memory_order_relaxed);
@@ -711,7 +573,7 @@ void PersistentMemoryAllocator::GetMemoryInfo(MemoryInfo* meminfo) const {
mem_size_ - shared_meta()->freeptr.load(std::memory_order_relaxed),
(uint32_t)sizeof(BlockHeader));
meminfo->total = mem_size_;
- meminfo->free = remaining - sizeof(BlockHeader);
+ meminfo->free = IsCorrupt() ? 0 : remaining - sizeof(BlockHeader);
}
void PersistentMemoryAllocator::MakeIterable(Reference ref) {
@@ -779,15 +641,9 @@ void PersistentMemoryAllocator::MakeIterable(Reference ref) {
// case, it's safe to discard the constness and modify the local flag and
// maybe even the shared flag if the underlying data isn't actually read-only.
void PersistentMemoryAllocator::SetCorrupt() const {
- if (!corrupt_.load(std::memory_order_relaxed) &&
- !CheckFlag(
- const_cast<volatile std::atomic<uint32_t>*>(&shared_meta()->flags),
- kFlagCorrupt)) {
- LOG(ERROR) << "Corruption detected in shared-memory segment.";
- RecordError(kMemoryIsCorrupt);
- }
-
- corrupt_.store(true, std::memory_order_relaxed);
+ LOG(ERROR) << "Corruption detected in shared-memory segment.";
+ const_cast<std::atomic<bool>*>(&corrupt_)->store(true,
+ std::memory_order_relaxed);
if (!readonly_) {
SetFlag(const_cast<volatile std::atomic<uint32_t>*>(&shared_meta()->flags),
kFlagCorrupt);
@@ -817,10 +673,10 @@ PersistentMemoryAllocator::GetBlock(Reference ref, uint32_t type_id,
uint32_t size, bool queue_ok,
bool free_ok) const {
// Validation of parameters.
- if (ref < (queue_ok ? kReferenceQueue : sizeof(SharedMetadata)))
- return nullptr;
if (ref % kAllocAlignment != 0)
return nullptr;
+ if (ref < (queue_ok ? kReferenceQueue : sizeof(SharedMetadata)))
+ return nullptr;
size += sizeof(BlockHeader);
if (ref + size > mem_size_)
return nullptr;
@@ -849,11 +705,6 @@ PersistentMemoryAllocator::GetBlock(Reference ref, uint32_t type_id,
return reinterpret_cast<const volatile BlockHeader*>(mem_base_ + ref);
}
-void PersistentMemoryAllocator::RecordError(int error) const {
- if (errors_histogram_)
- errors_histogram_->Add(error);
-}
-
const volatile void* PersistentMemoryAllocator::GetBlockData(
Reference ref,
uint32_t type_id,
@@ -888,60 +739,37 @@ LocalPersistentMemoryAllocator::LocalPersistentMemoryAllocator(
size, 0, id, name, false) {}
LocalPersistentMemoryAllocator::~LocalPersistentMemoryAllocator() {
- DeallocateLocalMemory(const_cast<char*>(mem_base_), mem_size_, mem_type_);
+ DeallocateLocalMemory(const_cast<char*>(mem_base_), mem_size_);
}
// static
-PersistentMemoryAllocator::Memory
-LocalPersistentMemoryAllocator::AllocateLocalMemory(size_t size) {
- void* address;
-
+void* LocalPersistentMemoryAllocator::AllocateLocalMemory(size_t size) {
#if defined(OS_WIN)
- address =
+ void* address =
::VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
- if (address)
- return Memory(address, MEM_VIRTUAL);
- UMA_HISTOGRAM_SPARSE_SLOWLY("UMA.LocalPersistentMemoryAllocator.Failures.Win",
- ::GetLastError());
+ DPCHECK(address);
+ return address;
#elif defined(OS_POSIX)
// MAP_ANON is deprecated on Linux but MAP_ANONYMOUS is not universal on Mac.
// MAP_SHARED is not available on Linux <2.4 but required on Mac.
- address = ::mmap(nullptr, size, PROT_READ | PROT_WRITE,
- MAP_ANON | MAP_SHARED, -1, 0);
- if (address != MAP_FAILED)
- return Memory(address, MEM_VIRTUAL);
- UMA_HISTOGRAM_SPARSE_SLOWLY(
- "UMA.LocalPersistentMemoryAllocator.Failures.Posix", errno);
+ void* address = ::mmap(nullptr, size, PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_SHARED, -1, 0);
+ DPCHECK(MAP_FAILED != address);
+ return address;
#else
#error This architecture is not (yet) supported.
#endif
-
- // As a last resort, just allocate the memory from the heap. This will
- // achieve the same basic result but the acquired memory has to be
- // explicitly zeroed and thus realized immediately (i.e. all pages are
- // added to the process now istead of only when first accessed).
- address = malloc(size);
- DPCHECK(address);
- memset(address, 0, size);
- return Memory(address, MEM_MALLOC);
}
// static
void LocalPersistentMemoryAllocator::DeallocateLocalMemory(void* memory,
- size_t size,
- MemoryType type) {
- if (type == MEM_MALLOC) {
- free(memory);
- return;
- }
-
- DCHECK_EQ(MEM_VIRTUAL, type);
+ size_t size) {
#if defined(OS_WIN)
BOOL success = ::VirtualFree(memory, 0, MEM_DECOMMIT);
- DCHECK(success);
+ DPCHECK(success);
#elif defined(OS_POSIX)
int result = ::munmap(memory, size);
- DCHECK_EQ(0, result);
+ DPCHECK(0 == result);
#else
#error This architecture is not (yet) supported.
#endif
@@ -955,13 +783,12 @@ SharedPersistentMemoryAllocator::SharedPersistentMemoryAllocator(
uint64_t id,
base::StringPiece name,
bool read_only)
- : PersistentMemoryAllocator(
- Memory(static_cast<uint8_t*>(memory->memory()), MEM_SHARED),
- memory->mapped_size(),
- 0,
- id,
- name,
- read_only),
+ : PersistentMemoryAllocator(static_cast<uint8_t*>(memory->memory()),
+ memory->mapped_size(),
+ 0,
+ id,
+ name,
+ read_only),
shared_memory_(std::move(memory)) {}
SharedPersistentMemoryAllocator::~SharedPersistentMemoryAllocator() {}
@@ -982,13 +809,12 @@ FilePersistentMemoryAllocator::FilePersistentMemoryAllocator(
uint64_t id,
base::StringPiece name,
bool read_only)
- : PersistentMemoryAllocator(
- Memory(const_cast<uint8_t*>(file->data()), MEM_FILE),
- max_size != 0 ? max_size : file->length(),
- 0,
- id,
- name,
- read_only),
+ : PersistentMemoryAllocator(const_cast<uint8_t*>(file->data()),
+ max_size != 0 ? max_size : file->length(),
+ 0,
+ id,
+ name,
+ read_only),
mapped_file_(std::move(file)) {}
FilePersistentMemoryAllocator::~FilePersistentMemoryAllocator() {}
diff --git a/base/metrics/persistent_memory_allocator.h b/base/metrics/persistent_memory_allocator.h
index b38f284ff4..2fc0d2d0da 100644
--- a/base/metrics/persistent_memory_allocator.h
+++ b/base/metrics/persistent_memory_allocator.h
@@ -9,7 +9,6 @@
#include <atomic>
#include <memory>
-#include <type_traits>
#include "base/atomicops.h"
#include "base/base_export.h"
@@ -48,50 +47,6 @@ class SharedMemory;
// Note that memory not in active use is not accessed so it is possible to
// use virtual memory, including memory-mapped files, as backing storage with
// the OS "pinning" new (zeroed) physical RAM pages only as they are needed.
-//
-// OBJECTS: Although the allocator can be used in a "malloc" sense, fetching
-// character arrays and manipulating that memory manually, the better way is
-// generally to use the "object" methods to create and manage allocations. In
-// this way the sizing, type-checking, and construction are all automatic. For
-// this to work, however, every type of stored object must define two public
-// "constexpr" values, kPersistentTypeId and kExpectedInstanceSize, as such:
-//
-// struct MyPersistentObjectType {
-// // SHA1(MyPersistentObjectType): Increment this if structure changes!
-// static constexpr uint32_t kPersistentTypeId = 0x3E15F6DE + 1;
-//
-// // Expected size for 32/64-bit check. Update this if structure changes!
-// static constexpr size_t kExpectedInstanceSize = 20;
-//
-// ...
-// };
-//
-// kPersistentTypeId: This value is an arbitrary identifier that allows the
-// identification of these objects in the allocator, including the ability
-// to find them via iteration. The number is arbitrary but using the first
-// four bytes of the SHA1 hash of the type name means that there shouldn't
-// be any conflicts with other types that may also be stored in the memory.
-// The fully qualified name (e.g. base::debug::MyPersistentObjectType) could
-// be used to generate the hash if the type name seems common. Use a command
-// like this to get the hash: echo -n "MyPersistentObjectType" | sha1sum
-// If the structure layout changes, ALWAYS increment this number so that
-// newer versions of the code don't try to interpret persistent data written
-// by older versions with a different layout.
-//
-// kExpectedInstanceSize: This value is the hard-coded number that matches
-// what sizeof(T) would return. By providing it explicitly, the allocator can
-// verify that the structure is compatible between both 32-bit and 64-bit
-// versions of the code.
-//
-// Using New manages the memory and then calls the default constructor for the
-// object. Given that objects are persistent, no destructor is ever called
-// automatically though a caller can explicitly call Delete to destruct it and
-// change the type to something indicating it is no longer in use.
-//
-// Though persistent memory segments are transferrable between programs built
-// for different natural word widths, they CANNOT be exchanged between CPUs
-// of different endianess. Attempts to do so will simply see the existing data
-// as corrupt and refuse to access any of it.
class BASE_EXPORT PersistentMemoryAllocator {
public:
typedef uint32_t Reference;
@@ -101,11 +56,6 @@ class BASE_EXPORT PersistentMemoryAllocator {
// That means that multiple threads can share an iterator and the same
// reference will not be returned twice.
//
- // The order of the items returned by an iterator matches the order in which
- // MakeIterable() was called on them. Once an allocation is made iterable,
- // it is always such so the only possible difference between successive
- // iterations is for more to be added to the end.
- //
// Iteration, in general, is tolerant of corrupted memory. It will return
// what it can and stop only when corruption forces it to. Bad corruption
// could cause the same object to be returned many times but it will
@@ -126,17 +76,6 @@ class BASE_EXPORT PersistentMemoryAllocator {
Iterator(const PersistentMemoryAllocator* allocator,
Reference starting_after);
- // Resets the iterator back to the beginning.
- void Reset();
-
- // Resets the iterator, resuming from the |starting_after| reference.
- void Reset(Reference starting_after);
-
- // Returns the previously retrieved reference, or kReferenceNull if none.
- // If constructor or reset with a starting_after location, this will return
- // that value.
- Reference GetLast();
-
// Gets the next iterable, storing that type in |type_return|. The actual
// return value is a reference to the allocation inside the allocator or
// zero if there are no more. GetNext() may still be called again at a
@@ -149,18 +88,6 @@ class BASE_EXPORT PersistentMemoryAllocator {
// calls to GetNext() meaning it's possible to completely miss entries.
Reference GetNextOfType(uint32_t type_match);
- // As above but works using object type.
- template <typename T>
- Reference GetNextOfType() {
- return GetNextOfType(T::kPersistentTypeId);
- }
-
- // As above but works using objects and returns null if not found.
- template <typename T>
- const T* GetNextOfObject() {
- return GetAsObject<T>(GetNextOfType<T>());
- }
-
// Converts references to objects. This is a convenience method so that
// users of the iterator don't need to also have their own pointer to the
// allocator over which the iterator runs in order to retrieve objects.
@@ -169,27 +96,8 @@ class BASE_EXPORT PersistentMemoryAllocator {
// non-const (external) pointer to the same allocator (or use const_cast
// to remove the qualifier).
template <typename T>
- const T* GetAsObject(Reference ref) const {
- return allocator_->GetAsObject<T>(ref);
- }
-
- // Similar to GetAsObject() but converts references to arrays of things.
- template <typename T>
- const T* GetAsArray(Reference ref, uint32_t type_id, size_t count) const {
- return allocator_->GetAsArray<T>(ref, type_id, count);
- }
-
- // Convert a generic pointer back into a reference. A null reference will
- // be returned if |memory| is not inside the persistent segment or does not
- // point to an object of the specified |type_id|.
- Reference GetAsReference(const void* memory, uint32_t type_id) const {
- return allocator_->GetAsReference(memory, type_id);
- }
-
- // As above but convert an object back into a reference.
- template <typename T>
- Reference GetAsReference(const T* obj) const {
- return allocator_->GetAsReference(obj);
+ const T* GetAsObject(Reference ref, uint32_t type_id) const {
+ return allocator_->GetAsObject<T>(ref, type_id);
}
private:
@@ -212,21 +120,11 @@ class BASE_EXPORT PersistentMemoryAllocator {
};
enum : Reference {
- // A common "null" reference value.
- kReferenceNull = 0,
+ kReferenceNull = 0 // A common "null" reference value.
};
enum : uint32_t {
- // A value that will match any type when doing lookups.
- kTypeIdAny = 0x00000000,
-
- // A value indicating that the type is in transition. Work is being done
- // on the contents to prepare it for a new type to come.
- kTypeIdTransitioning = 0xFFFFFFFF,
- };
-
- enum : size_t {
- kSizeAny = 1 // Constant indicating that any array size is acceptable.
+ kTypeIdAny = 0 // Match any type-id inside GetAsObject().
};
// This is the standard file extension (suitable for being passed to the
@@ -289,7 +187,7 @@ class BASE_EXPORT PersistentMemoryAllocator {
//
// IMPORTANT: Callers must update tools/metrics/histograms/histograms.xml
// with the following histograms:
- // UMA.PersistentAllocator.name.Errors
+ // UMA.PersistentAllocator.name.Allocs
// UMA.PersistentAllocator.name.UsedPct
void CreateTrackingHistograms(base::StringPiece name);
@@ -310,27 +208,6 @@ class BASE_EXPORT PersistentMemoryAllocator {
// TIME before accessing it or risk crashing! Once dereferenced, the pointer
// is safe to reuse forever.
//
- // It is essential that the object be of a fixed size. All fields must be of
- // a defined type that does not change based on the compiler or the CPU
- // natural word size. Acceptable are char, float, double, and (u)intXX_t.
- // Unacceptable are int, bool, and wchar_t which are implementation defined
- // with regards to their size.
- //
- // Alignment must also be consistent. A uint64_t after a uint32_t will pad
- // differently between 32 and 64 bit architectures. Either put the bigger
- // elements first, group smaller elements into blocks the size of larger
- // elements, or manually insert padding fields as appropriate for the
- // largest architecture, including at the end.
- //
- // To protected against mistakes, all objects must have the attribute
- // |kExpectedInstanceSize| (static constexpr size_t) that is a hard-coded
- // numerical value -- NNN, not sizeof(T) -- that can be tested. If the
- // instance size is not fixed, at least one build will fail.
- //
- // If the size of a structure changes, the type-ID used to recognize it
- // should also change so later versions of the code don't try to read
- // incompatible structures from earlier versions.
- //
// NOTE: Though this method will guarantee that an object of the specified
// type can be accessed without going outside the bounds of the memory
// segment, it makes no guarantees of the validity of the data within the
@@ -343,51 +220,19 @@ class BASE_EXPORT PersistentMemoryAllocator {
// nature of that keyword to the caller. It can add it back, if necessary,
// based on knowledge of how the allocator is being used.
template <typename T>
- T* GetAsObject(Reference ref) {
- static_assert(std::is_standard_layout<T>::value, "only standard objects");
- static_assert(!std::is_array<T>::value, "use GetAsArray<>()");
- static_assert(T::kExpectedInstanceSize == sizeof(T), "inconsistent size");
- return const_cast<T*>(reinterpret_cast<volatile T*>(
- GetBlockData(ref, T::kPersistentTypeId, sizeof(T))));
- }
- template <typename T>
- const T* GetAsObject(Reference ref) const {
- static_assert(std::is_standard_layout<T>::value, "only standard objects");
- static_assert(!std::is_array<T>::value, "use GetAsArray<>()");
- static_assert(T::kExpectedInstanceSize == sizeof(T), "inconsistent size");
- return const_cast<const T*>(reinterpret_cast<const volatile T*>(
- GetBlockData(ref, T::kPersistentTypeId, sizeof(T))));
- }
-
- // Like GetAsObject but get an array of simple, fixed-size types.
- //
- // Use a |count| of the required number of array elements, or kSizeAny.
- // GetAllocSize() can be used to calculate the upper bound but isn't reliable
- // because padding can make space for extra elements that were not written.
- //
- // Remember that an array of char is a string but may not be NUL terminated.
- //
- // There are no compile-time or run-time checks to ensure 32/64-bit size
- // compatibilty when using these accessors. Only use fixed-size types such
- // as char, float, double, or (u)intXX_t.
- template <typename T>
- T* GetAsArray(Reference ref, uint32_t type_id, size_t count) {
- static_assert(std::is_fundamental<T>::value, "use GetAsObject<>()");
- return const_cast<T*>(reinterpret_cast<volatile T*>(
- GetBlockData(ref, type_id, count * sizeof(T))));
+ T* GetAsObject(Reference ref, uint32_t type_id) {
+ static_assert(!std::is_polymorphic<T>::value, "no polymorphic objects");
+ return const_cast<T*>(
+ reinterpret_cast<volatile T*>(GetBlockData(ref, type_id, sizeof(T))));
}
template <typename T>
- const T* GetAsArray(Reference ref, uint32_t type_id, size_t count) const {
- static_assert(std::is_fundamental<T>::value, "use GetAsObject<>()");
- return const_cast<const char*>(reinterpret_cast<const volatile T*>(
- GetBlockData(ref, type_id, count * sizeof(T))));
+ const T* GetAsObject(Reference ref, uint32_t type_id) const {
+ static_assert(!std::is_polymorphic<T>::value, "no polymorphic objects");
+ return const_cast<const T*>(
+ reinterpret_cast<const volatile T*>(GetBlockData(
+ ref, type_id, sizeof(T))));
}
- // Get the corresponding reference for an object held in persistent memory.
- // If the |memory| is not valid or the type does not match, a kReferenceNull
- // result will be returned.
- Reference GetAsReference(const void* memory, uint32_t type_id) const;
-
// Get the number of bytes allocated to a block. This is useful when storing
// arrays in order to validate the ending boundary. The returned value will
// include any padding added to achieve the required alignment and so could
@@ -399,22 +244,14 @@ class BASE_EXPORT PersistentMemoryAllocator {
// even though the memory stays valid and allocated. Changing the type is
// an atomic compare/exchange and so requires knowing the existing value.
// It will return false if the existing type is not what is expected.
- //
- // Changing the type doesn't mean the data is compatible with the new type.
- // Passing true for |clear| will zero the memory after the type has been
- // changed away from |from_type_id| but before it becomes |to_type_id| meaning
- // that it is done in a manner that is thread-safe. Memory is guaranteed to
- // be zeroed atomically by machine-word in a monotonically increasing order.
- //
- // It will likely be necessary to reconstruct the type before it can be used.
- // Changing the type WILL NOT invalidate existing pointers to the data, either
- // in this process or others, so changing the data structure could have
- // unpredicatable results. USE WITH CARE!
uint32_t GetType(Reference ref) const;
- bool ChangeType(Reference ref,
- uint32_t to_type_id,
- uint32_t from_type_id,
- bool clear);
+ bool ChangeType(Reference ref, uint32_t to_type_id, uint32_t from_type_id);
+
+ // Reserve space in the memory segment of the desired |size| and |type_id|.
+ // A return value of zero indicates the allocation failed, otherwise the
+ // returned reference can be used by any process to get a real pointer via
+ // the GetAsObject() call.
+ Reference Allocate(size_t size, uint32_t type_id);
// Allocated objects can be added to an internal list that can then be
// iterated over by other processes. If an allocated object can be found
@@ -423,7 +260,6 @@ class BASE_EXPORT PersistentMemoryAllocator {
// succeeds unless corruption is detected; check IsCorrupted() to find out.
// Once an object is made iterable, its position in iteration can never
// change; new iterable objects will always be added after it in the series.
- // Changing the type does not alter its "iterable" status.
void MakeIterable(Reference ref);
// Get the information about the amount of free space in the allocator. The
@@ -450,138 +286,8 @@ class BASE_EXPORT PersistentMemoryAllocator {
// called before such information is to be displayed or uploaded.
void UpdateTrackingHistograms();
- // While the above works much like malloc & free, these next methods provide
- // an "object" interface similar to new and delete.
-
- // Reserve space in the memory segment of the desired |size| and |type_id|.
- // A return value of zero indicates the allocation failed, otherwise the
- // returned reference can be used by any process to get a real pointer via
- // the GetAsObject() or GetAsArray calls.
- Reference Allocate(size_t size, uint32_t type_id);
-
- // Allocate and construct an object in persistent memory. The type must have
- // both (size_t) kExpectedInstanceSize and (uint32_t) kPersistentTypeId
- // static constexpr fields that are used to ensure compatibility between
- // software versions. An optional size parameter can be specified to force
- // the allocation to be bigger than the size of the object; this is useful
- // when the last field is actually variable length.
- template <typename T>
- T* New(size_t size) {
- if (size < sizeof(T))
- size = sizeof(T);
- Reference ref = Allocate(size, T::kPersistentTypeId);
- void* mem =
- const_cast<void*>(GetBlockData(ref, T::kPersistentTypeId, size));
- if (!mem)
- return nullptr;
- DCHECK_EQ(0U, reinterpret_cast<uintptr_t>(mem) & (ALIGNOF(T) - 1));
- return new (mem) T();
- }
- template <typename T>
- T* New() {
- return New<T>(sizeof(T));
- }
-
- // Similar to New, above, but construct the object out of an existing memory
- // block and of an expected type. If |clear| is true, memory will be zeroed
- // before construction. Though this is not standard object behavior, it
- // is present to match with new allocations that always come from zeroed
- // memory. Anything previously present simply ceases to exist; no destructor
- // is called for it so explicitly Delete() the old object first if need be.
- // Calling this will not invalidate existing pointers to the object, either
- // in this process or others, so changing the object could have unpredictable
- // results. USE WITH CARE!
- template <typename T>
- T* New(Reference ref, uint32_t from_type_id, bool clear) {
- DCHECK_LE(sizeof(T), GetAllocSize(ref)) << "alloc not big enough for obj";
- // Make sure the memory is appropriate. This won't be used until after
- // the type is changed but checking first avoids the possibility of having
- // to change the type back.
- void* mem = const_cast<void*>(GetBlockData(ref, 0, sizeof(T)));
- if (!mem)
- return nullptr;
- // Ensure the allocator's internal alignment is sufficient for this object.
- // This protects against coding errors in the allocator.
- DCHECK_EQ(0U, reinterpret_cast<uintptr_t>(mem) & (ALIGNOF(T) - 1));
- // Change the type, clearing the memory if so desired. The new type is
- // "transitioning" so that there is no race condition with the construction
- // of the object should another thread be simultaneously iterating over
- // data. This will "acquire" the memory so no changes get reordered before
- // it.
- if (!ChangeType(ref, kTypeIdTransitioning, from_type_id, clear))
- return nullptr;
- // Construct an object of the desired type on this memory, just as if
- // New() had been called to create it.
- T* obj = new (mem) T();
- // Finally change the type to the desired one. This will "release" all of
- // the changes above and so provide a consistent view to other threads.
- bool success =
- ChangeType(ref, T::kPersistentTypeId, kTypeIdTransitioning, false);
- DCHECK(success);
- return obj;
- }
-
- // Deletes an object by destructing it and then changing the type to a
- // different value (default 0).
- template <typename T>
- void Delete(T* obj, uint32_t new_type) {
- // Get the reference for the object.
- Reference ref = GetAsReference<T>(obj);
- // First change the type to "transitioning" so there is no race condition
- // where another thread could find the object through iteration while it
- // is been destructed. This will "acquire" the memory so no changes get
- // reordered before it. It will fail if |ref| is invalid.
- if (!ChangeType(ref, kTypeIdTransitioning, T::kPersistentTypeId, false))
- return;
- // Destruct the object.
- obj->~T();
- // Finally change the type to the desired value. This will "release" all
- // the changes above.
- bool success = ChangeType(ref, new_type, kTypeIdTransitioning, false);
- DCHECK(success);
- }
- template <typename T>
- void Delete(T* obj) {
- Delete<T>(obj, 0);
- }
-
- // As above but works with objects allocated from persistent memory.
- template <typename T>
- Reference GetAsReference(const T* obj) const {
- return GetAsReference(obj, T::kPersistentTypeId);
- }
-
- // As above but works with an object allocated from persistent memory.
- template <typename T>
- void MakeIterable(const T* obj) {
- MakeIterable(GetAsReference<T>(obj));
- }
-
protected:
- enum MemoryType {
- MEM_EXTERNAL,
- MEM_MALLOC,
- MEM_VIRTUAL,
- MEM_SHARED,
- MEM_FILE,
- };
-
- struct Memory {
- Memory(void* b, MemoryType t) : base(b), type(t) {}
-
- void* base;
- MemoryType type;
- };
-
- // Constructs the allocator. Everything is the same as the public allocator
- // except |memory| which is a structure with additional information besides
- // the base address.
- PersistentMemoryAllocator(Memory memory, size_t size, size_t page_size,
- uint64_t id, base::StringPiece name,
- bool readonly);
-
volatile char* const mem_base_; // Memory base. (char so sizeof guaranteed 1)
- const MemoryType mem_type_; // Type of memory allocation.
const uint32_t mem_size_; // Size of entire memory segment.
const uint32_t mem_page_; // Page size allocations shouldn't cross.
@@ -626,15 +332,11 @@ class BASE_EXPORT PersistentMemoryAllocator {
ref, type_id, size));
}
- // Record an error in the internal histogram.
- void RecordError(int error) const;
-
- const bool readonly_; // Indicates access to read-only memory.
- mutable std::atomic<bool> corrupt_; // Local version of "corrupted" flag.
+ const bool readonly_; // Indicates access to read-only memory.
+ std::atomic<bool> corrupt_; // Local version of "corrupted" flag.
HistogramBase* allocs_histogram_; // Histogram recording allocs.
HistogramBase* used_histogram_; // Histogram recording used space.
- HistogramBase* errors_histogram_; // Histogram recording errors.
friend class PersistentMemoryAllocatorTest;
FRIEND_TEST_ALL_PREFIXES(PersistentMemoryAllocatorTest, AllocateAndIterate);
@@ -657,10 +359,10 @@ class BASE_EXPORT LocalPersistentMemoryAllocator
// Allocates a block of local memory of the specified |size|, ensuring that
// the memory will not be physically allocated until accessed and will read
// as zero when that happens.
- static Memory AllocateLocalMemory(size_t size);
+ static void* AllocateLocalMemory(size_t size);
// Deallocates a block of local |memory| of the specified |size|.
- static void DeallocateLocalMemory(void* memory, size_t size, MemoryType type);
+ static void DeallocateLocalMemory(void* memory, size_t size);
DISALLOW_COPY_AND_ASSIGN(LocalPersistentMemoryAllocator);
};
@@ -680,7 +382,7 @@ class BASE_EXPORT SharedPersistentMemoryAllocator
SharedMemory* shared_memory() { return shared_memory_.get(); }
- // Ensure that the memory isn't so invalid that it would crash when passing it
+ // Ensure that the memory isn't so invalid that it won't crash when passing it
// to the allocator. This doesn't guarantee the data is valid, just that it
// won't cause the program to abort. The existing IsCorrupt() call will handle
// the rest.
@@ -709,7 +411,7 @@ class BASE_EXPORT FilePersistentMemoryAllocator
bool read_only);
~FilePersistentMemoryAllocator() override;
- // Ensure that the file isn't so invalid that it would crash when passing it
+ // Ensure that the file isn't so invalid that it won't crash when passing it
// to the allocator. This doesn't guarantee the file is valid, just that it
// won't cause the program to abort. The existing IsCorrupt() call will handle
// the rest.
diff --git a/base/metrics/persistent_memory_allocator_unittest.cc b/base/metrics/persistent_memory_allocator_unittest.cc
index d12e00f6d6..a3d90c2612 100644
--- a/base/metrics/persistent_memory_allocator_unittest.cc
+++ b/base/metrics/persistent_memory_allocator_unittest.cc
@@ -40,20 +40,16 @@ class PersistentMemoryAllocatorTest : public testing::Test {
uint32_t kAllocAlignment;
struct TestObject1 {
- static constexpr uint32_t kPersistentTypeId = 1;
- static constexpr size_t kExpectedInstanceSize = 4 + 1 + 3;
- int32_t onething;
+ int onething;
char oranother;
};
struct TestObject2 {
- static constexpr uint32_t kPersistentTypeId = 2;
- static constexpr size_t kExpectedInstanceSize = 8 + 4 + 4 + 8 + 8;
- int64_t thiis;
- int32_t that;
+ int thiis;
+ long that;
float andthe;
- double other;
- char thing[8];
+ char other;
+ double thing;
};
PersistentMemoryAllocatorTest() {
@@ -67,6 +63,7 @@ class PersistentMemoryAllocatorTest : public testing::Test {
allocator_.reset(new PersistentMemoryAllocator(
mem_segment_.get(), TEST_MEMORY_SIZE, TEST_MEMORY_PAGE,
TEST_ID, TEST_NAME, false));
+ allocator_->CreateTrackingHistograms(allocator_->Name());
}
void TearDown() override {
@@ -93,13 +90,14 @@ class PersistentMemoryAllocatorTest : public testing::Test {
};
TEST_F(PersistentMemoryAllocatorTest, AllocateAndIterate) {
- allocator_->CreateTrackingHistograms(allocator_->Name());
-
std::string base_name(TEST_NAME);
EXPECT_EQ(TEST_ID, allocator_->Id());
EXPECT_TRUE(allocator_->used_histogram_);
EXPECT_EQ("UMA.PersistentAllocator." + base_name + ".UsedPct",
allocator_->used_histogram_->histogram_name());
+ EXPECT_TRUE(allocator_->allocs_histogram_);
+ EXPECT_EQ("UMA.PersistentAllocator." + base_name + ".Allocs",
+ allocator_->allocs_histogram_->histogram_name());
// Get base memory info for later comparison.
PersistentMemoryAllocator::MemoryInfo meminfo0;
@@ -109,12 +107,10 @@ TEST_F(PersistentMemoryAllocatorTest, AllocateAndIterate) {
// Validate allocation of test object and make sure it can be referenced
// and all metadata looks correct.
- TestObject1* obj1 = allocator_->New<TestObject1>();
- ASSERT_TRUE(obj1);
- Reference block1 = allocator_->GetAsReference(obj1);
- ASSERT_NE(0U, block1);
- EXPECT_NE(nullptr, allocator_->GetAsObject<TestObject1>(block1));
- EXPECT_EQ(nullptr, allocator_->GetAsObject<TestObject2>(block1));
+ Reference block1 = allocator_->Allocate(sizeof(TestObject1), 1);
+ EXPECT_NE(0U, block1);
+ EXPECT_NE(nullptr, allocator_->GetAsObject<TestObject1>(block1, 1));
+ EXPECT_EQ(nullptr, allocator_->GetAsObject<TestObject2>(block1, 1));
EXPECT_LE(sizeof(TestObject1), allocator_->GetAllocSize(block1));
EXPECT_GT(sizeof(TestObject1) + kAllocAlignment,
allocator_->GetAllocSize(block1));
@@ -123,38 +119,21 @@ TEST_F(PersistentMemoryAllocatorTest, AllocateAndIterate) {
EXPECT_EQ(meminfo0.total, meminfo1.total);
EXPECT_GT(meminfo0.free, meminfo1.free);
- // Verify that pointers can be turned back into references and that invalid
- // addresses return null.
- char* memory1 = allocator_->GetAsArray<char>(block1, 1, 1);
- ASSERT_TRUE(memory1);
- EXPECT_EQ(block1, allocator_->GetAsReference(memory1, 0));
- EXPECT_EQ(block1, allocator_->GetAsReference(memory1, 1));
- EXPECT_EQ(0U, allocator_->GetAsReference(memory1, 2));
- EXPECT_EQ(0U, allocator_->GetAsReference(memory1 + 1, 0));
- EXPECT_EQ(0U, allocator_->GetAsReference(memory1 + 16, 0));
- EXPECT_EQ(0U, allocator_->GetAsReference(nullptr, 0));
- EXPECT_EQ(0U, allocator_->GetAsReference(&base_name, 0));
-
// Ensure that the test-object can be made iterable.
PersistentMemoryAllocator::Iterator iter1a(allocator_.get());
- EXPECT_EQ(0U, iter1a.GetLast());
uint32_t type;
EXPECT_EQ(0U, iter1a.GetNext(&type));
allocator_->MakeIterable(block1);
EXPECT_EQ(block1, iter1a.GetNext(&type));
EXPECT_EQ(1U, type);
- EXPECT_EQ(block1, iter1a.GetLast());
EXPECT_EQ(0U, iter1a.GetNext(&type));
- EXPECT_EQ(block1, iter1a.GetLast());
// Create second test-object and ensure everything is good and it cannot
// be confused with test-object of another type.
- TestObject2* obj2 = allocator_->New<TestObject2>();
- ASSERT_TRUE(obj2);
- Reference block2 = allocator_->GetAsReference(obj2);
- ASSERT_NE(0U, block2);
- EXPECT_NE(nullptr, allocator_->GetAsObject<TestObject2>(block2));
- EXPECT_EQ(nullptr, allocator_->GetAsObject<TestObject1>(block2));
+ Reference block2 = allocator_->Allocate(sizeof(TestObject2), 2);
+ EXPECT_NE(0U, block2);
+ EXPECT_NE(nullptr, allocator_->GetAsObject<TestObject2>(block2, 2));
+ EXPECT_EQ(nullptr, allocator_->GetAsObject<TestObject2>(block2, 1));
EXPECT_LE(sizeof(TestObject2), allocator_->GetAllocSize(block2));
EXPECT_GT(sizeof(TestObject2) + kAllocAlignment,
allocator_->GetAllocSize(block2));
@@ -164,27 +143,9 @@ TEST_F(PersistentMemoryAllocatorTest, AllocateAndIterate) {
EXPECT_GT(meminfo1.free, meminfo2.free);
// Ensure that second test-object can also be made iterable.
- allocator_->MakeIterable(obj2);
+ allocator_->MakeIterable(block2);
EXPECT_EQ(block2, iter1a.GetNext(&type));
EXPECT_EQ(2U, type);
- EXPECT_EQ(block2, iter1a.GetLast());
- EXPECT_EQ(0U, iter1a.GetNext(&type));
- EXPECT_EQ(block2, iter1a.GetLast());
-
- // Check that the iterator can be reset to the beginning.
- iter1a.Reset();
- EXPECT_EQ(0U, iter1a.GetLast());
- EXPECT_EQ(block1, iter1a.GetNext(&type));
- EXPECT_EQ(block1, iter1a.GetLast());
- EXPECT_EQ(block2, iter1a.GetNext(&type));
- EXPECT_EQ(block2, iter1a.GetLast());
- EXPECT_EQ(0U, iter1a.GetNext(&type));
-
- // Check that the iterator can be reset to an arbitrary location.
- iter1a.Reset(block1);
- EXPECT_EQ(block1, iter1a.GetLast());
- EXPECT_EQ(block2, iter1a.GetNext(&type));
- EXPECT_EQ(block2, iter1a.GetLast());
EXPECT_EQ(0U, iter1a.GetNext(&type));
// Check that iteration can begin after an arbitrary location.
@@ -203,11 +164,26 @@ TEST_F(PersistentMemoryAllocatorTest, AllocateAndIterate) {
EXPECT_TRUE(used_samples);
EXPECT_EQ(1, used_samples->TotalCount());
- // Check that an object's type can be changed.
+ // Check the internal histogram record of allocation requests.
+ std::unique_ptr<HistogramSamples> allocs_samples(
+ allocator_->allocs_histogram_->SnapshotSamples());
+ EXPECT_TRUE(allocs_samples);
+ EXPECT_EQ(2, allocs_samples->TotalCount());
+ EXPECT_EQ(0, allocs_samples->GetCount(0));
+ EXPECT_EQ(1, allocs_samples->GetCount(sizeof(TestObject1)));
+ EXPECT_EQ(1, allocs_samples->GetCount(sizeof(TestObject2)));
+#if !DCHECK_IS_ON() // DCHECK builds will die at a NOTREACHED().
+ EXPECT_EQ(0U, allocator_->Allocate(TEST_MEMORY_SIZE + 1, 0));
+ allocs_samples = allocator_->allocs_histogram_->SnapshotSamples();
+ EXPECT_EQ(3, allocs_samples->TotalCount());
+ EXPECT_EQ(1, allocs_samples->GetCount(0));
+#endif
+
+ // Check that an objcet's type can be changed.
EXPECT_EQ(2U, allocator_->GetType(block2));
- allocator_->ChangeType(block2, 3, 2, false);
+ allocator_->ChangeType(block2, 3, 2);
EXPECT_EQ(3U, allocator_->GetType(block2));
- allocator_->New<TestObject2>(block2, 3, false);
+ allocator_->ChangeType(block2, 2, 3);
EXPECT_EQ(2U, allocator_->GetType(block2));
// Create second allocator (read/write) using the same memory segment.
@@ -216,14 +192,16 @@ TEST_F(PersistentMemoryAllocatorTest, AllocateAndIterate) {
TEST_MEMORY_PAGE, 0, "", false));
EXPECT_EQ(TEST_ID, allocator2->Id());
EXPECT_FALSE(allocator2->used_histogram_);
+ EXPECT_FALSE(allocator2->allocs_histogram_);
+ EXPECT_NE(allocator2->allocs_histogram_, allocator_->allocs_histogram_);
// Ensure that iteration and access through second allocator works.
PersistentMemoryAllocator::Iterator iter2(allocator2.get());
EXPECT_EQ(block1, iter2.GetNext(&type));
EXPECT_EQ(block2, iter2.GetNext(&type));
EXPECT_EQ(0U, iter2.GetNext(&type));
- EXPECT_NE(nullptr, allocator2->GetAsObject<TestObject1>(block1));
- EXPECT_NE(nullptr, allocator2->GetAsObject<TestObject2>(block2));
+ EXPECT_NE(nullptr, allocator2->GetAsObject<TestObject1>(block1, 1));
+ EXPECT_NE(nullptr, allocator2->GetAsObject<TestObject2>(block2, 2));
// Create a third allocator (read-only) using the same memory segment.
std::unique_ptr<const PersistentMemoryAllocator> allocator3(
@@ -231,29 +209,20 @@ TEST_F(PersistentMemoryAllocatorTest, AllocateAndIterate) {
TEST_MEMORY_PAGE, 0, "", true));
EXPECT_EQ(TEST_ID, allocator3->Id());
EXPECT_FALSE(allocator3->used_histogram_);
+ EXPECT_FALSE(allocator3->allocs_histogram_);
// Ensure that iteration and access through third allocator works.
PersistentMemoryAllocator::Iterator iter3(allocator3.get());
EXPECT_EQ(block1, iter3.GetNext(&type));
EXPECT_EQ(block2, iter3.GetNext(&type));
EXPECT_EQ(0U, iter3.GetNext(&type));
- EXPECT_NE(nullptr, allocator3->GetAsObject<TestObject1>(block1));
- EXPECT_NE(nullptr, allocator3->GetAsObject<TestObject2>(block2));
+ EXPECT_NE(nullptr, allocator3->GetAsObject<TestObject1>(block1, 1));
+ EXPECT_NE(nullptr, allocator3->GetAsObject<TestObject2>(block2, 2));
// Ensure that GetNextOfType works.
PersistentMemoryAllocator::Iterator iter1c(allocator_.get());
- EXPECT_EQ(block2, iter1c.GetNextOfType<TestObject2>());
+ EXPECT_EQ(block2, iter1c.GetNextOfType(2));
EXPECT_EQ(0U, iter1c.GetNextOfType(2));
-
- // Ensure that GetNextOfObject works.
- PersistentMemoryAllocator::Iterator iter1d(allocator_.get());
- EXPECT_EQ(obj2, iter1d.GetNextOfObject<TestObject2>());
- EXPECT_EQ(nullptr, iter1d.GetNextOfObject<TestObject2>());
-
- // Ensure that deleting an object works.
- allocator_->Delete(obj2);
- PersistentMemoryAllocator::Iterator iter1z(allocator_.get());
- EXPECT_EQ(nullptr, iter1z.GetNextOfObject<TestObject2>());
}
TEST_F(PersistentMemoryAllocatorTest, PageTest) {
@@ -566,7 +535,7 @@ TEST(SharedPersistentMemoryAllocatorTest, CreationTest) {
r456 = local.Allocate(456, 456);
r789 = local.Allocate(789, 789);
local.MakeIterable(r123);
- local.ChangeType(r456, 654, 456, false);
+ local.ChangeType(r456, 654, 456);
local.MakeIterable(r789);
local.GetMemoryInfo(&meminfo1);
EXPECT_FALSE(local.IsFull());
@@ -635,25 +604,6 @@ TEST(SharedPersistentMemoryAllocatorTest, CreationTest) {
shalloc3.MakeIterable(obj);
EXPECT_EQ(obj, iter2.GetNext(&type));
EXPECT_EQ(42U, type);
-
- // Clear-on-change test.
- Reference data_ref = shalloc3.Allocate(sizeof(int) * 4, 911);
- int* data = shalloc3.GetAsArray<int>(data_ref, 911, 4);
- ASSERT_TRUE(data);
- data[0] = 0;
- data[1] = 1;
- data[2] = 2;
- data[3] = 3;
- ASSERT_TRUE(shalloc3.ChangeType(data_ref, 119, 911, false));
- EXPECT_EQ(0, data[0]);
- EXPECT_EQ(1, data[1]);
- EXPECT_EQ(2, data[2]);
- EXPECT_EQ(3, data[3]);
- ASSERT_TRUE(shalloc3.ChangeType(data_ref, 191, 119, true));
- EXPECT_EQ(0, data[0]);
- EXPECT_EQ(0, data[1]);
- EXPECT_EQ(0, data[2]);
- EXPECT_EQ(0, data[3]);
}
@@ -663,7 +613,7 @@ TEST(SharedPersistentMemoryAllocatorTest, CreationTest) {
TEST(FilePersistentMemoryAllocatorTest, CreationTest) {
ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath file_path = temp_dir.GetPath().AppendASCII("persistent_memory");
+ FilePath file_path = temp_dir.path().AppendASCII("persistent_memory");
PersistentMemoryAllocator::MemoryInfo meminfo1;
Reference r123, r456, r789;
@@ -674,7 +624,7 @@ TEST(FilePersistentMemoryAllocatorTest, CreationTest) {
r456 = local.Allocate(456, 456);
r789 = local.Allocate(789, 789);
local.MakeIterable(r123);
- local.ChangeType(r456, 654, 456, false);
+ local.ChangeType(r456, 654, 456);
local.MakeIterable(r789);
local.GetMemoryInfo(&meminfo1);
EXPECT_FALSE(local.IsFull());
@@ -718,7 +668,7 @@ TEST(FilePersistentMemoryAllocatorTest, CreationTest) {
TEST(FilePersistentMemoryAllocatorTest, ExtendTest) {
ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath file_path = temp_dir.GetPath().AppendASCII("extend_test");
+ FilePath file_path = temp_dir.path().AppendASCII("extend_test");
MemoryMappedFile::Region region = {0, 16 << 10}; // 16KiB maximum size.
// Start with a small but valid file of persistent data.
@@ -784,7 +734,7 @@ TEST(FilePersistentMemoryAllocatorTest, AcceptableTest) {
char filename[100];
for (size_t filesize = minsize; filesize > 0; --filesize) {
strings::SafeSPrintf(filename, "memory_%d_A", filesize);
- FilePath file_path = temp_dir.GetPath().AppendASCII(filename);
+ FilePath file_path = temp_dir.path().AppendASCII(filename);
ASSERT_FALSE(PathExists(file_path));
{
File writer(file_path, File::FLAG_CREATE | File::FLAG_WRITE);
@@ -815,8 +765,7 @@ TEST(FilePersistentMemoryAllocatorTest, AcceptableTest) {
uint32_t type_id;
Reference ref;
while ((ref = iter.GetNext(&type_id)) != 0) {
- const char* data = allocator.GetAsArray<char>(
- ref, 0, PersistentMemoryAllocator::kSizeAny);
+ const char* data = allocator.GetAsObject<char>(ref, 0);
uint32_t type = allocator.GetType(ref);
size_t size = allocator.GetAllocSize(ref);
// Ensure compiler can't optimize-out above variables.
@@ -835,7 +784,7 @@ TEST(FilePersistentMemoryAllocatorTest, AcceptableTest) {
}
strings::SafeSPrintf(filename, "memory_%d_B", filesize);
- file_path = temp_dir.GetPath().AppendASCII(filename);
+ file_path = temp_dir.path().AppendASCII(filename);
ASSERT_FALSE(PathExists(file_path));
{
File writer(file_path, File::FLAG_CREATE | File::FLAG_WRITE);
diff --git a/base/metrics/persistent_sample_map.cc b/base/metrics/persistent_sample_map.cc
index 51cc0c709d..15f83cdb33 100644
--- a/base/metrics/persistent_sample_map.cc
+++ b/base/metrics/persistent_sample_map.cc
@@ -6,7 +6,6 @@
#include "base/logging.h"
#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_macros.h"
#include "base/metrics/persistent_histogram_allocator.h"
#include "base/stl_util.h"
@@ -17,12 +16,6 @@ typedef HistogramBase::Sample Sample;
namespace {
-enum NegativeSampleReason {
- PERSISTENT_SPARSE_HAVE_LOGGED_BUT_NOT_SAMPLE,
- PERSISTENT_SPARSE_SAMPLE_LESS_THAN_LOGGED,
- MAX_NEGATIVE_SAMPLE_REASONS
-};
-
// An iterator for going through a PersistentSampleMap. The logic here is
// identical to that of SampleMapIterator but with different data structures.
// Changes here likely need to be duplicated there.
@@ -89,17 +82,14 @@ void PersistentSampleMapIterator::SkipEmptyBuckets() {
// memory allocator. The "id" must be unique across all maps held by an
// allocator or they will get attached to the wrong sample map.
struct SampleRecord {
- // SHA1(SampleRecord): Increment this if structure changes!
- static constexpr uint32_t kPersistentTypeId = 0x8FE6A69F + 1;
-
- // Expected size for 32/64-bit check.
- static constexpr size_t kExpectedInstanceSize = 16;
-
uint64_t id; // Unique identifier of owner.
Sample value; // The value for which this record holds a count.
Count count; // The count associated with the above value.
};
+// The type-id used to identify sample records inside an allocator.
+const uint32_t kTypeIdSampleRecord = 0x8FE6A69F + 1; // SHA1(SampleRecord) v1
+
} // namespace
PersistentSampleMap::PersistentSampleMap(
@@ -151,12 +141,15 @@ PersistentMemoryAllocator::Reference
PersistentSampleMap::GetNextPersistentRecord(
PersistentMemoryAllocator::Iterator& iterator,
uint64_t* sample_map_id) {
- const SampleRecord* record = iterator.GetNextOfObject<SampleRecord>();
+ PersistentMemoryAllocator::Reference ref =
+ iterator.GetNextOfType(kTypeIdSampleRecord);
+ const SampleRecord* record =
+ iterator.GetAsObject<SampleRecord>(ref, kTypeIdSampleRecord);
if (!record)
return 0;
*sample_map_id = record->id;
- return iterator.GetAsReference(record);
+ return ref;
}
// static
@@ -165,7 +158,11 @@ PersistentSampleMap::CreatePersistentRecord(
PersistentMemoryAllocator* allocator,
uint64_t sample_map_id,
Sample value) {
- SampleRecord* record = allocator->New<SampleRecord>();
+ PersistentMemoryAllocator::Reference ref =
+ allocator->Allocate(sizeof(SampleRecord), kTypeIdSampleRecord);
+ SampleRecord* record =
+ allocator->GetAsObject<SampleRecord>(ref, kTypeIdSampleRecord);
+
if (!record) {
NOTREACHED() << "full=" << allocator->IsFull()
<< ", corrupt=" << allocator->IsCorrupt();
@@ -175,8 +172,6 @@ PersistentSampleMap::CreatePersistentRecord(
record->id = sample_map_id;
record->value = value;
record->count = 0;
-
- PersistentMemoryAllocator::Reference ref = allocator->GetAsReference(record);
allocator->MakeIterable(ref);
return ref;
}
@@ -188,39 +183,11 @@ bool PersistentSampleMap::AddSubtractImpl(SampleCountIterator* iter,
Count count;
for (; !iter->Done(); iter->Next()) {
iter->Get(&min, &max, &count);
- if (count == 0)
- continue;
if (min + 1 != max)
return false; // SparseHistogram only supports bucket with size 1.
-#if 0 // TODO(bcwhite) Re-enable efficient version after crbug.com/682680.
*GetOrCreateSampleCountStorage(min) +=
(op == HistogramSamples::ADD) ? count : -count;
-#else
- if (op == HistogramSamples::ADD) {
- *GetOrCreateSampleCountStorage(min) += count;
- } else {
- // Subtract is used only for determining deltas when reporting which
- // means that it's in the "logged" iterator. It should have an active
- // sample record and thus there is no need to try to create one.
- NegativeSampleReason reason = MAX_NEGATIVE_SAMPLE_REASONS;
- Count* bucket = GetSampleCountStorage(min);
- if (bucket == nullptr) {
- reason = PERSISTENT_SPARSE_HAVE_LOGGED_BUT_NOT_SAMPLE;
- } else {
- if (*bucket < count) {
- reason = PERSISTENT_SPARSE_SAMPLE_LESS_THAN_LOGGED;
- *bucket = 0;
- } else {
- *bucket -= count;
- }
- }
- if (reason != MAX_NEGATIVE_SAMPLE_REASONS) {
- UMA_HISTOGRAM_ENUMERATION("UMA.NegativeSamples.Reason", reason,
- MAX_NEGATIVE_SAMPLE_REASONS);
- }
- }
-#endif
}
return true;
}
@@ -286,7 +253,8 @@ Count* PersistentSampleMap::ImportSamples(Sample until_value,
PersistentMemoryAllocator::Reference ref;
PersistentSampleMapRecords* records = GetRecords();
while ((ref = records->GetNext()) != 0) {
- SampleRecord* record = records->GetAsObject<SampleRecord>(ref);
+ SampleRecord* record =
+ records->GetAsObject<SampleRecord>(ref, kTypeIdSampleRecord);
if (!record)
continue;
diff --git a/base/metrics/persistent_sample_map.h b/base/metrics/persistent_sample_map.h
index 853f862182..3c175db542 100644
--- a/base/metrics/persistent_sample_map.h
+++ b/base/metrics/persistent_sample_map.h
@@ -24,6 +24,7 @@ namespace base {
class PersistentHistogramAllocator;
class PersistentSampleMapRecords;
+class PersistentSparseHistogramDataManager;
// The logic here is similar to that of SampleMap but with different data
// structures. Changes here likely need to be duplicated there.
diff --git a/base/metrics/persistent_sample_map_unittest.cc b/base/metrics/persistent_sample_map_unittest.cc
index d50ab997b2..beb72e5f20 100644
--- a/base/metrics/persistent_sample_map_unittest.cc
+++ b/base/metrics/persistent_sample_map_unittest.cc
@@ -8,7 +8,6 @@
#include "base/memory/ptr_util.h"
#include "base/metrics/persistent_histogram_allocator.h"
-#include "base/test/gtest_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
@@ -16,22 +15,22 @@ namespace {
std::unique_ptr<PersistentHistogramAllocator> CreateHistogramAllocator(
size_t bytes) {
- return MakeUnique<PersistentHistogramAllocator>(
- MakeUnique<LocalPersistentMemoryAllocator>(bytes, 0, ""));
+ return WrapUnique(new PersistentHistogramAllocator(
+ WrapUnique(new LocalPersistentMemoryAllocator(bytes, 0, ""))));
}
std::unique_ptr<PersistentHistogramAllocator> DuplicateHistogramAllocator(
PersistentHistogramAllocator* original) {
- return MakeUnique<PersistentHistogramAllocator>(
- MakeUnique<PersistentMemoryAllocator>(
+ return WrapUnique(
+ new PersistentHistogramAllocator(WrapUnique(new PersistentMemoryAllocator(
const_cast<void*>(original->data()), original->length(), 0,
- original->Id(), original->Name(), false));
+ original->Id(), original->Name(), false))));
}
TEST(PersistentSampleMapTest, AccumulateTest) {
std::unique_ptr<PersistentHistogramAllocator> allocator =
CreateHistogramAllocator(64 << 10); // 64 KiB
- HistogramSamples::LocalMetadata meta;
+ HistogramSamples::Metadata meta;
PersistentSampleMap samples(1, allocator.get(), &meta);
samples.Accumulate(1, 100);
@@ -48,7 +47,7 @@ TEST(PersistentSampleMapTest, AccumulateTest) {
TEST(PersistentSampleMapTest, Accumulate_LargeValuesDontOverflow) {
std::unique_ptr<PersistentHistogramAllocator> allocator =
CreateHistogramAllocator(64 << 10); // 64 KiB
- HistogramSamples::LocalMetadata meta;
+ HistogramSamples::Metadata meta;
PersistentSampleMap samples(1, allocator.get(), &meta);
samples.Accumulate(250000000, 100);
@@ -65,7 +64,7 @@ TEST(PersistentSampleMapTest, Accumulate_LargeValuesDontOverflow) {
TEST(PersistentSampleMapTest, AddSubtractTest) {
std::unique_ptr<PersistentHistogramAllocator> allocator1 =
CreateHistogramAllocator(64 << 10); // 64 KiB
- HistogramSamples::LocalMetadata meta1;
+ HistogramSamples::Metadata meta1;
PersistentSampleMap samples1(1, allocator1.get(), &meta1);
samples1.Accumulate(1, 100);
samples1.Accumulate(2, 100);
@@ -73,7 +72,7 @@ TEST(PersistentSampleMapTest, AddSubtractTest) {
std::unique_ptr<PersistentHistogramAllocator> allocator2 =
DuplicateHistogramAllocator(allocator1.get());
- HistogramSamples::LocalMetadata meta2;
+ HistogramSamples::Metadata meta2;
PersistentSampleMap samples2(2, allocator2.get(), &meta2);
samples2.Accumulate(1, 200);
samples2.Accumulate(2, 200);
@@ -101,7 +100,7 @@ TEST(PersistentSampleMapTest, AddSubtractTest) {
TEST(PersistentSampleMapTest, PersistenceTest) {
std::unique_ptr<PersistentHistogramAllocator> allocator1 =
CreateHistogramAllocator(64 << 10); // 64 KiB
- HistogramSamples::LocalMetadata meta12;
+ HistogramSamples::Metadata meta12;
PersistentSampleMap samples1(12, allocator1.get(), &meta12);
samples1.Accumulate(1, 100);
samples1.Accumulate(2, 200);
@@ -154,7 +153,7 @@ TEST(PersistentSampleMapTest, PersistenceTest) {
TEST(PersistentSampleMapIteratorTest, IterateTest) {
std::unique_ptr<PersistentHistogramAllocator> allocator =
CreateHistogramAllocator(64 << 10); // 64 KiB
- HistogramSamples::LocalMetadata meta;
+ HistogramSamples::Metadata meta;
PersistentSampleMap samples(1, allocator.get(), &meta);
samples.Accumulate(1, 100);
samples.Accumulate(2, 200);
@@ -192,7 +191,7 @@ TEST(PersistentSampleMapIteratorTest, IterateTest) {
TEST(PersistentSampleMapIteratorTest, SkipEmptyRanges) {
std::unique_ptr<PersistentHistogramAllocator> allocator1 =
CreateHistogramAllocator(64 << 10); // 64 KiB
- HistogramSamples::LocalMetadata meta1;
+ HistogramSamples::Metadata meta1;
PersistentSampleMap samples1(1, allocator1.get(), &meta1);
samples1.Accumulate(5, 1);
samples1.Accumulate(10, 2);
@@ -202,7 +201,7 @@ TEST(PersistentSampleMapIteratorTest, SkipEmptyRanges) {
std::unique_ptr<PersistentHistogramAllocator> allocator2 =
DuplicateHistogramAllocator(allocator1.get());
- HistogramSamples::LocalMetadata meta2;
+ HistogramSamples::Metadata meta2;
PersistentSampleMap samples2(2, allocator2.get(), &meta2);
samples2.Accumulate(5, 1);
samples2.Accumulate(20, 4);
@@ -234,10 +233,12 @@ TEST(PersistentSampleMapIteratorTest, SkipEmptyRanges) {
EXPECT_TRUE(it->Done());
}
+// Only run this test on builds that support catching a DCHECK crash.
+#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && GTEST_HAS_DEATH_TEST
TEST(PersistentSampleMapIteratorDeathTest, IterateDoneTest) {
std::unique_ptr<PersistentHistogramAllocator> allocator =
CreateHistogramAllocator(64 << 10); // 64 KiB
- HistogramSamples::LocalMetadata meta;
+ HistogramSamples::Metadata meta;
PersistentSampleMap samples(1, allocator.get(), &meta);
std::unique_ptr<SampleCountIterator> it = samples.Iterator();
@@ -247,14 +248,16 @@ TEST(PersistentSampleMapIteratorDeathTest, IterateDoneTest) {
HistogramBase::Sample min;
HistogramBase::Sample max;
HistogramBase::Count count;
- EXPECT_DCHECK_DEATH(it->Get(&min, &max, &count));
+ EXPECT_DEATH(it->Get(&min, &max, &count), "");
- EXPECT_DCHECK_DEATH(it->Next());
+ EXPECT_DEATH(it->Next(), "");
samples.Accumulate(1, 100);
it = samples.Iterator();
EXPECT_FALSE(it->Done());
}
+#endif
+// (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && GTEST_HAS_DEATH_TEST
} // namespace
} // namespace base
diff --git a/base/metrics/sample_map_unittest.cc b/base/metrics/sample_map_unittest.cc
index 9d7e818bb7..8f577109cd 100644
--- a/base/metrics/sample_map_unittest.cc
+++ b/base/metrics/sample_map_unittest.cc
@@ -6,7 +6,6 @@
#include <memory>
-#include "base/test/gtest_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
diff --git a/base/metrics/sample_vector_unittest.cc b/base/metrics/sample_vector_unittest.cc
index 897ceed520..02e48aac17 100644
--- a/base/metrics/sample_vector_unittest.cc
+++ b/base/metrics/sample_vector_unittest.cc
@@ -12,7 +12,6 @@
#include "base/metrics/bucket_ranges.h"
#include "base/metrics/histogram.h"
-#include "base/test/gtest_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
diff --git a/base/metrics/sparse_histogram.cc b/base/metrics/sparse_histogram.cc
index bee48d4c17..3c1222d2ae 100644
--- a/base/metrics/sparse_histogram.cc
+++ b/base/metrics/sparse_histogram.cc
@@ -68,7 +68,7 @@ HistogramBase* SparseHistogram::FactoryGet(const std::string& name,
ReportHistogramActivity(*histogram, HISTOGRAM_LOOKUP);
}
- CHECK_EQ(SPARSE_HISTOGRAM, histogram->GetHistogramType());
+ DCHECK_EQ(SPARSE_HISTOGRAM, histogram->GetHistogramType());
return histogram;
}
@@ -282,8 +282,8 @@ void SparseHistogram::WriteAsciiHeader(const Count total_count,
"Histogram: %s recorded %d samples",
histogram_name().c_str(),
total_count);
- if (flags())
- StringAppendF(output, " (flags = 0x%x)", flags());
+ if (flags() & ~kHexRangePrintingFlag)
+ StringAppendF(output, " (flags = 0x%x)", flags() & ~kHexRangePrintingFlag);
}
} // namespace base
diff --git a/base/metrics/sparse_histogram.h b/base/metrics/sparse_histogram.h
index 97709ba18f..3b302d6f22 100644
--- a/base/metrics/sparse_histogram.h
+++ b/base/metrics/sparse_histogram.h
@@ -13,17 +13,45 @@
#include <string>
#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
#include "base/macros.h"
#include "base/metrics/histogram_base.h"
-#include "base/metrics/histogram_samples.h"
+#include "base/metrics/sample_map.h"
#include "base/synchronization/lock.h"
namespace base {
+// Sparse histograms are well suited for recording counts of exact sample values
+// that are sparsely distributed over a large range.
+//
+// The implementation uses a lock and a map, whereas other histogram types use a
+// vector and no lock. It is thus more costly to add values to, and each value
+// stored has more overhead, compared to the other histogram types. However it
+// may be more efficient in memory if the total number of sample values is small
+// compared to the range of their values.
+//
+// UMA_HISTOGRAM_ENUMERATION would be better suited for a smaller range of
+// enumerations that are (nearly) contiguous. Also for code that is expected to
+// run often or in a tight loop.
+//
+// UMA_HISTOGRAM_SPARSE_SLOWLY is good for sparsely distributed and or
+// infrequently recorded values.
+//
+// For instance, Sqlite.Version.* are SPARSE because for any given database,
+// there's going to be exactly one version logged, meaning no gain to having a
+// pre-allocated vector of slots once the fleet gets to version 4 or 5 or 10.
+// Likewise Sqlite.Error.* are SPARSE, because most databases generate few or no
+// errors and there are large gaps in the set of possible errors.
+#define UMA_HISTOGRAM_SPARSE_SLOWLY(name, sample) \
+ do { \
+ base::HistogramBase* histogram = base::SparseHistogram::FactoryGet( \
+ name, base::HistogramBase::kUmaTargetedHistogramFlag); \
+ histogram->Add(sample); \
+ } while (0)
+
class HistogramSamples;
class PersistentHistogramAllocator;
-class Pickle;
-class PickleIterator;
class BASE_EXPORT SparseHistogram : public HistogramBase {
public:
diff --git a/base/metrics/sparse_histogram_unittest.cc b/base/metrics/sparse_histogram_unittest.cc
index f4a7c9495e..eab7790276 100644
--- a/base/metrics/sparse_histogram_unittest.cc
+++ b/base/metrics/sparse_histogram_unittest.cc
@@ -8,7 +8,6 @@
#include <string>
#include "base/metrics/histogram_base.h"
-#include "base/metrics/histogram_macros.h"
#include "base/metrics/histogram_samples.h"
#include "base/metrics/persistent_histogram_allocator.h"
#include "base/metrics/persistent_memory_allocator.h"
diff --git a/base/metrics/statistics_recorder.cc b/base/metrics/statistics_recorder.cc
index 74c964a3fb..42ed5a9545 100644
--- a/base/metrics/statistics_recorder.cc
+++ b/base/metrics/statistics_recorder.cc
@@ -16,6 +16,7 @@
#include "base/metrics/persistent_histogram_allocator.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
+#include "base/synchronization/lock.h"
#include "base/values.h"
namespace {
@@ -58,10 +59,10 @@ StatisticsRecorder::HistogramIterator::~HistogramIterator() {}
StatisticsRecorder::HistogramIterator&
StatisticsRecorder::HistogramIterator::operator++() {
const HistogramMap::iterator histograms_end = histograms_->end();
- if (iter_ == histograms_end)
+ if (iter_ == histograms_end || lock_ == NULL)
return *this;
- base::AutoLock auto_lock(lock_.Get());
+ base::AutoLock auto_lock(*lock_);
for (;;) {
++iter_;
@@ -78,63 +79,51 @@ StatisticsRecorder::HistogramIterator::operator++() {
}
StatisticsRecorder::~StatisticsRecorder() {
+ DCHECK(lock_);
DCHECK(histograms_);
DCHECK(ranges_);
// Clean out what this object created and then restore what existed before.
Reset();
- base::AutoLock auto_lock(lock_.Get());
+ base::AutoLock auto_lock(*lock_);
histograms_ = existing_histograms_.release();
callbacks_ = existing_callbacks_.release();
ranges_ = existing_ranges_.release();
- providers_ = existing_providers_.release();
}
// static
void StatisticsRecorder::Initialize() {
- // Tests sometimes create local StatisticsRecorders in order to provide a
- // contained environment of histograms that can be later discarded. If a
- // true global instance gets created in this environment then it will
- // eventually get disconnected when the local instance destructs and
- // restores the previous state, resulting in no StatisticsRecorder at all.
- // The global lazy instance, however, will remain valid thus ensuring that
- // another never gets installed via this method. If a |histograms_| map
- // exists then assume the StatisticsRecorder is already "initialized".
- if (histograms_)
- return;
-
// Ensure that an instance of the StatisticsRecorder object is created.
g_statistics_recorder_.Get();
}
// static
bool StatisticsRecorder::IsActive() {
- base::AutoLock auto_lock(lock_.Get());
- return histograms_ != nullptr;
-}
-
-// static
-void StatisticsRecorder::RegisterHistogramProvider(
- const WeakPtr<HistogramProvider>& provider) {
- providers_->push_back(provider);
+ if (lock_ == NULL)
+ return false;
+ base::AutoLock auto_lock(*lock_);
+ return NULL != histograms_;
}
// static
HistogramBase* StatisticsRecorder::RegisterOrDeleteDuplicate(
HistogramBase* histogram) {
- HistogramBase* histogram_to_delete = nullptr;
- HistogramBase* histogram_to_return = nullptr;
+ // As per crbug.com/79322 the histograms are intentionally leaked, so we need
+ // to annotate them. Because ANNOTATE_LEAKING_OBJECT_PTR may be used only once
+ // for an object, the duplicates should not be annotated.
+ // Callers are responsible for not calling RegisterOrDeleteDuplicate(ptr)
+ // twice if (lock_ == NULL) || (!histograms_).
+ if (lock_ == NULL) {
+ ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322
+ return histogram;
+ }
+
+ HistogramBase* histogram_to_delete = NULL;
+ HistogramBase* histogram_to_return = NULL;
{
- base::AutoLock auto_lock(lock_.Get());
- if (!histograms_) {
+ base::AutoLock auto_lock(*lock_);
+ if (histograms_ == NULL) {
histogram_to_return = histogram;
-
- // As per crbug.com/79322 the histograms are intentionally leaked, so we
- // need to annotate them. Because ANNOTATE_LEAKING_OBJECT_PTR may be used
- // only once for an object, the duplicates should not be annotated.
- // Callers are responsible for not calling RegisterOrDeleteDuplicate(ptr)
- // twice |if (!histograms_)|.
- ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322
} else {
const std::string& name = histogram->histogram_name();
HistogramMap::iterator it = histograms_->find(name);
@@ -175,8 +164,13 @@ const BucketRanges* StatisticsRecorder::RegisterOrDeleteDuplicateRanges(
DCHECK(ranges->HasValidChecksum());
std::unique_ptr<const BucketRanges> ranges_deleter;
- base::AutoLock auto_lock(lock_.Get());
- if (!ranges_) {
+ if (lock_ == NULL) {
+ ANNOTATE_LEAKING_OBJECT_PTR(ranges);
+ return ranges;
+ }
+
+ base::AutoLock auto_lock(*lock_);
+ if (ranges_ == NULL) {
ANNOTATE_LEAKING_OBJECT_PTR(ranges);
return ranges;
}
@@ -273,8 +267,10 @@ std::string StatisticsRecorder::ToJSON(const std::string& query) {
// static
void StatisticsRecorder::GetHistograms(Histograms* output) {
- base::AutoLock auto_lock(lock_.Get());
- if (!histograms_)
+ if (lock_ == NULL)
+ return;
+ base::AutoLock auto_lock(*lock_);
+ if (histograms_ == NULL)
return;
for (const auto& entry : *histograms_) {
@@ -285,8 +281,10 @@ void StatisticsRecorder::GetHistograms(Histograms* output) {
// static
void StatisticsRecorder::GetBucketRanges(
std::vector<const BucketRanges*>* output) {
- base::AutoLock auto_lock(lock_.Get());
- if (!ranges_)
+ if (lock_ == NULL)
+ return;
+ base::AutoLock auto_lock(*lock_);
+ if (ranges_ == NULL)
return;
for (const auto& entry : *ranges_) {
@@ -303,31 +301,19 @@ HistogramBase* StatisticsRecorder::FindHistogram(base::StringPiece name) {
// will acquire the lock at that time.
ImportGlobalPersistentHistograms();
- base::AutoLock auto_lock(lock_.Get());
- if (!histograms_)
- return nullptr;
+ if (lock_ == NULL)
+ return NULL;
+ base::AutoLock auto_lock(*lock_);
+ if (histograms_ == NULL)
+ return NULL;
HistogramMap::iterator it = histograms_->find(name);
if (histograms_->end() == it)
- return nullptr;
+ return NULL;
return it->second;
}
// static
-void StatisticsRecorder::ImportProvidedHistograms() {
- if (!providers_)
- return;
-
- // Merge histogram data from each provider in turn.
- for (const WeakPtr<HistogramProvider>& provider : *providers_) {
- // Weak-pointer may be invalid if the provider was destructed, though they
- // generally never are.
- if (provider)
- provider->MergeHistogramDeltas();
- }
-}
-
-// static
StatisticsRecorder::HistogramIterator StatisticsRecorder::begin(
bool include_persistent) {
DCHECK(histograms_);
@@ -335,7 +321,7 @@ StatisticsRecorder::HistogramIterator StatisticsRecorder::begin(
HistogramMap::iterator iter_begin;
{
- base::AutoLock auto_lock(lock_.Get());
+ base::AutoLock auto_lock(*lock_);
iter_begin = histograms_->begin();
}
return HistogramIterator(iter_begin, include_persistent);
@@ -345,7 +331,7 @@ StatisticsRecorder::HistogramIterator StatisticsRecorder::begin(
StatisticsRecorder::HistogramIterator StatisticsRecorder::end() {
HistogramMap::iterator iter_end;
{
- base::AutoLock auto_lock(lock_.Get());
+ base::AutoLock auto_lock(*lock_);
iter_end = histograms_->end();
}
return HistogramIterator(iter_end, true);
@@ -353,21 +339,20 @@ StatisticsRecorder::HistogramIterator StatisticsRecorder::end() {
// static
void StatisticsRecorder::InitLogOnShutdown() {
- if (!histograms_)
+ if (lock_ == nullptr)
return;
-
- base::AutoLock auto_lock(lock_.Get());
+ base::AutoLock auto_lock(*lock_);
g_statistics_recorder_.Get().InitLogOnShutdownWithoutLock();
}
// static
void StatisticsRecorder::GetSnapshot(const std::string& query,
Histograms* snapshot) {
- base::AutoLock auto_lock(lock_.Get());
- if (!histograms_)
+ if (lock_ == NULL)
+ return;
+ base::AutoLock auto_lock(*lock_);
+ if (histograms_ == NULL)
return;
-
- ImportGlobalPersistentHistograms();
for (const auto& entry : *histograms_) {
if (entry.second->histogram_name().find(query) != std::string::npos)
@@ -380,8 +365,10 @@ bool StatisticsRecorder::SetCallback(
const std::string& name,
const StatisticsRecorder::OnSampleCallback& cb) {
DCHECK(!cb.is_null());
- base::AutoLock auto_lock(lock_.Get());
- if (!histograms_)
+ if (lock_ == NULL)
+ return false;
+ base::AutoLock auto_lock(*lock_);
+ if (histograms_ == NULL)
return false;
if (ContainsKey(*callbacks_, name))
@@ -397,8 +384,10 @@ bool StatisticsRecorder::SetCallback(
// static
void StatisticsRecorder::ClearCallback(const std::string& name) {
- base::AutoLock auto_lock(lock_.Get());
- if (!histograms_)
+ if (lock_ == NULL)
+ return;
+ base::AutoLock auto_lock(*lock_);
+ if (histograms_ == NULL)
return;
callbacks_->erase(name);
@@ -412,8 +401,10 @@ void StatisticsRecorder::ClearCallback(const std::string& name) {
// static
StatisticsRecorder::OnSampleCallback StatisticsRecorder::FindCallback(
const std::string& name) {
- base::AutoLock auto_lock(lock_.Get());
- if (!histograms_)
+ if (lock_ == NULL)
+ return OnSampleCallback();
+ base::AutoLock auto_lock(*lock_);
+ if (histograms_ == NULL)
return OnSampleCallback();
auto callback_iterator = callbacks_->find(name);
@@ -423,7 +414,10 @@ StatisticsRecorder::OnSampleCallback StatisticsRecorder::FindCallback(
// static
size_t StatisticsRecorder::GetHistogramCount() {
- base::AutoLock auto_lock(lock_.Get());
+ if (!lock_)
+ return 0;
+
+ base::AutoLock auto_lock(*lock_);
if (!histograms_)
return 0;
return histograms_->size();
@@ -444,7 +438,7 @@ StatisticsRecorder::CreateTemporaryForTesting() {
// static
void StatisticsRecorder::UninitializeForTesting() {
// Stop now if it's never been initialized.
- if (!histograms_)
+ if (lock_ == NULL || histograms_ == NULL)
return;
// Get the global instance and destruct it. It's held in static memory so
@@ -460,7 +454,7 @@ void StatisticsRecorder::UninitializeForTesting() {
// static
void StatisticsRecorder::ImportGlobalPersistentHistograms() {
- if (!histograms_)
+ if (lock_ == NULL)
return;
// Import histograms from known persistent storage. Histograms could have
@@ -476,17 +470,25 @@ void StatisticsRecorder::ImportGlobalPersistentHistograms() {
// of main(), and hence it is not thread safe. It initializes globals to
// provide support for all future calls.
StatisticsRecorder::StatisticsRecorder() {
- base::AutoLock auto_lock(lock_.Get());
+ if (lock_ == NULL) {
+ // This will leak on purpose. It's the only way to make sure we won't race
+ // against the static uninitialization of the module while one of our
+ // static methods relying on the lock get called at an inappropriate time
+ // during the termination phase. Since it's a static data member, we will
+ // leak one per process, which would be similar to the instance allocated
+ // during static initialization and released only on process termination.
+ lock_ = new base::Lock;
+ }
+
+ base::AutoLock auto_lock(*lock_);
existing_histograms_.reset(histograms_);
existing_callbacks_.reset(callbacks_);
existing_ranges_.reset(ranges_);
- existing_providers_.reset(providers_);
histograms_ = new HistogramMap;
callbacks_ = new CallbackMap;
ranges_ = new RangesMap;
- providers_ = new HistogramProviders;
InitLogOnShutdownWithoutLock();
}
@@ -500,21 +502,23 @@ void StatisticsRecorder::InitLogOnShutdownWithoutLock() {
// static
void StatisticsRecorder::Reset() {
+ // If there's no lock then there is nothing to reset.
+ if (!lock_)
+ return;
std::unique_ptr<HistogramMap> histograms_deleter;
std::unique_ptr<CallbackMap> callbacks_deleter;
std::unique_ptr<RangesMap> ranges_deleter;
- std::unique_ptr<HistogramProviders> providers_deleter;
+ // We don't delete lock_ on purpose to avoid having to properly protect
+ // against it going away after we checked for NULL in the static methods.
{
- base::AutoLock auto_lock(lock_.Get());
+ base::AutoLock auto_lock(*lock_);
histograms_deleter.reset(histograms_);
callbacks_deleter.reset(callbacks_);
ranges_deleter.reset(ranges_);
- providers_deleter.reset(providers_);
- histograms_ = nullptr;
- callbacks_ = nullptr;
- ranges_ = nullptr;
- providers_ = nullptr;
+ histograms_ = NULL;
+ callbacks_ = NULL;
+ ranges_ = NULL;
}
// We are going to leak the histograms and the ranges.
}
@@ -528,15 +532,12 @@ void StatisticsRecorder::DumpHistogramsToVlog(void* /*instance*/) {
// static
-StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = nullptr;
+StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL;
// static
-StatisticsRecorder::CallbackMap* StatisticsRecorder::callbacks_ = nullptr;
+StatisticsRecorder::CallbackMap* StatisticsRecorder::callbacks_ = NULL;
// static
-StatisticsRecorder::RangesMap* StatisticsRecorder::ranges_ = nullptr;
+StatisticsRecorder::RangesMap* StatisticsRecorder::ranges_ = NULL;
// static
-StatisticsRecorder::HistogramProviders* StatisticsRecorder::providers_;
-// static
-base::LazyInstance<base::Lock>::Leaky StatisticsRecorder::lock_ =
- LAZY_INSTANCE_INITIALIZER;
+base::Lock* StatisticsRecorder::lock_ = NULL;
} // namespace base
diff --git a/base/metrics/statistics_recorder.h b/base/metrics/statistics_recorder.h
index 55be86a85b..c3c6aceffd 100644
--- a/base/metrics/statistics_recorder.h
+++ b/base/metrics/statistics_recorder.h
@@ -23,14 +23,15 @@
#include "base/gtest_prod_util.h"
#include "base/lazy_instance.h"
#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_base.h"
#include "base/strings/string_piece.h"
-#include "base/synchronization/lock.h"
+
+class SubprocessMetricsProviderTest;
namespace base {
class BucketRanges;
+class Lock;
class BASE_EXPORT StatisticsRecorder {
public:
@@ -63,18 +64,8 @@ class BASE_EXPORT StatisticsRecorder {
}
};
- // An interface class that allows the StatisticsRecorder to forcibly merge
- // histograms from providers when necessary.
- class HistogramProvider {
- public:
- virtual ~HistogramProvider() {}
- // Merges all histogram information into the global versions.
- virtual void MergeHistogramDeltas() = 0;
- };
-
typedef std::map<StringKey, HistogramBase*> HistogramMap;
typedef std::vector<HistogramBase*> Histograms;
- typedef std::vector<WeakPtr<HistogramProvider>> HistogramProviders;
// A class for iterating over the histograms held within this global resource.
class BASE_EXPORT HistogramIterator {
@@ -112,12 +103,6 @@ class BASE_EXPORT StatisticsRecorder {
// Find out if histograms can now be registered into our list.
static bool IsActive();
- // Register a provider of histograms that can be called to merge those into
- // the global StatisticsRecorder. Calls to ImportProvidedHistograms() will
- // fetch from registered providers.
- static void RegisterHistogramProvider(
- const WeakPtr<HistogramProvider>& provider);
-
// Register, or add a new histogram to the collection of statistics. If an
// identically named histogram is already registered, then the argument
// |histogram| will deleted. The returned value is always the registered
@@ -151,9 +136,6 @@ class BASE_EXPORT StatisticsRecorder {
// safe. It returns NULL if a matching histogram is not found.
static HistogramBase* FindHistogram(base::StringPiece name);
- // Imports histograms from providers. This must be called on the UI thread.
- static void ImportProvidedHistograms();
-
// Support for iterating over known histograms.
static HistogramIterator begin(bool include_persistent);
static HistogramIterator end();
@@ -218,7 +200,7 @@ class BASE_EXPORT StatisticsRecorder {
// |bucket_ranges_|.
typedef std::map<uint32_t, std::list<const BucketRanges*>*> RangesMap;
- friend struct LazyInstanceTraitsBase<StatisticsRecorder>;
+ friend struct DefaultLazyInstanceTraits<StatisticsRecorder>;
friend class StatisticsRecorderTest;
// Imports histograms from global persistent memory. The global lock must
@@ -240,7 +222,6 @@ class BASE_EXPORT StatisticsRecorder {
std::unique_ptr<HistogramMap> existing_histograms_;
std::unique_ptr<CallbackMap> existing_callbacks_;
std::unique_ptr<RangesMap> existing_ranges_;
- std::unique_ptr<HistogramProviders> existing_providers_;
bool vlog_initialized_ = false;
@@ -250,13 +231,9 @@ class BASE_EXPORT StatisticsRecorder {
static HistogramMap* histograms_;
static CallbackMap* callbacks_;
static RangesMap* ranges_;
- static HistogramProviders* providers_;
- // Lock protects access to above maps. This is a LazyInstance to avoid races
- // when the above methods are used before Initialize(). Previously each method
- // would do |if (!lock_) return;| which would race with
- // |lock_ = new Lock;| in StatisticsRecorder(). http://crbug.com/672852.
- static base::LazyInstance<base::Lock>::Leaky lock_;
+ // Lock protects access to above maps.
+ static base::Lock* lock_;
DISALLOW_COPY_AND_ASSIGN(StatisticsRecorder);
};
diff --git a/base/metrics/statistics_recorder_unittest.cc b/base/metrics/statistics_recorder_unittest.cc
index 48b6df3068..65e2c98f52 100644
--- a/base/metrics/statistics_recorder_unittest.cc
+++ b/base/metrics/statistics_recorder_unittest.cc
@@ -12,7 +12,6 @@
#include "base/bind.h"
#include "base/json/json_reader.h"
#include "base/logging.h"
-#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/persistent_histogram_allocator.h"
#include "base/metrics/sparse_histogram.h"
@@ -657,74 +656,4 @@ TEST_P(StatisticsRecorderTest, LogOnShutdownInitialized) {
EXPECT_TRUE(VLogInitialized());
}
-class TestHistogramProvider : public StatisticsRecorder::HistogramProvider {
- public:
- TestHistogramProvider(std::unique_ptr<PersistentHistogramAllocator> allocator)
- : allocator_(std::move(allocator)), weak_factory_(this) {
- StatisticsRecorder::RegisterHistogramProvider(weak_factory_.GetWeakPtr());
- }
-
- void MergeHistogramDeltas() override {
- PersistentHistogramAllocator::Iterator hist_iter(allocator_.get());
- while (true) {
- std::unique_ptr<base::HistogramBase> histogram = hist_iter.GetNext();
- if (!histogram)
- break;
- allocator_->MergeHistogramDeltaToStatisticsRecorder(histogram.get());
- }
- }
-
- private:
- std::unique_ptr<PersistentHistogramAllocator> allocator_;
- WeakPtrFactory<TestHistogramProvider> weak_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(TestHistogramProvider);
-};
-
-TEST_P(StatisticsRecorderTest, ImportHistogramsTest) {
- // Create a second SR to create some histograms for later import.
- std::unique_ptr<StatisticsRecorder> temp_sr =
- StatisticsRecorder::CreateTemporaryForTesting();
-
- // Extract any existing global allocator so a new one can be created.
- std::unique_ptr<GlobalHistogramAllocator> old_allocator =
- GlobalHistogramAllocator::ReleaseForTesting();
-
- // Create a histogram inside a new allocator for testing.
- GlobalHistogramAllocator::CreateWithLocalMemory(kAllocatorMemorySize, 0, "");
- HistogramBase* histogram = LinearHistogram::FactoryGet("Foo", 1, 10, 11, 0);
- histogram->Add(3);
-
- // Undo back to the starting point.
- std::unique_ptr<GlobalHistogramAllocator> new_allocator =
- GlobalHistogramAllocator::ReleaseForTesting();
- GlobalHistogramAllocator::Set(std::move(old_allocator));
- temp_sr.reset();
-
- // Create a provider that can supply histograms to the current SR.
- TestHistogramProvider provider(std::move(new_allocator));
-
- // Verify that the created histogram is no longer known.
- ASSERT_FALSE(StatisticsRecorder::FindHistogram(histogram->histogram_name()));
-
- // Now test that it merges.
- StatisticsRecorder::ImportProvidedHistograms();
- HistogramBase* found =
- StatisticsRecorder::FindHistogram(histogram->histogram_name());
- ASSERT_TRUE(found);
- EXPECT_NE(histogram, found);
- std::unique_ptr<HistogramSamples> snapshot = found->SnapshotSamples();
- EXPECT_EQ(1, snapshot->TotalCount());
- EXPECT_EQ(1, snapshot->GetCount(3));
-
- // Finally, verify that updates can also be merged.
- histogram->Add(3);
- histogram->Add(5);
- StatisticsRecorder::ImportProvidedHistograms();
- snapshot = found->SnapshotSamples();
- EXPECT_EQ(3, snapshot->TotalCount());
- EXPECT_EQ(2, snapshot->GetCount(3));
- EXPECT_EQ(1, snapshot->GetCount(5));
-}
-
} // namespace base
diff --git a/base/metrics/user_metrics.cc b/base/metrics/user_metrics.cc
index 65ac918817..169a0634e4 100644
--- a/base/metrics/user_metrics.cc
+++ b/base/metrics/user_metrics.cc
@@ -17,10 +17,10 @@
namespace base {
namespace {
-LazyInstance<std::vector<ActionCallback>>::DestructorAtExit g_callbacks =
+LazyInstance<std::vector<ActionCallback>> g_callbacks =
+ LAZY_INSTANCE_INITIALIZER;
+LazyInstance<scoped_refptr<SingleThreadTaskRunner>> g_task_runner =
LAZY_INSTANCE_INITIALIZER;
-LazyInstance<scoped_refptr<SingleThreadTaskRunner>>::DestructorAtExit
- g_task_runner = LAZY_INSTANCE_INITIALIZER;
} // namespace
diff --git a/base/metrics/user_metrics.h b/base/metrics/user_metrics.h
index 87fbd9cac0..93701e8fd2 100644
--- a/base/metrics/user_metrics.h
+++ b/base/metrics/user_metrics.h
@@ -17,9 +17,6 @@ namespace base {
// This module provides some helper functions for logging actions tracked by
// the user metrics system.
-// For best practices on deciding when to emit a user action, see
-// https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/actions/README.md
-
// Record that the user performed an action.
// This function must be called after the task runner has been set with
// SetRecordActionTaskRunner().
diff --git a/base/native_library.h b/base/native_library.h
index 02eae1d508..b4f3a3cd1b 100644
--- a/base/native_library.h
+++ b/base/native_library.h
@@ -65,32 +65,12 @@ struct BASE_EXPORT NativeLibraryLoadError {
#endif // OS_WIN
};
-struct BASE_EXPORT NativeLibraryOptions {
- NativeLibraryOptions() = default;
- NativeLibraryOptions(const NativeLibraryOptions& options) = default;
-
- // If |true|, a loaded library is required to prefer local symbol resolution
- // before considering global symbols. Note that this is already the default
- // behavior on most systems. Setting this to |false| does not guarantee the
- // inverse, i.e., it does not force a preference for global symbols over local
- // ones.
- bool prefer_own_symbols = false;
-};
-
// Loads a native library from disk. Release it with UnloadNativeLibrary when
// you're done. Returns NULL on failure.
// If |error| is not NULL, it may be filled in on load error.
BASE_EXPORT NativeLibrary LoadNativeLibrary(const FilePath& library_path,
NativeLibraryLoadError* error);
-// Loads a native library from disk. Release it with UnloadNativeLibrary when
-// you're done. Returns NULL on failure.
-// If |error| is not NULL, it may be filled in on load error.
-BASE_EXPORT NativeLibrary LoadNativeLibraryWithOptions(
- const FilePath& library_path,
- const NativeLibraryOptions& options,
- NativeLibraryLoadError* error);
-
#if defined(OS_WIN)
// Loads a native library from disk. Release it with UnloadNativeLibrary when
// you're done.
diff --git a/base/native_library_posix.cc b/base/native_library_posix.cc
index 3459716af1..2dc434b7be 100644
--- a/base/native_library_posix.cc
+++ b/base/native_library_posix.cc
@@ -19,27 +19,16 @@ std::string NativeLibraryLoadError::ToString() const {
}
// static
-NativeLibrary LoadNativeLibraryWithOptions(const FilePath& library_path,
- const NativeLibraryOptions& options,
- NativeLibraryLoadError* error) {
+NativeLibrary LoadNativeLibrary(const FilePath& library_path,
+ NativeLibraryLoadError* error) {
// dlopen() opens the file off disk.
ThreadRestrictions::AssertIOAllowed();
- // We deliberately do not use RTLD_DEEPBIND by default. For the history why,
- // please refer to the bug tracker. Some useful bug reports to read include:
+ // We deliberately do not use RTLD_DEEPBIND. For the history why, please
+ // refer to the bug tracker. Some useful bug reports to read include:
// http://crbug.com/17943, http://crbug.com/17557, http://crbug.com/36892,
// and http://crbug.com/40794.
- int flags = RTLD_LAZY;
-#if defined(OS_ANDROID) || !defined(RTLD_DEEPBIND)
- // Certain platforms don't define RTLD_DEEPBIND. Android dlopen() requires
- // further investigation, as it might vary across versions. Crash here to
- // warn developers that they're trying to rely on uncertain behavior.
- CHECK(!options.prefer_own_symbols);
-#else
- if (options.prefer_own_symbols)
- flags |= RTLD_DEEPBIND;
-#endif
- void* dl = dlopen(library_path.value().c_str(), flags);
+ void* dl = dlopen(library_path.value().c_str(), RTLD_LAZY);
if (!dl && error)
error->message = dlerror();
diff --git a/base/numerics/safe_conversions.h b/base/numerics/safe_conversions.h
index b0ec279eb5..6b558afde4 100644
--- a/base/numerics/safe_conversions.h
+++ b/base/numerics/safe_conversions.h
@@ -8,130 +8,95 @@
#include <stddef.h>
#include <limits>
-#include <ostream>
#include <type_traits>
+#include "base/logging.h"
#include "base/numerics/safe_conversions_impl.h"
namespace base {
-// The following are helper constexpr template functions and classes for safely
-// performing a range of conversions, assignments, and tests:
-//
-// checked_cast<> - Analogous to static_cast<> for numeric types, except
-// that it CHECKs that the specified numeric conversion will not overflow
-// or underflow. NaN source will always trigger a CHECK.
-// The default CHECK triggers a crash, but the handler can be overriden.
-// saturated_cast<> - Analogous to static_cast<> for numeric types, except
-// that it returns a saturated result when the specified numeric conversion
-// would otherwise overflow or underflow. An NaN source returns 0 by
-// default, but can be overridden to return a different result.
-// strict_cast<> - Analogous to static_cast<> for numeric types, except that
-// it will cause a compile failure if the destination type is not large
-// enough to contain any value in the source type. It performs no runtime
-// checking and thus introduces no runtime overhead.
-// IsValueInRangeForNumericType<>() - A convenience function that returns true
-// if the type supplied to the template parameter can represent the value
-// passed as an argument to the function.
-// IsValueNegative<>() - A convenience function that will accept any arithmetic
-// type as an argument and will return whether the value is less than zero.
-// Unsigned types always return false.
-// SafeUnsignedAbs() - Returns the absolute value of the supplied integer
-// parameter as an unsigned result (thus avoiding an overflow if the value
-// is the signed, two's complement minimum).
-// StrictNumeric<> - A wrapper type that performs assignments and copies via
-// the strict_cast<> template, and can perform valid arithmetic comparisons
-// across any range of arithmetic types. StrictNumeric is the return type
-// for values extracted from a CheckedNumeric class instance. The raw
-// arithmetic value is extracted via static_cast to the underlying type.
-// MakeStrictNum() - Creates a new StrictNumeric from the underlying type of
-// the supplied arithmetic or StrictNumeric type.
-
// Convenience function that returns true if the supplied value is in range
// for the destination type.
template <typename Dst, typename Src>
constexpr bool IsValueInRangeForNumericType(Src value) {
- return internal::DstRangeRelationToSrcRange<Dst>(value).IsValid();
+ return internal::DstRangeRelationToSrcRange<Dst>(value) ==
+ internal::RANGE_VALID;
}
-// Forces a crash, like a CHECK(false). Used for numeric boundary errors.
-struct CheckOnFailure {
- template <typename T>
- static T HandleFailure() {
-#if defined(__GNUC__) || defined(__clang__)
- __builtin_trap();
-#else
- ((void)(*(volatile char*)0 = 0));
-#endif
- return T();
- }
-};
+// Convenience function for determining if a numeric value is negative without
+// throwing compiler warnings on: unsigned(value) < 0.
+template <typename T>
+constexpr typename std::enable_if<std::numeric_limits<T>::is_signed, bool>::type
+IsValueNegative(T value) {
+ static_assert(std::numeric_limits<T>::is_specialized,
+ "Argument must be numeric.");
+ return value < 0;
+}
+
+template <typename T>
+constexpr typename std::enable_if<!std::numeric_limits<T>::is_signed,
+ bool>::type IsValueNegative(T) {
+ static_assert(std::numeric_limits<T>::is_specialized,
+ "Argument must be numeric.");
+ return false;
+}
// checked_cast<> is analogous to static_cast<> for numeric types,
// except that it CHECKs that the specified numeric conversion will not
// overflow or underflow. NaN source will always trigger a CHECK.
-template <typename Dst,
- class CheckHandler = CheckOnFailure,
- typename Src>
-constexpr Dst checked_cast(Src value) {
- // This throws a compile-time error on evaluating the constexpr if it can be
- // determined at compile-time as failing, otherwise it will CHECK at runtime.
- using SrcType = typename internal::UnderlyingType<Src>::type;
- return IsValueInRangeForNumericType<Dst, SrcType>(value)
- ? static_cast<Dst>(static_cast<SrcType>(value))
- : CheckHandler::template HandleFailure<Dst>();
+template <typename Dst, typename Src>
+inline Dst checked_cast(Src value) {
+ CHECK(IsValueInRangeForNumericType<Dst>(value));
+ return static_cast<Dst>(value);
}
-// Default boundaries for integral/float: max/infinity, lowest/-infinity, 0/NaN.
-template <typename T>
-struct SaturationDefaultHandler {
- static constexpr T NaN() {
- return std::numeric_limits<T>::has_quiet_NaN
- ? std::numeric_limits<T>::quiet_NaN()
- : T();
- }
- static constexpr T max() { return std::numeric_limits<T>::max(); }
- static constexpr T Overflow() {
- return std::numeric_limits<T>::has_infinity
- ? std::numeric_limits<T>::infinity()
- : std::numeric_limits<T>::max();
+// HandleNaN will cause this class to CHECK(false).
+struct SaturatedCastNaNBehaviorCheck {
+ template <typename T>
+ static T HandleNaN() {
+ CHECK(false);
+ return T();
}
- static constexpr T lowest() { return std::numeric_limits<T>::lowest(); }
- static constexpr T Underflow() {
- return std::numeric_limits<T>::has_infinity
- ? std::numeric_limits<T>::infinity() * -1
- : std::numeric_limits<T>::lowest();
+};
+
+// HandleNaN will return 0 in this case.
+struct SaturatedCastNaNBehaviorReturnZero {
+ template <typename T>
+ static constexpr T HandleNaN() {
+ return T();
}
};
namespace internal {
-
-template <typename Dst, template <typename> class S, typename Src>
-constexpr Dst saturated_cast_impl(Src value, RangeCheck constraint) {
- // For some reason clang generates much better code when the branch is
- // structured exactly this way, rather than a sequence of checks.
- return !constraint.IsOverflowFlagSet()
- ? (!constraint.IsUnderflowFlagSet() ? static_cast<Dst>(value)
- : S<Dst>::Underflow())
- // Skip this check for integral Src, which cannot be NaN.
- : (std::is_integral<Src>::value || !constraint.IsUnderflowFlagSet()
- ? S<Dst>::Overflow()
- : S<Dst>::NaN());
+// This wrapper is used for C++11 constexpr support by avoiding the declaration
+// of local variables in the saturated_cast template function.
+template <typename Dst, class NaNHandler, typename Src>
+constexpr Dst saturated_cast_impl(const Src value,
+ const RangeConstraint constraint) {
+ return constraint == RANGE_VALID
+ ? static_cast<Dst>(value)
+ : (constraint == RANGE_UNDERFLOW
+ ? std::numeric_limits<Dst>::min()
+ : (constraint == RANGE_OVERFLOW
+ ? std::numeric_limits<Dst>::max()
+ : (constraint == RANGE_INVALID
+ ? NaNHandler::template HandleNaN<Dst>()
+ : (NOTREACHED(), static_cast<Dst>(value)))));
}
+} // namespace internal
// saturated_cast<> is analogous to static_cast<> for numeric types, except
-// that the specified numeric conversion will saturate by default rather than
-// overflow or underflow, and NaN assignment to an integral will return 0.
-// All boundary condition behaviors can be overriden with a custom handler.
+// that the specified numeric conversion will saturate rather than overflow or
+// underflow. NaN assignment to an integral will defer the behavior to a
+// specified class. By default, it will return 0.
template <typename Dst,
- template <typename>
- class SaturationHandler = SaturationDefaultHandler,
+ class NaNHandler = SaturatedCastNaNBehaviorReturnZero,
typename Src>
constexpr Dst saturated_cast(Src value) {
- using SrcType = typename UnderlyingType<Src>::type;
- return saturated_cast_impl<Dst, SaturationHandler, SrcType>(
- value,
- DstRangeRelationToSrcRange<Dst, SaturationHandler, SrcType>(value));
+ return std::numeric_limits<Dst>::is_iec559
+ ? static_cast<Dst>(value) // Floating point optimization.
+ : internal::saturated_cast_impl<Dst, NaNHandler>(
+ value, internal::DstRangeRelationToSrcRange<Dst>(value));
}
// strict_cast<> is analogous to static_cast<> for numeric types, except that
@@ -139,40 +104,22 @@ constexpr Dst saturated_cast(Src value) {
// to contain any value in the source type. It performs no runtime checking.
template <typename Dst, typename Src>
constexpr Dst strict_cast(Src value) {
- using SrcType = typename UnderlyingType<Src>::type;
- static_assert(UnderlyingType<Src>::is_numeric, "Argument must be numeric.");
- static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric.");
-
- // If you got here from a compiler error, it's because you tried to assign
- // from a source type to a destination type that has insufficient range.
- // The solution may be to change the destination type you're assigning to,
- // and use one large enough to represent the source.
- // Alternatively, you may be better served with the checked_cast<> or
- // saturated_cast<> template functions for your particular use case.
- static_assert(StaticDstRangeRelationToSrcRange<Dst, SrcType>::value ==
- NUMERIC_RANGE_CONTAINED,
- "The source type is out of range for the destination type. "
- "Please see strict_cast<> comments for more information.");
-
- return static_cast<Dst>(static_cast<SrcType>(value));
+ static_assert(std::numeric_limits<Src>::is_specialized,
+ "Argument must be numeric.");
+ static_assert(std::numeric_limits<Dst>::is_specialized,
+ "Result must be numeric.");
+ static_assert((internal::StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
+ internal::NUMERIC_RANGE_CONTAINED),
+ "The numeric conversion is out of range for this type. You "
+ "should probably use one of the following conversion "
+ "mechanisms on the value you want to pass:\n"
+ "- base::checked_cast\n"
+ "- base::saturated_cast\n"
+ "- base::CheckedNumeric");
+
+ return static_cast<Dst>(value);
}
-// Some wrappers to statically check that a type is in range.
-template <typename Dst, typename Src, class Enable = void>
-struct IsNumericRangeContained {
- static const bool value = false;
-};
-
-template <typename Dst, typename Src>
-struct IsNumericRangeContained<
- Dst,
- Src,
- typename std::enable_if<ArithmeticOrUnderlyingEnum<Dst>::value &&
- ArithmeticOrUnderlyingEnum<Src>::value>::type> {
- static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
- NUMERIC_RANGE_CONTAINED;
-};
-
// StrictNumeric implements compile time range checking between numeric types by
// wrapping assignment operations in a strict_cast. This class is intended to be
// used for function arguments and return types, to ensure the destination type
@@ -186,7 +133,7 @@ struct IsNumericRangeContained<
template <typename T>
class StrictNumeric {
public:
- using type = T;
+ typedef T type;
constexpr StrictNumeric() : value_(0) {}
@@ -198,74 +145,21 @@ class StrictNumeric {
// This is not an explicit constructor because we implicitly upgrade regular
// numerics to StrictNumerics to make them easier to use.
template <typename Src>
- constexpr StrictNumeric(Src value) // NOLINT(runtime/explicit)
+ constexpr StrictNumeric(Src value)
: value_(strict_cast<T>(value)) {}
- // If you got here from a compiler error, it's because you tried to assign
- // from a source type to a destination type that has insufficient range.
- // The solution may be to change the destination type you're assigning to,
- // and use one large enough to represent the source.
- // If you're assigning from a CheckedNumeric<> class, you may be able to use
- // the AssignIfValid() member function, specify a narrower destination type to
- // the member value functions (e.g. val.template ValueOrDie<Dst>()), use one
- // of the value helper functions (e.g. ValueOrDieForType<Dst>(val)).
- // If you've encountered an _ambiguous overload_ you can use a static_cast<>
- // to explicitly cast the result to the destination type.
- // If none of that works, you may be better served with the checked_cast<> or
- // saturated_cast<> template functions for your particular use case.
- template <typename Dst,
- typename std::enable_if<
- IsNumericRangeContained<Dst, T>::value>::type* = nullptr>
+ // The numeric cast operator basically handles all the magic.
+ template <typename Dst>
constexpr operator Dst() const {
- return static_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>(value_);
+ return strict_cast<Dst>(value_);
}
private:
const T value_;
};
-// Convience wrapper returns a StrictNumeric from the provided arithmetic type.
-template <typename T>
-constexpr StrictNumeric<typename UnderlyingType<T>::type> MakeStrictNum(
- const T value) {
- return value;
-}
-
-// Overload the ostream output operator to make logging work nicely.
-template <typename T>
-std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) {
- os << static_cast<T>(value);
- return os;
-}
-
-#define STRICT_COMPARISON_OP(NAME, OP) \
- template <typename L, typename R, \
- typename std::enable_if< \
- internal::IsStrictOp<L, R>::value>::type* = nullptr> \
- constexpr bool operator OP(const L lhs, const R rhs) { \
- return SafeCompare<NAME, typename UnderlyingType<L>::type, \
- typename UnderlyingType<R>::type>(lhs, rhs); \
- }
-
-STRICT_COMPARISON_OP(IsLess, <);
-STRICT_COMPARISON_OP(IsLessOrEqual, <=);
-STRICT_COMPARISON_OP(IsGreater, >);
-STRICT_COMPARISON_OP(IsGreaterOrEqual, >=);
-STRICT_COMPARISON_OP(IsEqual, ==);
-STRICT_COMPARISON_OP(IsNotEqual, !=);
-
-#undef STRICT_COMPARISON_OP
-};
-
-using internal::strict_cast;
-using internal::saturated_cast;
-using internal::SafeUnsignedAbs;
-using internal::StrictNumeric;
-using internal::MakeStrictNum;
-using internal::IsValueNegative;
-
-// Explicitly make a shorter size_t alias for convenience.
-using SizeT = StrictNumeric<size_t>;
+// Explicitly make a shorter size_t typedef for convenience.
+typedef StrictNumeric<size_t> SizeT;
} // namespace base
diff --git a/base/numerics/safe_conversions_impl.h b/base/numerics/safe_conversions_impl.h
index 24357fd6a5..0f0aebcab7 100644
--- a/base/numerics/safe_conversions_impl.h
+++ b/base/numerics/safe_conversions_impl.h
@@ -5,77 +5,28 @@
#ifndef BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
#define BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
+#include <limits.h>
#include <stdint.h>
+#include <climits>
#include <limits>
-#include <type_traits>
namespace base {
namespace internal {
// The std library doesn't provide a binary max_exponent for integers, however
-// we can compute an analog using std::numeric_limits<>::digits.
+// we can compute one by adding one to the number of non-sign bits. This allows
+// for accurate range comparisons between floating point and integer types.
template <typename NumericType>
struct MaxExponent {
- static const int value = std::is_floating_point<NumericType>::value
+ static_assert(std::is_arithmetic<NumericType>::value,
+ "Argument must be numeric.");
+ static const int value = std::numeric_limits<NumericType>::is_iec559
? std::numeric_limits<NumericType>::max_exponent
- : std::numeric_limits<NumericType>::digits + 1;
+ : (sizeof(NumericType) * CHAR_BIT + 1 -
+ std::numeric_limits<NumericType>::is_signed);
};
-// The number of bits (including the sign) in an integer. Eliminates sizeof
-// hacks.
-template <typename NumericType>
-struct IntegerBitsPlusSign {
- static const int value = std::numeric_limits<NumericType>::digits +
- std::is_signed<NumericType>::value;
-};
-
-// Helper templates for integer manipulations.
-
-template <typename Integer>
-struct PositionOfSignBit {
- static const size_t value = IntegerBitsPlusSign<Integer>::value - 1;
-};
-
-// Determines if a numeric value is negative without throwing compiler
-// warnings on: unsigned(value) < 0.
-template <typename T,
- typename std::enable_if<std::is_signed<T>::value>::type* = nullptr>
-constexpr bool IsValueNegative(T value) {
- static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
- return value < 0;
-}
-
-template <typename T,
- typename std::enable_if<!std::is_signed<T>::value>::type* = nullptr>
-constexpr bool IsValueNegative(T) {
- static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
- return false;
-}
-
-// This performs a fast negation, returning a signed value. It works on unsigned
-// arguments, but probably doesn't do what you want for any unsigned value
-// larger than max / 2 + 1 (i.e. signed min cast to unsigned).
-template <typename T>
-constexpr typename std::make_signed<T>::type ConditionalNegate(
- T x,
- bool is_negative) {
- static_assert(std::is_integral<T>::value, "Type must be integral");
- using SignedT = typename std::make_signed<T>::type;
- using UnsignedT = typename std::make_unsigned<T>::type;
- return static_cast<SignedT>(
- (static_cast<UnsignedT>(x) ^ -SignedT(is_negative)) + is_negative);
-}
-
-// This performs a safe, absolute value via unsigned overflow.
-template <typename T>
-constexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) {
- static_assert(std::is_integral<T>::value, "Type must be integral");
- using UnsignedT = typename std::make_unsigned<T>::type;
- return IsValueNegative(value) ? 0 - static_cast<UnsignedT>(value)
- : static_cast<UnsignedT>(value);
-}
-
enum IntegerRepresentation {
INTEGER_REPRESENTATION_UNSIGNED,
INTEGER_REPRESENTATION_SIGNED
@@ -83,7 +34,7 @@ enum IntegerRepresentation {
// A range for a given nunmeric Src type is contained for a given numeric Dst
// type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and
-// numeric_limits<Src>::lowest() >= numeric_limits<Dst>::lowest() are true.
+// numeric_limits<Src>::min() >= numeric_limits<Dst>::min() are true.
// We implement this as template specializations rather than simple static
// comparisons to ensure type correctness in our comparisons.
enum NumericRangeRepresentation {
@@ -94,14 +45,16 @@ enum NumericRangeRepresentation {
// Helper templates to statically determine if our destination type can contain
// maximum and minimum values represented by the source type.
-template <typename Dst,
- typename Src,
- IntegerRepresentation DstSign = std::is_signed<Dst>::value
- ? INTEGER_REPRESENTATION_SIGNED
- : INTEGER_REPRESENTATION_UNSIGNED,
- IntegerRepresentation SrcSign = std::is_signed<Src>::value
- ? INTEGER_REPRESENTATION_SIGNED
- : INTEGER_REPRESENTATION_UNSIGNED>
+template <
+ typename Dst,
+ typename Src,
+ IntegerRepresentation DstSign = std::numeric_limits<Dst>::is_signed
+ ? INTEGER_REPRESENTATION_SIGNED
+ : INTEGER_REPRESENTATION_UNSIGNED,
+ IntegerRepresentation SrcSign =
+ std::numeric_limits<Src>::is_signed
+ ? INTEGER_REPRESENTATION_SIGNED
+ : INTEGER_REPRESENTATION_UNSIGNED >
struct StaticDstRangeRelationToSrcRange;
// Same sign: Dst is guaranteed to contain Src only if its range is equal or
@@ -136,34 +89,30 @@ struct StaticDstRangeRelationToSrcRange<Dst,
static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED;
};
-// This class wraps the range constraints as separate booleans so the compiler
-// can identify constants and eliminate unused code paths.
-class RangeCheck {
- public:
- constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound)
- : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {}
- constexpr RangeCheck() : is_underflow_(0), is_overflow_(0) {}
- constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; }
- constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; }
- constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; }
- constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; }
- constexpr bool IsOverflowFlagSet() const { return is_overflow_; }
- constexpr bool IsUnderflowFlagSet() const { return is_underflow_; }
- constexpr bool operator==(const RangeCheck rhs) const {
- return is_underflow_ == rhs.is_underflow_ &&
- is_overflow_ == rhs.is_overflow_;
- }
- constexpr bool operator!=(const RangeCheck rhs) const {
- return !(*this == rhs);
- }
-
- private:
- // Do not change the order of these member variables. The integral conversion
- // optimization depends on this exact order.
- const bool is_underflow_;
- const bool is_overflow_;
+enum RangeConstraint {
+ RANGE_VALID = 0x0, // Value can be represented by the destination type.
+ RANGE_UNDERFLOW = 0x1, // Value would overflow.
+ RANGE_OVERFLOW = 0x2, // Value would underflow.
+ RANGE_INVALID = RANGE_UNDERFLOW | RANGE_OVERFLOW // Invalid (i.e. NaN).
};
+// Helper function for coercing an int back to a RangeContraint.
+constexpr RangeConstraint GetRangeConstraint(int integer_range_constraint) {
+ // TODO(jschuh): Once we get full C++14 support we want this
+ // assert(integer_range_constraint >= RANGE_VALID &&
+ // integer_range_constraint <= RANGE_INVALID)
+ return static_cast<RangeConstraint>(integer_range_constraint);
+}
+
+// This function creates a RangeConstraint from an upper and lower bound
+// check by taking advantage of the fact that only NaN can be out of range in
+// both directions at once.
+constexpr inline RangeConstraint GetRangeConstraint(bool is_in_upper_bound,
+ bool is_in_lower_bound) {
+ return GetRangeConstraint((is_in_upper_bound ? 0 : RANGE_OVERFLOW) |
+ (is_in_lower_bound ? 0 : RANGE_UNDERFLOW));
+}
+
// The following helper template addresses a corner case in range checks for
// conversion from a floating-point type to an integral type of smaller range
// but larger precision (e.g. float -> unsigned). The problem is as follows:
@@ -185,547 +134,131 @@ class RangeCheck {
// To fix this bug we manually truncate the maximum value when the destination
// type is an integral of larger precision than the source floating-point type,
// such that the resulting maximum is represented exactly as a floating point.
-template <typename Dst, typename Src, template <typename> class Bounds>
+template <typename Dst, typename Src>
struct NarrowingRange {
- using SrcLimits = std::numeric_limits<Src>;
- using DstLimits = typename std::numeric_limits<Dst>;
-
- // Computes the mask required to make an accurate comparison between types.
- static const int kShift =
- (MaxExponent<Src>::value > MaxExponent<Dst>::value &&
- SrcLimits::digits < DstLimits::digits)
- ? (DstLimits::digits - SrcLimits::digits)
- : 0;
- template <
- typename T,
- typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
-
- // Masks out the integer bits that are beyond the precision of the
- // intermediate type used for comparison.
- static constexpr T Adjust(T value) {
- static_assert(std::is_same<T, Dst>::value, "");
- static_assert(kShift < DstLimits::digits, "");
- return static_cast<T>(
- ConditionalNegate(SafeUnsignedAbs(value) & ~((T(1) << kShift) - T(1)),
- IsValueNegative(value)));
+ typedef typename std::numeric_limits<Src> SrcLimits;
+ typedef typename std::numeric_limits<Dst> DstLimits;
+ // The following logic avoids warnings where the max function is
+ // instantiated with invalid values for a bit shift (even though
+ // such a function can never be called).
+ static const int shift = (MaxExponent<Src>::value > MaxExponent<Dst>::value &&
+ SrcLimits::digits < DstLimits::digits &&
+ SrcLimits::is_iec559 &&
+ DstLimits::is_integer)
+ ? (DstLimits::digits - SrcLimits::digits)
+ : 0;
+
+ static constexpr Dst max() {
+ // We use UINTMAX_C below to avoid compiler warnings about shifting floating
+ // points. Since it's a compile time calculation, it shouldn't have any
+ // performance impact.
+ return DstLimits::max() - static_cast<Dst>((UINTMAX_C(1) << shift) - 1);
}
- template <typename T,
- typename std::enable_if<std::is_floating_point<T>::value>::type* =
- nullptr>
- static constexpr T Adjust(T value) {
- static_assert(std::is_same<T, Dst>::value, "");
- static_assert(kShift == 0, "");
- return value;
+ static constexpr Dst min() {
+ return std::numeric_limits<Dst>::is_iec559 ? -DstLimits::max()
+ : DstLimits::min();
}
-
- static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); }
- static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); }
};
-template <typename Dst,
- typename Src,
- template <typename> class Bounds,
- IntegerRepresentation DstSign = std::is_signed<Dst>::value
- ? INTEGER_REPRESENTATION_SIGNED
- : INTEGER_REPRESENTATION_UNSIGNED,
- IntegerRepresentation SrcSign = std::is_signed<Src>::value
- ? INTEGER_REPRESENTATION_SIGNED
- : INTEGER_REPRESENTATION_UNSIGNED,
- NumericRangeRepresentation DstRange =
- StaticDstRangeRelationToSrcRange<Dst, Src>::value>
+template <
+ typename Dst,
+ typename Src,
+ IntegerRepresentation DstSign = std::numeric_limits<Dst>::is_signed
+ ? INTEGER_REPRESENTATION_SIGNED
+ : INTEGER_REPRESENTATION_UNSIGNED,
+ IntegerRepresentation SrcSign = std::numeric_limits<Src>::is_signed
+ ? INTEGER_REPRESENTATION_SIGNED
+ : INTEGER_REPRESENTATION_UNSIGNED,
+ NumericRangeRepresentation DstRange =
+ StaticDstRangeRelationToSrcRange<Dst, Src>::value >
struct DstRangeRelationToSrcRangeImpl;
// The following templates are for ranges that must be verified at runtime. We
// split it into checks based on signedness to avoid confusing casts and
// compiler warnings on signed an unsigned comparisons.
-// Same sign narrowing: The range is contained for normal limits.
+// Dst range is statically determined to contain Src: Nothing to check.
template <typename Dst,
typename Src,
- template <typename> class Bounds,
IntegerRepresentation DstSign,
IntegerRepresentation SrcSign>
struct DstRangeRelationToSrcRangeImpl<Dst,
Src,
- Bounds,
DstSign,
SrcSign,
NUMERIC_RANGE_CONTAINED> {
- static constexpr RangeCheck Check(Src value) {
- using SrcLimits = std::numeric_limits<Src>;
- using DstLimits = NarrowingRange<Dst, Src, Bounds>;
- return RangeCheck(
- static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() ||
- static_cast<Dst>(value) >= DstLimits::lowest(),
- static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() ||
- static_cast<Dst>(value) <= DstLimits::max());
- }
+ static constexpr RangeConstraint Check(Src /*value*/) { return RANGE_VALID; }
};
// Signed to signed narrowing: Both the upper and lower boundaries may be
-// exceeded for standard limits.
-template <typename Dst, typename Src, template <typename> class Bounds>
+// exceeded.
+template <typename Dst, typename Src>
struct DstRangeRelationToSrcRangeImpl<Dst,
Src,
- Bounds,
INTEGER_REPRESENTATION_SIGNED,
INTEGER_REPRESENTATION_SIGNED,
NUMERIC_RANGE_NOT_CONTAINED> {
- static constexpr RangeCheck Check(Src value) {
- using DstLimits = NarrowingRange<Dst, Src, Bounds>;
- return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max());
+ static constexpr RangeConstraint Check(Src value) {
+ return GetRangeConstraint((value <= NarrowingRange<Dst, Src>::max()),
+ (value >= NarrowingRange<Dst, Src>::min()));
}
};
-// Unsigned to unsigned narrowing: Only the upper bound can be exceeded for
-// standard limits.
-template <typename Dst, typename Src, template <typename> class Bounds>
+// Unsigned to unsigned narrowing: Only the upper boundary can be exceeded.
+template <typename Dst, typename Src>
struct DstRangeRelationToSrcRangeImpl<Dst,
Src,
- Bounds,
INTEGER_REPRESENTATION_UNSIGNED,
INTEGER_REPRESENTATION_UNSIGNED,
NUMERIC_RANGE_NOT_CONTAINED> {
- static constexpr RangeCheck Check(Src value) {
- using DstLimits = NarrowingRange<Dst, Src, Bounds>;
- return RangeCheck(
- DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest(),
- value <= DstLimits::max());
+ static constexpr RangeConstraint Check(Src value) {
+ return GetRangeConstraint(value <= NarrowingRange<Dst, Src>::max(), true);
}
};
-// Unsigned to signed: Only the upper bound can be exceeded for standard limits.
-template <typename Dst, typename Src, template <typename> class Bounds>
+// Unsigned to signed: The upper boundary may be exceeded.
+template <typename Dst, typename Src>
struct DstRangeRelationToSrcRangeImpl<Dst,
Src,
- Bounds,
INTEGER_REPRESENTATION_SIGNED,
INTEGER_REPRESENTATION_UNSIGNED,
NUMERIC_RANGE_NOT_CONTAINED> {
- static constexpr RangeCheck Check(Src value) {
- using DstLimits = NarrowingRange<Dst, Src, Bounds>;
- using Promotion = decltype(Src() + Dst());
- return RangeCheck(DstLimits::lowest() <= Dst(0) ||
- static_cast<Promotion>(value) >=
- static_cast<Promotion>(DstLimits::lowest()),
- static_cast<Promotion>(value) <=
- static_cast<Promotion>(DstLimits::max()));
+ static constexpr RangeConstraint Check(Src value) {
+ return sizeof(Dst) > sizeof(Src)
+ ? RANGE_VALID
+ : GetRangeConstraint(
+ value <= static_cast<Src>(NarrowingRange<Dst, Src>::max()),
+ true);
}
};
// Signed to unsigned: The upper boundary may be exceeded for a narrower Dst,
-// and any negative value exceeds the lower boundary for standard limits.
-template <typename Dst, typename Src, template <typename> class Bounds>
+// and any negative value exceeds the lower boundary.
+template <typename Dst, typename Src>
struct DstRangeRelationToSrcRangeImpl<Dst,
Src,
- Bounds,
INTEGER_REPRESENTATION_UNSIGNED,
INTEGER_REPRESENTATION_SIGNED,
NUMERIC_RANGE_NOT_CONTAINED> {
- static constexpr RangeCheck Check(Src value) {
- using SrcLimits = std::numeric_limits<Src>;
- using DstLimits = NarrowingRange<Dst, Src, Bounds>;
- using Promotion = decltype(Src() + Dst());
- return RangeCheck(
- value >= Src(0) && (DstLimits::lowest() == 0 ||
- static_cast<Dst>(value) >= DstLimits::lowest()),
- static_cast<Promotion>(SrcLimits::max()) <=
- static_cast<Promotion>(DstLimits::max()) ||
- static_cast<Promotion>(value) <=
- static_cast<Promotion>(DstLimits::max()));
+ static constexpr RangeConstraint Check(Src value) {
+ return (MaxExponent<Dst>::value >= MaxExponent<Src>::value)
+ ? GetRangeConstraint(true, value >= static_cast<Src>(0))
+ : GetRangeConstraint(
+ value <= static_cast<Src>(NarrowingRange<Dst, Src>::max()),
+ value >= static_cast<Src>(0));
}
};
-template <typename Dst,
- template <typename> class Bounds = std::numeric_limits,
- typename Src>
-constexpr RangeCheck DstRangeRelationToSrcRange(Src value) {
- static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
- static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric.");
- static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), "");
- return DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds>::Check(value);
-}
-
-// Integer promotion templates used by the portable checked integer arithmetic.
-template <size_t Size, bool IsSigned>
-struct IntegerForDigitsAndSign;
-
-#define INTEGER_FOR_DIGITS_AND_SIGN(I) \
- template <> \
- struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \
- std::is_signed<I>::value> { \
- using type = I; \
- }
-
-INTEGER_FOR_DIGITS_AND_SIGN(int8_t);
-INTEGER_FOR_DIGITS_AND_SIGN(uint8_t);
-INTEGER_FOR_DIGITS_AND_SIGN(int16_t);
-INTEGER_FOR_DIGITS_AND_SIGN(uint16_t);
-INTEGER_FOR_DIGITS_AND_SIGN(int32_t);
-INTEGER_FOR_DIGITS_AND_SIGN(uint32_t);
-INTEGER_FOR_DIGITS_AND_SIGN(int64_t);
-INTEGER_FOR_DIGITS_AND_SIGN(uint64_t);
-#undef INTEGER_FOR_DIGITS_AND_SIGN
-
-// WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
-// support 128-bit math, then the ArithmeticPromotion template below will need
-// to be updated (or more likely replaced with a decltype expression).
-static_assert(IntegerBitsPlusSign<intmax_t>::value == 64,
- "Max integer size not supported for this toolchain.");
-
-template <typename Integer, bool IsSigned = std::is_signed<Integer>::value>
-struct TwiceWiderInteger {
- using type =
- typename IntegerForDigitsAndSign<IntegerBitsPlusSign<Integer>::value * 2,
- IsSigned>::type;
-};
-
-enum ArithmeticPromotionCategory {
- LEFT_PROMOTION, // Use the type of the left-hand argument.
- RIGHT_PROMOTION // Use the type of the right-hand argument.
-};
-
-// Determines the type that can represent the largest positive value.
-template <typename Lhs,
- typename Rhs,
- ArithmeticPromotionCategory Promotion =
- (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
- ? LEFT_PROMOTION
- : RIGHT_PROMOTION>
-struct MaxExponentPromotion;
-
-template <typename Lhs, typename Rhs>
-struct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> {
- using type = Lhs;
-};
-
-template <typename Lhs, typename Rhs>
-struct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
- using type = Rhs;
-};
-
-// Determines the type that can represent the lowest arithmetic value.
-template <typename Lhs,
- typename Rhs,
- ArithmeticPromotionCategory Promotion =
- std::is_signed<Lhs>::value
- ? (std::is_signed<Rhs>::value
- ? (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value
- ? LEFT_PROMOTION
- : RIGHT_PROMOTION)
- : LEFT_PROMOTION)
- : (std::is_signed<Rhs>::value
- ? RIGHT_PROMOTION
- : (MaxExponent<Lhs>::value < MaxExponent<Rhs>::value
- ? LEFT_PROMOTION
- : RIGHT_PROMOTION))>
-struct LowestValuePromotion;
-
-template <typename Lhs, typename Rhs>
-struct LowestValuePromotion<Lhs, Rhs, LEFT_PROMOTION> {
- using type = Lhs;
-};
-
-template <typename Lhs, typename Rhs>
-struct LowestValuePromotion<Lhs, Rhs, RIGHT_PROMOTION> {
- using type = Rhs;
-};
-
-// Determines the type that is best able to represent an arithmetic result.
-template <
- typename Lhs,
- typename Rhs = Lhs,
- bool is_intmax_type =
- std::is_integral<typename MaxExponentPromotion<Lhs, Rhs>::type>::value&&
- IntegerBitsPlusSign<typename MaxExponentPromotion<Lhs, Rhs>::type>::
- value == IntegerBitsPlusSign<intmax_t>::value,
- bool is_max_exponent =
- StaticDstRangeRelationToSrcRange<
- typename MaxExponentPromotion<Lhs, Rhs>::type,
- Lhs>::value ==
- NUMERIC_RANGE_CONTAINED&& StaticDstRangeRelationToSrcRange<
- typename MaxExponentPromotion<Lhs, Rhs>::type,
- Rhs>::value == NUMERIC_RANGE_CONTAINED>
-struct BigEnoughPromotion;
-
-// The side with the max exponent is big enough.
-template <typename Lhs, typename Rhs, bool is_intmax_type>
-struct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> {
- using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
- static const bool is_contained = true;
-};
-
-// We can use a twice wider type to fit.
-template <typename Lhs, typename Rhs>
-struct BigEnoughPromotion<Lhs, Rhs, false, false> {
- using type =
- typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
- std::is_signed<Lhs>::value ||
- std::is_signed<Rhs>::value>::type;
- static const bool is_contained = true;
-};
-
-// No type is large enough.
-template <typename Lhs, typename Rhs>
-struct BigEnoughPromotion<Lhs, Rhs, true, false> {
- using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
- static const bool is_contained = false;
-};
-
-// We can statically check if operations on the provided types can wrap, so we
-// can skip the checked operations if they're not needed. So, for an integer we
-// care if the destination type preserves the sign and is twice the width of
-// the source.
-template <typename T, typename Lhs, typename Rhs = Lhs>
-struct IsIntegerArithmeticSafe {
- static const bool value =
- !std::is_floating_point<T>::value &&
- !std::is_floating_point<Lhs>::value &&
- !std::is_floating_point<Rhs>::value &&
- std::is_signed<T>::value >= std::is_signed<Lhs>::value &&
- IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Lhs>::value) &&
- std::is_signed<T>::value >= std::is_signed<Rhs>::value &&
- IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Rhs>::value);
-};
-
-// Promotes to a type that can represent any possible result of a binary
-// arithmetic operation with the source types.
-template <typename Lhs,
- typename Rhs,
- bool is_promotion_possible = IsIntegerArithmeticSafe<
- typename std::conditional<std::is_signed<Lhs>::value ||
- std::is_signed<Rhs>::value,
- intmax_t,
- uintmax_t>::type,
- typename MaxExponentPromotion<Lhs, Rhs>::type>::value>
-struct FastIntegerArithmeticPromotion;
-
-template <typename Lhs, typename Rhs>
-struct FastIntegerArithmeticPromotion<Lhs, Rhs, true> {
- using type =
- typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
- std::is_signed<Lhs>::value ||
- std::is_signed<Rhs>::value>::type;
- static_assert(IsIntegerArithmeticSafe<type, Lhs, Rhs>::value, "");
- static const bool is_contained = true;
-};
-
-template <typename Lhs, typename Rhs>
-struct FastIntegerArithmeticPromotion<Lhs, Rhs, false> {
- using type = typename BigEnoughPromotion<Lhs, Rhs>::type;
- static const bool is_contained = false;
-};
-
-// This hacks around libstdc++ 4.6 missing stuff in type_traits.
-#if defined(__GLIBCXX__)
-#define PRIV_GLIBCXX_4_7_0 20120322
-#define PRIV_GLIBCXX_4_5_4 20120702
-#define PRIV_GLIBCXX_4_6_4 20121127
-#if (__GLIBCXX__ < PRIV_GLIBCXX_4_7_0 || __GLIBCXX__ == PRIV_GLIBCXX_4_5_4 || \
- __GLIBCXX__ == PRIV_GLIBCXX_4_6_4)
-#define PRIV_USE_FALLBACKS_FOR_OLD_GLIBCXX
-#undef PRIV_GLIBCXX_4_7_0
-#undef PRIV_GLIBCXX_4_5_4
-#undef PRIV_GLIBCXX_4_6_4
-#endif
-#endif
-
-// Extracts the underlying type from an enum.
-template <typename T, bool is_enum = std::is_enum<T>::value>
-struct ArithmeticOrUnderlyingEnum;
-
-template <typename T>
-struct ArithmeticOrUnderlyingEnum<T, true> {
-#if defined(PRIV_USE_FALLBACKS_FOR_OLD_GLIBCXX)
- using type = __underlying_type(T);
-#else
- using type = typename std::underlying_type<T>::type;
-#endif
- static const bool value = std::is_arithmetic<type>::value;
-};
-
-#if defined(PRIV_USE_FALLBACKS_FOR_OLD_GLIBCXX)
-#undef PRIV_USE_FALLBACKS_FOR_OLD_GLIBCXX
-#endif
-
-template <typename T>
-struct ArithmeticOrUnderlyingEnum<T, false> {
- using type = T;
- static const bool value = std::is_arithmetic<type>::value;
-};
-
-// The following are helper templates used in the CheckedNumeric class.
-template <typename T>
-class CheckedNumeric;
-
-template <typename T>
-class StrictNumeric;
-
-// Used to treat CheckedNumeric and arithmetic underlying types the same.
-template <typename T>
-struct UnderlyingType {
- using type = typename ArithmeticOrUnderlyingEnum<T>::type;
- static const bool is_numeric = std::is_arithmetic<type>::value;
- static const bool is_checked = false;
- static const bool is_strict = false;
-};
-
-template <typename T>
-struct UnderlyingType<CheckedNumeric<T>> {
- using type = T;
- static const bool is_numeric = true;
- static const bool is_checked = true;
- static const bool is_strict = false;
-};
-
-template <typename T>
-struct UnderlyingType<StrictNumeric<T>> {
- using type = T;
- static const bool is_numeric = true;
- static const bool is_checked = false;
- static const bool is_strict = true;
-};
-
-template <typename L, typename R>
-struct IsCheckedOp {
- static const bool value =
- UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
- (UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
-};
-
-template <typename L, typename R>
-struct IsStrictOp {
- static const bool value =
- UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
- (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict);
-};
-
-template <typename L, typename R>
-constexpr bool IsLessImpl(const L lhs,
- const R rhs,
- const RangeCheck l_range,
- const RangeCheck r_range) {
- return l_range.IsUnderflow() || r_range.IsOverflow() ||
- (l_range == r_range &&
- static_cast<decltype(lhs + rhs)>(lhs) <
- static_cast<decltype(lhs + rhs)>(rhs));
-}
-
-template <typename L, typename R>
-struct IsLess {
- static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
- "Types must be numeric.");
- static constexpr bool Test(const L lhs, const R rhs) {
- return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
- DstRangeRelationToSrcRange<L>(rhs));
- }
-};
-
-template <typename L, typename R>
-constexpr bool IsLessOrEqualImpl(const L lhs,
- const R rhs,
- const RangeCheck l_range,
- const RangeCheck r_range) {
- return l_range.IsUnderflow() || r_range.IsOverflow() ||
- (l_range == r_range &&
- static_cast<decltype(lhs + rhs)>(lhs) <=
- static_cast<decltype(lhs + rhs)>(rhs));
-}
-
-template <typename L, typename R>
-struct IsLessOrEqual {
- static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
- "Types must be numeric.");
- static constexpr bool Test(const L lhs, const R rhs) {
- return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
- DstRangeRelationToSrcRange<L>(rhs));
- }
-};
-
-template <typename L, typename R>
-constexpr bool IsGreaterImpl(const L lhs,
- const R rhs,
- const RangeCheck l_range,
- const RangeCheck r_range) {
- return l_range.IsOverflow() || r_range.IsUnderflow() ||
- (l_range == r_range &&
- static_cast<decltype(lhs + rhs)>(lhs) >
- static_cast<decltype(lhs + rhs)>(rhs));
-}
-
-template <typename L, typename R>
-struct IsGreater {
- static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
- "Types must be numeric.");
- static constexpr bool Test(const L lhs, const R rhs) {
- return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
- DstRangeRelationToSrcRange<L>(rhs));
- }
-};
-
-template <typename L, typename R>
-constexpr bool IsGreaterOrEqualImpl(const L lhs,
- const R rhs,
- const RangeCheck l_range,
- const RangeCheck r_range) {
- return l_range.IsOverflow() || r_range.IsUnderflow() ||
- (l_range == r_range &&
- static_cast<decltype(lhs + rhs)>(lhs) >=
- static_cast<decltype(lhs + rhs)>(rhs));
+template <typename Dst, typename Src>
+constexpr RangeConstraint DstRangeRelationToSrcRange(Src value) {
+ static_assert(std::numeric_limits<Src>::is_specialized,
+ "Argument must be numeric.");
+ static_assert(std::numeric_limits<Dst>::is_specialized,
+ "Result must be numeric.");
+ return DstRangeRelationToSrcRangeImpl<Dst, Src>::Check(value);
}
-template <typename L, typename R>
-struct IsGreaterOrEqual {
- static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
- "Types must be numeric.");
- static constexpr bool Test(const L lhs, const R rhs) {
- return IsGreaterOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
- DstRangeRelationToSrcRange<L>(rhs));
- }
-};
-
-template <typename L, typename R>
-struct IsEqual {
- static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
- "Types must be numeric.");
- static constexpr bool Test(const L lhs, const R rhs) {
- return DstRangeRelationToSrcRange<R>(lhs) ==
- DstRangeRelationToSrcRange<L>(rhs) &&
- static_cast<decltype(lhs + rhs)>(lhs) ==
- static_cast<decltype(lhs + rhs)>(rhs);
- }
-};
-
-template <typename L, typename R>
-struct IsNotEqual {
- static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
- "Types must be numeric.");
- static constexpr bool Test(const L lhs, const R rhs) {
- return DstRangeRelationToSrcRange<R>(lhs) !=
- DstRangeRelationToSrcRange<L>(rhs) ||
- static_cast<decltype(lhs + rhs)>(lhs) !=
- static_cast<decltype(lhs + rhs)>(rhs);
- }
-};
-
-// These perform the actual math operations on the CheckedNumerics.
-// Binary arithmetic operations.
-template <template <typename, typename> class C, typename L, typename R>
-constexpr bool SafeCompare(const L lhs, const R rhs) {
- static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
- "Types must be numeric.");
- using Promotion = BigEnoughPromotion<L, R>;
- using BigType = typename Promotion::type;
- return Promotion::is_contained
- // Force to a larger type for speed if both are contained.
- ? C<BigType, BigType>::Test(
- static_cast<BigType>(static_cast<L>(lhs)),
- static_cast<BigType>(static_cast<R>(rhs)))
- // Let the template functions figure it out for mixed types.
- : C<L, R>::Test(lhs, rhs);
-};
-
} // namespace internal
} // namespace base
diff --git a/base/numerics/safe_math.h b/base/numerics/safe_math.h
index f5007db39c..d0003b79db 100644
--- a/base/numerics/safe_math.h
+++ b/base/numerics/safe_math.h
@@ -10,259 +10,155 @@
#include <limits>
#include <type_traits>
+#include "base/logging.h"
#include "base/numerics/safe_math_impl.h"
namespace base {
+
namespace internal {
-// CheckedNumeric<> implements all the logic and operators for detecting integer
+// CheckedNumeric implements all the logic and operators for detecting integer
// boundary conditions such as overflow, underflow, and invalid conversions.
// The CheckedNumeric type implicitly converts from floating point and integer
// data types, and contains overloads for basic arithmetic operations (i.e.: +,
-// -, *, / for all types and %, <<, >>, &, |, ^ for integers). Type promotions
-// are a slightly modified version of the standard C arithmetic rules with the
-// two differences being that there is no default promotion to int and bitwise
-// logical operations always return an unsigned of the wider type.
-//
-// You may also use one of the variadic convenience functions, which accept
-// standard arithmetic or CheckedNumeric types, perform arithmetic operations,
-// and return a CheckedNumeric result. The supported functions are:
-// CheckAdd() - Addition.
-// CheckSub() - Subtraction.
-// CheckMul() - Multiplication.
-// CheckDiv() - Division.
-// CheckMod() - Modulous (integer only).
-// CheckLsh() - Left integer shift (integer only).
-// CheckRsh() - Right integer shift (integer only).
-// CheckAnd() - Bitwise AND (integer only with unsigned result).
-// CheckOr() - Bitwise OR (integer only with unsigned result).
-// CheckXor() - Bitwise XOR (integer only with unsigned result).
-// CheckMax() - Maximum of supplied arguments.
-// CheckMin() - Minimum of supplied arguments.
-//
-// The unary negation, increment, and decrement operators are supported, along
-// with the following unary arithmetic methods, which return a new
-// CheckedNumeric as a result of the operation:
-// Abs() - Absolute value.
-// UnsignedAbs() - Absolute value as an equal-width unsigned underlying type
-// (valid for only integral types).
-// Max() - Returns whichever is greater of the current instance or argument.
-// The underlying return type is whichever has the greatest magnitude.
-// Min() - Returns whichever is lowest of the current instance or argument.
-// The underlying return type is whichever has can represent the lowest
-// number in the smallest width (e.g. int8_t over unsigned, int over
-// int8_t, and float over int).
+// -, *, /, %).
//
// The following methods convert from CheckedNumeric to standard numeric values:
-// AssignIfValid() - Assigns the underlying value to the supplied destination
-// pointer if the value is currently valid and within the range
-// supported by the destination type. Returns true on success.
-// ****************************************************************************
-// * WARNING: All of the following functions return a StrictNumeric, which *
-// * is valid for comparison and assignment operations, but will trigger a *
-// * compile failure on attempts to assign to a type of insufficient range. *
-// ****************************************************************************
-// IsValid() - Returns true if the underlying numeric value is valid (i.e. has
-// has not wrapped and is not the result of an invalid conversion).
-// ValueOrDie() - Returns the underlying value. If the state is not valid this
-// call will crash on a CHECK.
-// ValueOrDefault() - Returns the current value, or the supplied default if the
-// state is not valid (will not trigger a CHECK).
+// IsValid() - Returns true if the underlying numeric value is valid (i.e. has
+// has not wrapped and is not the result of an invalid conversion).
+// ValueOrDie() - Returns the underlying value. If the state is not valid this
+// call will crash on a CHECK.
+// ValueOrDefault() - Returns the current value, or the supplied default if the
+// state is not valid.
+// ValueFloating() - Returns the underlying floating point value (valid only
+// only for floating point CheckedNumeric types).
//
-// The following wrapper functions can be used to avoid the template
-// disambiguator syntax when converting a destination type.
-// IsValidForType<>() in place of: a.template IsValid<Dst>()
-// ValueOrDieForType<>() in place of: a.template ValueOrDie()
-// ValueOrDefaultForType<>() in place of: a.template ValueOrDefault(default)
-//
-// The following are general utility methods that are useful for converting
-// between arithmetic types and CheckedNumeric types:
-// CheckedNumeric::Cast<Dst>() - Instance method returning a CheckedNumeric
-// derived from casting the current instance to a CheckedNumeric of
-// the supplied destination type.
-// MakeCheckedNum() - Creates a new CheckedNumeric from the underlying type of
-// the supplied arithmetic, CheckedNumeric, or StrictNumeric type.
-//
-// Comparison operations are explicitly not supported because they could result
-// in a crash on an unexpected CHECK condition. You should use patterns like the
-// following for comparisons:
+// Bitwise operations are explicitly not supported, because correct
+// handling of some cases (e.g. sign manipulation) is ambiguous. Comparison
+// operations are explicitly not supported because they could result in a crash
+// on a CHECK condition. You should use patterns like the following for these
+// operations:
+// Bitwise operation:
+// CheckedNumeric<int> checked_int = untrusted_input_value;
+// int x = checked_int.ValueOrDefault(0) | kFlagValues;
+// Comparison:
// CheckedNumeric<size_t> checked_size = untrusted_input_value;
// checked_size += HEADER LENGTH;
// if (checked_size.IsValid() && checked_size.ValueOrDie() < buffer_size)
// Do stuff...
-
template <typename T>
class CheckedNumeric {
static_assert(std::is_arithmetic<T>::value,
"CheckedNumeric<T>: T must be a numeric type.");
public:
- using type = T;
+ typedef T type;
- constexpr CheckedNumeric() {}
+ CheckedNumeric() {}
// Copy constructor.
template <typename Src>
- constexpr CheckedNumeric(const CheckedNumeric<Src>& rhs)
- : state_(rhs.state_.value(), rhs.IsValid()) {}
+ CheckedNumeric(const CheckedNumeric<Src>& rhs)
+ : state_(rhs.ValueUnsafe(), rhs.validity()) {}
template <typename Src>
- friend class CheckedNumeric;
+ CheckedNumeric(Src value, RangeConstraint validity)
+ : state_(value, validity) {}
// This is not an explicit constructor because we implicitly upgrade regular
// numerics to CheckedNumerics to make them easier to use.
template <typename Src>
- constexpr CheckedNumeric(Src value) // NOLINT(runtime/explicit)
+ CheckedNumeric(Src value) // NOLINT(runtime/explicit)
: state_(value) {
- static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
+ static_assert(std::numeric_limits<Src>::is_specialized,
+ "Argument must be numeric.");
}
// This is not an explicit constructor because we want a seamless conversion
// from StrictNumeric types.
template <typename Src>
- constexpr CheckedNumeric(
- StrictNumeric<Src> value) // NOLINT(runtime/explicit)
- : state_(static_cast<Src>(value)) {}
-
- // IsValid() - The public API to test if a CheckedNumeric is currently valid.
- // A range checked destination type can be supplied using the Dst template
- // parameter.
- template <typename Dst = T>
- constexpr bool IsValid() const {
- return state_.is_valid() &&
- IsValueInRangeForNumericType<Dst>(state_.value());
+ CheckedNumeric(StrictNumeric<Src> value) // NOLINT(runtime/explicit)
+ : state_(static_cast<Src>(value)) {
}
- // AssignIfValid(Dst) - Assigns the underlying value if it is currently valid
- // and is within the range supported by the destination type. Returns true if
- // successful and false otherwise.
- template <typename Dst>
- constexpr bool AssignIfValid(Dst* result) const {
- return IsValid<Dst>() ? ((*result = static_cast<Dst>(state_.value())), true)
- : false;
- }
+ // IsValid() is the public API to test if a CheckedNumeric is currently valid.
+ bool IsValid() const { return validity() == RANGE_VALID; }
- // ValueOrDie() - The primary accessor for the underlying value. If the
- // current state is not valid it will CHECK and crash.
- // A range checked destination type can be supplied using the Dst template
- // parameter, which will trigger a CHECK if the value is not in bounds for
- // the destination.
- // The CHECK behavior can be overridden by supplying a handler as a
- // template parameter, for test code, etc. However, the handler cannot access
- // the underlying value, and it is not available through other means.
- template <typename Dst = T, class CheckHandler = CheckOnFailure>
- constexpr StrictNumeric<Dst> ValueOrDie() const {
- return IsValid<Dst>() ? static_cast<Dst>(state_.value())
- : CheckHandler::template HandleFailure<Dst>();
+ // ValueOrDie() The primary accessor for the underlying value. If the current
+ // state is not valid it will CHECK and crash.
+ T ValueOrDie() const {
+ CHECK(IsValid());
+ return state_.value();
}
- // ValueOrDefault(T default_value) - A convenience method that returns the
+ // ValueOrDefault(T default_value) A convenience method that returns the
// current value if the state is valid, and the supplied default_value for
// any other state.
- // A range checked destination type can be supplied using the Dst template
- // parameter. WARNING: This function may fail to compile or CHECK at runtime
- // if the supplied default_value is not within range of the destination type.
- template <typename Dst = T, typename Src>
- constexpr StrictNumeric<Dst> ValueOrDefault(const Src default_value) const {
- return IsValid<Dst>() ? static_cast<Dst>(state_.value())
- : checked_cast<Dst>(default_value);
+ T ValueOrDefault(T default_value) const {
+ return IsValid() ? state_.value() : default_value;
}
- // Returns a checked numeric of the specified type, cast from the current
- // CheckedNumeric. If the current state is invalid or the destination cannot
- // represent the result then the returned CheckedNumeric will be invalid.
- template <typename Dst>
- constexpr CheckedNumeric<typename UnderlyingType<Dst>::type> Cast() const {
- return *this;
+ // ValueFloating() - Since floating point values include their validity state,
+ // we provide an easy method for extracting them directly, without a risk of
+ // crashing on a CHECK.
+ T ValueFloating() const {
+ static_assert(std::numeric_limits<T>::is_iec559, "Argument must be float.");
+ return CheckedNumeric<T>::cast(*this).ValueUnsafe();
}
- // This friend method is available solely for providing more detailed logging
- // in the the tests. Do not implement it in production code, because the
- // underlying values may change at any time.
- template <typename U>
- friend U GetNumericValueForTest(const CheckedNumeric<U>& src);
+ // validity() - DO NOT USE THIS IN EXTERNAL CODE - It is public right now for
+ // tests and to avoid a big matrix of friend operator overloads. But the
+ // values it returns are likely to change in the future.
+ // Returns: current validity state (i.e. valid, overflow, underflow, nan).
+ // TODO(jschuh): crbug.com/332611 Figure out and implement semantics for
+ // saturation/wrapping so we can expose this state consistently and implement
+ // saturated arithmetic.
+ RangeConstraint validity() const { return state_.validity(); }
+
+ // ValueUnsafe() - DO NOT USE THIS IN EXTERNAL CODE - It is public right now
+ // for tests and to avoid a big matrix of friend operator overloads. But the
+ // values it returns are likely to change in the future.
+ // Returns: the raw numeric value, regardless of the current state.
+ // TODO(jschuh): crbug.com/332611 Figure out and implement semantics for
+ // saturation/wrapping so we can expose this state consistently and implement
+ // saturated arithmetic.
+ T ValueUnsafe() const { return state_.value(); }
// Prototypes for the supported arithmetic operator overloads.
- template <typename Src>
- CheckedNumeric& operator+=(const Src rhs);
- template <typename Src>
- CheckedNumeric& operator-=(const Src rhs);
- template <typename Src>
- CheckedNumeric& operator*=(const Src rhs);
- template <typename Src>
- CheckedNumeric& operator/=(const Src rhs);
- template <typename Src>
- CheckedNumeric& operator%=(const Src rhs);
- template <typename Src>
- CheckedNumeric& operator<<=(const Src rhs);
- template <typename Src>
- CheckedNumeric& operator>>=(const Src rhs);
- template <typename Src>
- CheckedNumeric& operator&=(const Src rhs);
- template <typename Src>
- CheckedNumeric& operator|=(const Src rhs);
- template <typename Src>
- CheckedNumeric& operator^=(const Src rhs);
-
- constexpr CheckedNumeric operator-() const {
- return CheckedNumeric<T>(
- NegateWrapper(state_.value()),
- IsValid() &&
- (!std::is_signed<T>::value || std::is_floating_point<T>::value ||
- NegateWrapper(state_.value()) !=
- std::numeric_limits<T>::lowest()));
+ template <typename Src> CheckedNumeric& operator+=(Src rhs);
+ template <typename Src> CheckedNumeric& operator-=(Src rhs);
+ template <typename Src> CheckedNumeric& operator*=(Src rhs);
+ template <typename Src> CheckedNumeric& operator/=(Src rhs);
+ template <typename Src> CheckedNumeric& operator%=(Src rhs);
+
+ CheckedNumeric operator-() const {
+ RangeConstraint validity;
+ T value = CheckedNeg(state_.value(), &validity);
+ // Negation is always valid for floating point.
+ if (std::numeric_limits<T>::is_iec559)
+ return CheckedNumeric<T>(value);
+
+ validity = GetRangeConstraint(state_.validity() | validity);
+ return CheckedNumeric<T>(value, validity);
}
- constexpr CheckedNumeric operator~() const {
- return CheckedNumeric<decltype(InvertWrapper(T()))>(
- InvertWrapper(state_.value()), IsValid());
- }
-
- constexpr CheckedNumeric Abs() const {
- return CheckedNumeric<T>(
- AbsWrapper(state_.value()),
- IsValid() &&
- (!std::is_signed<T>::value || std::is_floating_point<T>::value ||
- AbsWrapper(state_.value()) != std::numeric_limits<T>::lowest()));
- }
+ CheckedNumeric Abs() const {
+ RangeConstraint validity;
+ T value = CheckedAbs(state_.value(), &validity);
+ // Absolute value is always valid for floating point.
+ if (std::numeric_limits<T>::is_iec559)
+ return CheckedNumeric<T>(value);
- template <typename U>
- constexpr CheckedNumeric<typename MathWrapper<CheckedMaxOp, T, U>::type> Max(
- const U rhs) const {
- using R = typename UnderlyingType<U>::type;
- using result_type = typename MathWrapper<CheckedMaxOp, T, U>::type;
- // TODO(jschuh): This can be converted to the MathOp version and remain
- // constexpr once we have C++14 support.
- return CheckedNumeric<result_type>(
- static_cast<result_type>(
- IsGreater<T, R>::Test(state_.value(), Wrapper<U>::value(rhs))
- ? state_.value()
- : Wrapper<U>::value(rhs)),
- state_.is_valid() && Wrapper<U>::is_valid(rhs));
- }
-
- template <typename U>
- constexpr CheckedNumeric<typename MathWrapper<CheckedMinOp, T, U>::type> Min(
- const U rhs) const {
- using R = typename UnderlyingType<U>::type;
- using result_type = typename MathWrapper<CheckedMinOp, T, U>::type;
- // TODO(jschuh): This can be converted to the MathOp version and remain
- // constexpr once we have C++14 support.
- return CheckedNumeric<result_type>(
- static_cast<result_type>(
- IsLess<T, R>::Test(state_.value(), Wrapper<U>::value(rhs))
- ? state_.value()
- : Wrapper<U>::value(rhs)),
- state_.is_valid() && Wrapper<U>::is_valid(rhs));
+ validity = GetRangeConstraint(state_.validity() | validity);
+ return CheckedNumeric<T>(value, validity);
}
// This function is available only for integral types. It returns an unsigned
// integer of the same width as the source type, containing the absolute value
// of the source, and properly handling signed min.
- constexpr CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>
- UnsignedAbs() const {
+ CheckedNumeric<typename UnsignedOrFloatForSize<T>::type> UnsignedAbs() const {
return CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>(
- SafeUnsignedAbs(state_.value()), state_.is_valid());
+ CheckedUnsignedAbs(state_.value()), state_.validity());
}
CheckedNumeric& operator++() {
@@ -287,221 +183,126 @@ class CheckedNumeric {
return value;
}
- // These perform the actual math operations on the CheckedNumerics.
- // Binary arithmetic operations.
- template <template <typename, typename, typename> class M,
- typename L,
- typename R>
- static CheckedNumeric MathOp(const L lhs, const R rhs) {
- using Math = typename MathWrapper<M, L, R>::math;
- T result = 0;
- bool is_valid =
- Wrapper<L>::is_valid(lhs) && Wrapper<R>::is_valid(rhs) &&
- Math::Do(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs), &result);
- return CheckedNumeric<T>(result, is_valid);
- };
-
- // Assignment arithmetic operations.
- template <template <typename, typename, typename> class M, typename R>
- CheckedNumeric& MathOp(const R rhs) {
- using Math = typename MathWrapper<M, T, R>::math;
- T result = 0; // Using T as the destination saves a range check.
- bool is_valid = state_.is_valid() && Wrapper<R>::is_valid(rhs) &&
- Math::Do(state_.value(), Wrapper<R>::value(rhs), &result);
- *this = CheckedNumeric<T>(result, is_valid);
- return *this;
- };
-
- private:
- CheckedNumericState<T> state_;
-
+ // These static methods behave like a convenience cast operator targeting
+ // the desired CheckedNumeric type. As an optimization, a reference is
+ // returned when Src is the same type as T.
template <typename Src>
- constexpr CheckedNumeric(Src value, bool is_valid)
- : state_(value, is_valid) {}
+ static CheckedNumeric<T> cast(
+ Src u,
+ typename std::enable_if<std::numeric_limits<Src>::is_specialized,
+ int>::type = 0) {
+ return u;
+ }
- // These wrappers allow us to handle state the same way for both
- // CheckedNumeric and POD arithmetic types.
template <typename Src>
- struct Wrapper {
- static constexpr bool is_valid(Src) { return true; }
- static constexpr Src value(Src value) { return value; }
- };
+ static CheckedNumeric<T> cast(
+ const CheckedNumeric<Src>& u,
+ typename std::enable_if<!std::is_same<Src, T>::value, int>::type = 0) {
+ return u;
+ }
- template <typename Src>
- struct Wrapper<CheckedNumeric<Src>> {
- static constexpr bool is_valid(const CheckedNumeric<Src> v) {
- return v.IsValid();
- }
- static constexpr Src value(const CheckedNumeric<Src> v) {
- return v.state_.value();
- }
- };
+ static const CheckedNumeric<T>& cast(const CheckedNumeric<T>& u) { return u; }
- template <typename Src>
- struct Wrapper<StrictNumeric<Src>> {
- static constexpr bool is_valid(const StrictNumeric<Src>) { return true; }
- static constexpr Src value(const StrictNumeric<Src> v) {
- return static_cast<Src>(v);
- }
+ private:
+ template <typename NumericType>
+ struct UnderlyingType {
+ using type = NumericType;
};
-};
-// Convenience functions to avoid the ugly template disambiguator syntax.
-template <typename Dst, typename Src>
-constexpr bool IsValidForType(const CheckedNumeric<Src> value) {
- return value.template IsValid<Dst>();
-}
-
-template <typename Dst, typename Src>
-constexpr StrictNumeric<Dst> ValueOrDieForType(
- const CheckedNumeric<Src> value) {
- return value.template ValueOrDie<Dst>();
-}
-
-template <typename Dst, typename Src, typename Default>
-constexpr StrictNumeric<Dst> ValueOrDefaultForType(
- const CheckedNumeric<Src> value,
- const Default default_value) {
- return value.template ValueOrDefault<Dst>(default_value);
-}
-
-// These variadic templates work out the return types.
-// TODO(jschuh): Rip all this out once we have C++14 non-trailing auto support.
-template <template <typename, typename, typename> class M,
- typename L,
- typename R,
- typename... Args>
-struct ResultType;
-
-template <template <typename, typename, typename> class M,
- typename L,
- typename R>
-struct ResultType<M, L, R> {
- using type = typename MathWrapper<M, L, R>::type;
-};
-
-template <template <typename, typename, typename> class M,
- typename L,
- typename R,
- typename... Args>
-struct ResultType {
- using type =
- typename ResultType<M, typename ResultType<M, L, R>::type, Args...>::type;
-};
+ template <typename NumericType>
+ struct UnderlyingType<CheckedNumeric<NumericType>> {
+ using type = NumericType;
+ };
-// Convience wrapper to return a new CheckedNumeric from the provided arithmetic
-// or CheckedNumericType.
-template <typename T>
-constexpr CheckedNumeric<typename UnderlyingType<T>::type> MakeCheckedNum(
- const T value) {
- return value;
-}
-
-// These implement the variadic wrapper for the math operations.
-template <template <typename, typename, typename> class M,
- typename L,
- typename R>
-CheckedNumeric<typename MathWrapper<M, L, R>::type> ChkMathOp(const L lhs,
- const R rhs) {
- using Math = typename MathWrapper<M, L, R>::math;
- return CheckedNumeric<typename Math::result_type>::template MathOp<M>(lhs,
- rhs);
-}
-
-// General purpose wrapper template for arithmetic operations.
-template <template <typename, typename, typename> class M,
- typename L,
- typename R,
- typename... Args>
-CheckedNumeric<typename ResultType<M, L, R, Args...>::type>
-ChkMathOp(const L lhs, const R rhs, const Args... args) {
- auto tmp = ChkMathOp<M>(lhs, rhs);
- return tmp.IsValid() ? ChkMathOp<M>(tmp, args...)
- : decltype(ChkMathOp<M>(tmp, args...))(tmp);
+ CheckedNumericState<T> state_;
};
-// The following macros are just boilerplate for the standard arithmetic
-// operator overloads and variadic function templates. A macro isn't the nicest
-// solution, but it beats rewriting these over and over again.
-#define BASE_NUMERIC_ARITHMETIC_VARIADIC(NAME) \
- template <typename L, typename R, typename... Args> \
- CheckedNumeric<typename ResultType<Checked##NAME##Op, L, R, Args...>::type> \
- Check##NAME(const L lhs, const R rhs, const Args... args) { \
- return ChkMathOp<Checked##NAME##Op, L, R, Args...>(lhs, rhs, args...); \
+// This is the boilerplate for the standard arithmetic operator overloads. A
+// macro isn't the prettiest solution, but it beats rewriting these five times.
+// Some details worth noting are:
+// * We apply the standard arithmetic promotions.
+// * We skip range checks for floating points.
+// * We skip range checks for destination integers with sufficient range.
+// TODO(jschuh): extract these out into templates.
+#define BASE_NUMERIC_ARITHMETIC_OPERATORS(NAME, OP, COMPOUND_OP) \
+ /* Binary arithmetic operator for CheckedNumerics of the same type. */ \
+ template <typename T> \
+ CheckedNumeric<typename ArithmeticPromotion<T>::type> operator OP( \
+ const CheckedNumeric<T>& lhs, const CheckedNumeric<T>& rhs) { \
+ typedef typename ArithmeticPromotion<T>::type Promotion; \
+ /* Floating point always takes the fast path */ \
+ if (std::numeric_limits<T>::is_iec559) \
+ return CheckedNumeric<T>(lhs.ValueUnsafe() OP rhs.ValueUnsafe()); \
+ if (IsIntegerArithmeticSafe<Promotion, T, T>::value) \
+ return CheckedNumeric<Promotion>( \
+ lhs.ValueUnsafe() OP rhs.ValueUnsafe(), \
+ GetRangeConstraint(rhs.validity() | lhs.validity())); \
+ RangeConstraint validity = RANGE_VALID; \
+ T result = static_cast<T>( \
+ Checked##NAME(static_cast<Promotion>(lhs.ValueUnsafe()), \
+ static_cast<Promotion>(rhs.ValueUnsafe()), &validity)); \
+ return CheckedNumeric<Promotion>( \
+ result, \
+ GetRangeConstraint(validity | lhs.validity() | rhs.validity())); \
+ } \
+ /* Assignment arithmetic operator implementation from CheckedNumeric. */ \
+ template <typename T> \
+ template <typename Src> \
+ CheckedNumeric<T>& CheckedNumeric<T>::operator COMPOUND_OP(Src rhs) { \
+ *this = CheckedNumeric<T>::cast(*this) \
+ OP CheckedNumeric<typename UnderlyingType<Src>::type>::cast(rhs); \
+ return *this; \
+ } \
+ /* Binary arithmetic operator for CheckedNumeric of different type. */ \
+ template <typename T, typename Src> \
+ CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP( \
+ const CheckedNumeric<Src>& lhs, const CheckedNumeric<T>& rhs) { \
+ typedef typename ArithmeticPromotion<T, Src>::type Promotion; \
+ if (IsIntegerArithmeticSafe<Promotion, T, Src>::value) \
+ return CheckedNumeric<Promotion>( \
+ lhs.ValueUnsafe() OP rhs.ValueUnsafe(), \
+ GetRangeConstraint(rhs.validity() | lhs.validity())); \
+ return CheckedNumeric<Promotion>::cast(lhs) \
+ OP CheckedNumeric<Promotion>::cast(rhs); \
+ } \
+ /* Binary arithmetic operator for left CheckedNumeric and right numeric. */ \
+ template <typename T, typename Src, \
+ typename std::enable_if<std::is_arithmetic<Src>::value>::type* = \
+ nullptr> \
+ CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP( \
+ const CheckedNumeric<T>& lhs, Src rhs) { \
+ typedef typename ArithmeticPromotion<T, Src>::type Promotion; \
+ if (IsIntegerArithmeticSafe<Promotion, T, Src>::value) \
+ return CheckedNumeric<Promotion>(lhs.ValueUnsafe() OP rhs, \
+ lhs.validity()); \
+ return CheckedNumeric<Promotion>::cast(lhs) \
+ OP CheckedNumeric<Promotion>::cast(rhs); \
+ } \
+ /* Binary arithmetic operator for left numeric and right CheckedNumeric. */ \
+ template <typename T, typename Src, \
+ typename std::enable_if<std::is_arithmetic<Src>::value>::type* = \
+ nullptr> \
+ CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP( \
+ Src lhs, const CheckedNumeric<T>& rhs) { \
+ typedef typename ArithmeticPromotion<T, Src>::type Promotion; \
+ if (IsIntegerArithmeticSafe<Promotion, T, Src>::value) \
+ return CheckedNumeric<Promotion>(lhs OP rhs.ValueUnsafe(), \
+ rhs.validity()); \
+ return CheckedNumeric<Promotion>::cast(lhs) \
+ OP CheckedNumeric<Promotion>::cast(rhs); \
}
-#define BASE_NUMERIC_ARITHMETIC_OPERATORS(NAME, OP, COMPOUND_OP) \
- /* Binary arithmetic operator for all CheckedNumeric operations. */ \
- template <typename L, typename R, \
- typename std::enable_if<IsCheckedOp<L, R>::value>::type* = \
- nullptr> \
- CheckedNumeric<typename MathWrapper<Checked##NAME##Op, L, R>::type> \
- operator OP(const L lhs, const R rhs) { \
- return decltype(lhs OP rhs)::template MathOp<Checked##NAME##Op>(lhs, rhs); \
- } \
- /* Assignment arithmetic operator implementation from CheckedNumeric. */ \
- template <typename L> \
- template <typename R> \
- CheckedNumeric<L>& CheckedNumeric<L>::operator COMPOUND_OP(const R rhs) { \
- return MathOp<Checked##NAME##Op>(rhs); \
- } \
- /* Variadic arithmetic functions that return CheckedNumeric. */ \
- BASE_NUMERIC_ARITHMETIC_VARIADIC(NAME)
-
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Add, +, +=)
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Sub, -, -=)
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Mul, *, *=)
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Div, /, /=)
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Mod, %, %=)
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Lsh, <<, <<=)
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Rsh, >>, >>=)
-BASE_NUMERIC_ARITHMETIC_OPERATORS(And, &, &=)
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Or, |, |=)
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Xor, ^, ^=)
-BASE_NUMERIC_ARITHMETIC_VARIADIC(Max)
-BASE_NUMERIC_ARITHMETIC_VARIADIC(Min)
-
-#undef BASE_NUMERIC_ARITHMETIC_VARIADIC
-#undef BASE_NUMERIC_ARITHMETIC_OPERATORS
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Add, +, += )
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Sub, -, -= )
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Mul, *, *= )
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Div, /, /= )
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Mod, %, %= )
-// These are some extra StrictNumeric operators to support simple pointer
-// arithmetic with our result types. Since wrapping on a pointer is always
-// bad, we trigger the CHECK condition here.
-template <typename L, typename R>
-L* operator+(L* lhs, const StrictNumeric<R> rhs) {
- uintptr_t result = CheckAdd(reinterpret_cast<uintptr_t>(lhs),
- CheckMul(sizeof(L), static_cast<R>(rhs)))
- .template ValueOrDie<uintptr_t>();
- return reinterpret_cast<L*>(result);
-}
-
-template <typename L, typename R>
-L* operator-(L* lhs, const StrictNumeric<R> rhs) {
- uintptr_t result = CheckSub(reinterpret_cast<uintptr_t>(lhs),
- CheckMul(sizeof(L), static_cast<R>(rhs)))
- .template ValueOrDie<uintptr_t>();
- return reinterpret_cast<L*>(result);
-}
+#undef BASE_NUMERIC_ARITHMETIC_OPERATORS
} // namespace internal
using internal::CheckedNumeric;
-using internal::IsValidForType;
-using internal::ValueOrDieForType;
-using internal::ValueOrDefaultForType;
-using internal::MakeCheckedNum;
-using internal::CheckMax;
-using internal::CheckMin;
-using internal::CheckAdd;
-using internal::CheckSub;
-using internal::CheckMul;
-using internal::CheckDiv;
-using internal::CheckMod;
-using internal::CheckLsh;
-using internal::CheckRsh;
-using internal::CheckAnd;
-using internal::CheckOr;
-using internal::CheckXor;
} // namespace base
diff --git a/base/numerics/safe_math_impl.h b/base/numerics/safe_math_impl.h
index a224f692dd..f214f3fec2 100644
--- a/base/numerics/safe_math_impl.h
+++ b/base/numerics/safe_math_impl.h
@@ -23,486 +23,348 @@ namespace internal {
// but it may not be fast. This code could be split based on
// platform/architecture and replaced with potentially faster implementations.
+// Integer promotion templates used by the portable checked integer arithmetic.
+template <size_t Size, bool IsSigned>
+struct IntegerForSizeAndSign;
+template <>
+struct IntegerForSizeAndSign<1, true> {
+ typedef int8_t type;
+};
+template <>
+struct IntegerForSizeAndSign<1, false> {
+ typedef uint8_t type;
+};
+template <>
+struct IntegerForSizeAndSign<2, true> {
+ typedef int16_t type;
+};
+template <>
+struct IntegerForSizeAndSign<2, false> {
+ typedef uint16_t type;
+};
+template <>
+struct IntegerForSizeAndSign<4, true> {
+ typedef int32_t type;
+};
+template <>
+struct IntegerForSizeAndSign<4, false> {
+ typedef uint32_t type;
+};
+template <>
+struct IntegerForSizeAndSign<8, true> {
+ typedef int64_t type;
+};
+template <>
+struct IntegerForSizeAndSign<8, false> {
+ typedef uint64_t type;
+};
+
+// WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
+// support 128-bit math, then the ArithmeticPromotion template below will need
+// to be updated (or more likely replaced with a decltype expression).
+
+template <typename Integer>
+struct UnsignedIntegerForSize {
+ typedef typename std::enable_if<
+ std::numeric_limits<Integer>::is_integer,
+ typename IntegerForSizeAndSign<sizeof(Integer), false>::type>::type type;
+};
+
+template <typename Integer>
+struct SignedIntegerForSize {
+ typedef typename std::enable_if<
+ std::numeric_limits<Integer>::is_integer,
+ typename IntegerForSizeAndSign<sizeof(Integer), true>::type>::type type;
+};
+
+template <typename Integer>
+struct TwiceWiderInteger {
+ typedef typename std::enable_if<
+ std::numeric_limits<Integer>::is_integer,
+ typename IntegerForSizeAndSign<
+ sizeof(Integer) * 2,
+ std::numeric_limits<Integer>::is_signed>::type>::type type;
+};
+
+template <typename Integer>
+struct PositionOfSignBit {
+ static const typename std::enable_if<std::numeric_limits<Integer>::is_integer,
+ size_t>::type value =
+ CHAR_BIT * sizeof(Integer) - 1;
+};
+
// This is used for UnsignedAbs, where we need to support floating-point
// template instantiations even though we don't actually support the operations.
-// However, there is no corresponding implementation of e.g. SafeUnsignedAbs,
+// However, there is no corresponding implementation of e.g. CheckedUnsignedAbs,
// so the float versions will not compile.
template <typename Numeric,
- bool IsInteger = std::is_integral<Numeric>::value,
- bool IsFloat = std::is_floating_point<Numeric>::value>
+ bool IsInteger = std::numeric_limits<Numeric>::is_integer,
+ bool IsFloat = std::numeric_limits<Numeric>::is_iec559>
struct UnsignedOrFloatForSize;
template <typename Numeric>
struct UnsignedOrFloatForSize<Numeric, true, false> {
- using type = typename std::make_unsigned<Numeric>::type;
+ typedef typename UnsignedIntegerForSize<Numeric>::type type;
};
template <typename Numeric>
struct UnsignedOrFloatForSize<Numeric, false, true> {
- using type = Numeric;
+ typedef Numeric type;
};
-// Probe for builtin math overflow support on Clang and version check on GCC.
-#if defined(__has_builtin)
-#define USE_OVERFLOW_BUILTINS (__has_builtin(__builtin_add_overflow))
-#elif defined(__GNUC__)
-#define USE_OVERFLOW_BUILTINS (__GNUC__ >= 5)
-#else
-#define USE_OVERFLOW_BUILTINS (0)
-#endif
+// Helper templates for integer manipulations.
template <typename T>
-bool CheckedAddImpl(T x, T y, T* result) {
- static_assert(std::is_integral<T>::value, "Type must be integral");
+constexpr bool HasSignBit(T x) {
+ // Cast to unsigned since right shift on signed is undefined.
+ return !!(static_cast<typename UnsignedIntegerForSize<T>::type>(x) >>
+ PositionOfSignBit<T>::value);
+}
+
+// This wrapper undoes the standard integer promotions.
+template <typename T>
+constexpr T BinaryComplement(T x) {
+ return static_cast<T>(~x);
+}
+
+// Here are the actual portable checked integer math implementations.
+// TODO(jschuh): Break this code out from the enable_if pattern and find a clean
+// way to coalesce things into the CheckedNumericState specializations below.
+
+template <typename T>
+typename std::enable_if<std::numeric_limits<T>::is_integer, T>::type
+CheckedAdd(T x, T y, RangeConstraint* validity) {
// Since the value of x+y is undefined if we have a signed type, we compute
// it using the unsigned type of the same size.
- using UnsignedDst = typename std::make_unsigned<T>::type;
- using SignedDst = typename std::make_signed<T>::type;
+ typedef typename UnsignedIntegerForSize<T>::type UnsignedDst;
UnsignedDst ux = static_cast<UnsignedDst>(x);
UnsignedDst uy = static_cast<UnsignedDst>(y);
UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy);
- *result = static_cast<T>(uresult);
// Addition is valid if the sign of (x + y) is equal to either that of x or
// that of y.
- return (std::is_signed<T>::value)
- ? static_cast<SignedDst>((uresult ^ ux) & (uresult ^ uy)) >= 0
- : uresult >= uy; // Unsigned is either valid or underflow.
-}
-
-template <typename T, typename U, class Enable = void>
-struct CheckedAddOp {};
-
-template <typename T, typename U>
-struct CheckedAddOp<T,
- U,
- typename std::enable_if<std::is_integral<T>::value &&
- std::is_integral<U>::value>::type> {
- using result_type = typename MaxExponentPromotion<T, U>::type;
- template <typename V>
- static bool Do(T x, U y, V* result) {
-#if USE_OVERFLOW_BUILTINS
- return !__builtin_add_overflow(x, y, result);
-#else
- using Promotion = typename BigEnoughPromotion<T, U>::type;
- Promotion presult;
- // Fail if either operand is out of range for the promoted type.
- // TODO(jschuh): This could be made to work for a broader range of values.
- bool is_valid = IsValueInRangeForNumericType<Promotion>(x) &&
- IsValueInRangeForNumericType<Promotion>(y);
-
- if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
- presult = static_cast<Promotion>(x) + static_cast<Promotion>(y);
- } else {
- is_valid &= CheckedAddImpl(static_cast<Promotion>(x),
- static_cast<Promotion>(y), &presult);
+ if (std::numeric_limits<T>::is_signed) {
+ if (HasSignBit(BinaryComplement(
+ static_cast<UnsignedDst>((uresult ^ ux) & (uresult ^ uy))))) {
+ *validity = RANGE_VALID;
+ } else { // Direction of wrap is inverse of result sign.
+ *validity = HasSignBit(uresult) ? RANGE_OVERFLOW : RANGE_UNDERFLOW;
}
- *result = static_cast<V>(presult);
- return is_valid && IsValueInRangeForNumericType<V>(presult);
-#endif
+ } else { // Unsigned is either valid or overflow.
+ *validity = BinaryComplement(x) >= y ? RANGE_VALID : RANGE_OVERFLOW;
}
-};
+ return static_cast<T>(uresult);
+}
template <typename T>
-bool CheckedSubImpl(T x, T y, T* result) {
- static_assert(std::is_integral<T>::value, "Type must be integral");
+typename std::enable_if<std::numeric_limits<T>::is_integer, T>::type
+CheckedSub(T x, T y, RangeConstraint* validity) {
// Since the value of x+y is undefined if we have a signed type, we compute
// it using the unsigned type of the same size.
- using UnsignedDst = typename std::make_unsigned<T>::type;
- using SignedDst = typename std::make_signed<T>::type;
+ typedef typename UnsignedIntegerForSize<T>::type UnsignedDst;
UnsignedDst ux = static_cast<UnsignedDst>(x);
UnsignedDst uy = static_cast<UnsignedDst>(y);
UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy);
- *result = static_cast<T>(uresult);
// Subtraction is valid if either x and y have same sign, or (x-y) and x have
// the same sign.
- return (std::is_signed<T>::value)
- ? static_cast<SignedDst>((uresult ^ ux) & (ux ^ uy)) >= 0
- : x >= y;
-}
-
-template <typename T, typename U, class Enable = void>
-struct CheckedSubOp {};
-
-template <typename T, typename U>
-struct CheckedSubOp<T,
- U,
- typename std::enable_if<std::is_integral<T>::value &&
- std::is_integral<U>::value>::type> {
- using result_type = typename MaxExponentPromotion<T, U>::type;
- template <typename V>
- static bool Do(T x, U y, V* result) {
-#if USE_OVERFLOW_BUILTINS
- return !__builtin_sub_overflow(x, y, result);
-#else
- using Promotion = typename BigEnoughPromotion<T, U>::type;
- Promotion presult;
- // Fail if either operand is out of range for the promoted type.
- // TODO(jschuh): This could be made to work for a broader range of values.
- bool is_valid = IsValueInRangeForNumericType<Promotion>(x) &&
- IsValueInRangeForNumericType<Promotion>(y);
-
- if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
- presult = static_cast<Promotion>(x) - static_cast<Promotion>(y);
- } else {
- is_valid &= CheckedSubImpl(static_cast<Promotion>(x),
- static_cast<Promotion>(y), &presult);
+ if (std::numeric_limits<T>::is_signed) {
+ if (HasSignBit(BinaryComplement(
+ static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy))))) {
+ *validity = RANGE_VALID;
+ } else { // Direction of wrap is inverse of result sign.
+ *validity = HasSignBit(uresult) ? RANGE_OVERFLOW : RANGE_UNDERFLOW;
}
- *result = static_cast<V>(presult);
- return is_valid && IsValueInRangeForNumericType<V>(presult);
-#endif
+ } else { // Unsigned is either valid or underflow.
+ *validity = x >= y ? RANGE_VALID : RANGE_UNDERFLOW;
}
-};
+ return static_cast<T>(uresult);
+}
+// Integer multiplication is a bit complicated. In the fast case we just
+// we just promote to a twice wider type, and range check the result. In the
+// slow case we need to manually check that the result won't be truncated by
+// checking with division against the appropriate bound.
template <typename T>
-bool CheckedMulImpl(T x, T y, T* result) {
- static_assert(std::is_integral<T>::value, "Type must be integral");
- // Since the value of x*y is potentially undefined if we have a signed type,
- // we compute it using the unsigned type of the same size.
- using UnsignedDst = typename std::make_unsigned<T>::type;
- using SignedDst = typename std::make_signed<T>::type;
- const UnsignedDst ux = SafeUnsignedAbs(x);
- const UnsignedDst uy = SafeUnsignedAbs(y);
- UnsignedDst uresult = static_cast<UnsignedDst>(ux * uy);
- const bool is_negative =
- std::is_signed<T>::value && static_cast<SignedDst>(x ^ y) < 0;
- *result = is_negative ? 0 - uresult : uresult;
- // We have a fast out for unsigned identity or zero on the second operand.
- // After that it's an unsigned overflow check on the absolute value, with
- // a +1 bound for a negative result.
- return uy <= UnsignedDst(!std::is_signed<T>::value || is_negative) ||
- ux <= (std::numeric_limits<T>::max() + UnsignedDst(is_negative)) / uy;
+typename std::enable_if<std::numeric_limits<T>::is_integer &&
+ sizeof(T) * 2 <= sizeof(uintmax_t),
+ T>::type
+CheckedMul(T x, T y, RangeConstraint* validity) {
+ typedef typename TwiceWiderInteger<T>::type IntermediateType;
+ IntermediateType tmp =
+ static_cast<IntermediateType>(x) * static_cast<IntermediateType>(y);
+ *validity = DstRangeRelationToSrcRange<T>(tmp);
+ return static_cast<T>(tmp);
}
-template <typename T, typename U, class Enable = void>
-struct CheckedMulOp {};
-
-template <typename T, typename U>
-struct CheckedMulOp<T,
- U,
- typename std::enable_if<std::is_integral<T>::value &&
- std::is_integral<U>::value>::type> {
- using result_type = typename MaxExponentPromotion<T, U>::type;
- template <typename V>
- static bool Do(T x, U y, V* result) {
-#if USE_OVERFLOW_BUILTINS
-#if defined(__clang__)
- // TODO(jschuh): Get the Clang runtime library issues sorted out so we can
- // support full-width, mixed-sign multiply builtins.
- // https://crbug.com/613003
- static const bool kUseMaxInt =
- // Narrower type than uintptr_t is always safe.
- std::numeric_limits<__typeof__(x * y)>::digits <
- std::numeric_limits<intptr_t>::digits ||
- // Safe for intptr_t and uintptr_t if the sign matches.
- (IntegerBitsPlusSign<__typeof__(x * y)>::value ==
- IntegerBitsPlusSign<intptr_t>::value &&
- std::is_signed<T>::value == std::is_signed<U>::value);
-#else
- static const bool kUseMaxInt = true;
-#endif
- if (kUseMaxInt)
- return !__builtin_mul_overflow(x, y, result);
-#endif
- using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;
- Promotion presult;
- // Fail if either operand is out of range for the promoted type.
- // TODO(jschuh): This could be made to work for a broader range of values.
- bool is_valid = IsValueInRangeForNumericType<Promotion>(x) &&
- IsValueInRangeForNumericType<Promotion>(y);
-
- if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
- presult = static_cast<Promotion>(x) * static_cast<Promotion>(y);
- } else {
- is_valid &= CheckedMulImpl(static_cast<Promotion>(x),
- static_cast<Promotion>(y), &presult);
- }
- *result = static_cast<V>(presult);
- return is_valid && IsValueInRangeForNumericType<V>(presult);
+template <typename T>
+typename std::enable_if<std::numeric_limits<T>::is_integer &&
+ std::numeric_limits<T>::is_signed &&
+ (sizeof(T) * 2 > sizeof(uintmax_t)),
+ T>::type
+CheckedMul(T x, T y, RangeConstraint* validity) {
+ // If either side is zero then the result will be zero.
+ if (!x || !y) {
+ *validity = RANGE_VALID;
+ return static_cast<T>(0);
+
+ } else if (x > 0) {
+ if (y > 0)
+ *validity =
+ x <= std::numeric_limits<T>::max() / y ? RANGE_VALID : RANGE_OVERFLOW;
+ else
+ *validity = y >= std::numeric_limits<T>::min() / x ? RANGE_VALID
+ : RANGE_UNDERFLOW;
+
+ } else {
+ if (y > 0)
+ *validity = x >= std::numeric_limits<T>::min() / y ? RANGE_VALID
+ : RANGE_UNDERFLOW;
+ else
+ *validity =
+ y >= std::numeric_limits<T>::max() / x ? RANGE_VALID : RANGE_OVERFLOW;
}
-};
-// Avoid poluting the namespace once we're done with the macro.
-#undef USE_OVERFLOW_BUILTINS
+ return static_cast<T>(x * y);
+}
-// Division just requires a check for a zero denominator or an invalid negation
-// on signed min/-1.
template <typename T>
-bool CheckedDivImpl(T x, T y, T* result) {
- static_assert(std::is_integral<T>::value, "Type must be integral");
- if (y && (!std::is_signed<T>::value ||
- x != std::numeric_limits<T>::lowest() || y != static_cast<T>(-1))) {
- *result = x / y;
- return true;
- }
- return false;
+typename std::enable_if<std::numeric_limits<T>::is_integer &&
+ !std::numeric_limits<T>::is_signed &&
+ (sizeof(T) * 2 > sizeof(uintmax_t)),
+ T>::type
+CheckedMul(T x, T y, RangeConstraint* validity) {
+ *validity = (y == 0 || x <= std::numeric_limits<T>::max() / y)
+ ? RANGE_VALID
+ : RANGE_OVERFLOW;
+ return static_cast<T>(x * y);
}
-template <typename T, typename U, class Enable = void>
-struct CheckedDivOp {};
-
-template <typename T, typename U>
-struct CheckedDivOp<T,
- U,
- typename std::enable_if<std::is_integral<T>::value &&
- std::is_integral<U>::value>::type> {
- using result_type = typename MaxExponentPromotion<T, U>::type;
- template <typename V>
- static bool Do(T x, U y, V* result) {
- using Promotion = typename BigEnoughPromotion<T, U>::type;
- Promotion presult;
- // Fail if either operand is out of range for the promoted type.
- // TODO(jschuh): This could be made to work for a broader range of values.
- bool is_valid = IsValueInRangeForNumericType<Promotion>(x) &&
- IsValueInRangeForNumericType<Promotion>(y);
- is_valid &= CheckedDivImpl(static_cast<Promotion>(x),
- static_cast<Promotion>(y), &presult);
- *result = static_cast<V>(presult);
- return is_valid && IsValueInRangeForNumericType<V>(presult);
+// Division just requires a check for an invalid negation on signed min/-1.
+template <typename T>
+T CheckedDiv(T x,
+ T y,
+ RangeConstraint* validity,
+ typename std::enable_if<std::numeric_limits<T>::is_integer,
+ int>::type = 0) {
+ if (std::numeric_limits<T>::is_signed && x == std::numeric_limits<T>::min() &&
+ y == static_cast<T>(-1)) {
+ *validity = RANGE_OVERFLOW;
+ return std::numeric_limits<T>::min();
}
-};
+
+ *validity = RANGE_VALID;
+ return static_cast<T>(x / y);
+}
template <typename T>
-bool CheckedModImpl(T x, T y, T* result) {
- static_assert(std::is_integral<T>::value, "Type must be integral");
- if (y > 0) {
- *result = static_cast<T>(x % y);
- return true;
- }
- return false;
+typename std::enable_if<std::numeric_limits<T>::is_integer &&
+ std::numeric_limits<T>::is_signed,
+ T>::type
+CheckedMod(T x, T y, RangeConstraint* validity) {
+ *validity = y > 0 ? RANGE_VALID : RANGE_INVALID;
+ return static_cast<T>(x % y);
}
-template <typename T, typename U, class Enable = void>
-struct CheckedModOp {};
-
-template <typename T, typename U>
-struct CheckedModOp<T,
- U,
- typename std::enable_if<std::is_integral<T>::value &&
- std::is_integral<U>::value>::type> {
- using result_type = typename MaxExponentPromotion<T, U>::type;
- template <typename V>
- static bool Do(T x, U y, V* result) {
- using Promotion = typename BigEnoughPromotion<T, U>::type;
- Promotion presult;
- bool is_valid = CheckedModImpl(static_cast<Promotion>(x),
- static_cast<Promotion>(y), &presult);
- *result = static_cast<V>(presult);
- return is_valid && IsValueInRangeForNumericType<V>(presult);
- }
-};
+template <typename T>
+typename std::enable_if<std::numeric_limits<T>::is_integer &&
+ !std::numeric_limits<T>::is_signed,
+ T>::type
+CheckedMod(T x, T y, RangeConstraint* validity) {
+ *validity = RANGE_VALID;
+ return static_cast<T>(x % y);
+}
-template <typename T, typename U, class Enable = void>
-struct CheckedLshOp {};
-
-// Left shift. Shifts less than 0 or greater than or equal to the number
-// of bits in the promoted type are undefined. Shifts of negative values
-// are undefined. Otherwise it is defined when the result fits.
-template <typename T, typename U>
-struct CheckedLshOp<T,
- U,
- typename std::enable_if<std::is_integral<T>::value &&
- std::is_integral<U>::value>::type> {
- using result_type = T;
- template <typename V>
- static bool Do(T x, U shift, V* result) {
- using ShiftType = typename std::make_unsigned<T>::type;
- static const ShiftType kBitWidth = IntegerBitsPlusSign<T>::value;
- const ShiftType real_shift = static_cast<ShiftType>(shift);
- // Signed shift is not legal on negative values.
- if (!IsValueNegative(x) && real_shift < kBitWidth) {
- // Just use a multiplication because it's easy.
- // TODO(jschuh): This could probably be made more efficient.
- if (!std::is_signed<T>::value || real_shift != kBitWidth - 1)
- return CheckedMulOp<T, T>::Do(x, static_cast<T>(1) << shift, result);
- return !x; // Special case zero for a full width signed shift.
- }
- return false;
- }
-};
+template <typename T>
+typename std::enable_if<std::numeric_limits<T>::is_integer &&
+ std::numeric_limits<T>::is_signed,
+ T>::type
+CheckedNeg(T value, RangeConstraint* validity) {
+ *validity =
+ value != std::numeric_limits<T>::min() ? RANGE_VALID : RANGE_OVERFLOW;
+ // The negation of signed min is min, so catch that one.
+ return static_cast<T>(-value);
+}
-template <typename T, typename U, class Enable = void>
-struct CheckedRshOp {};
-
-// Right shift. Shifts less than 0 or greater than or equal to the number
-// of bits in the promoted type are undefined. Otherwise, it is always defined,
-// but a right shift of a negative value is implementation-dependent.
-template <typename T, typename U>
-struct CheckedRshOp<T,
- U,
- typename std::enable_if<std::is_integral<T>::value &&
- std::is_integral<U>::value>::type> {
- using result_type = T;
- template <typename V = result_type>
- static bool Do(T x, U shift, V* result) {
- // Use the type conversion push negative values out of range.
- using ShiftType = typename std::make_unsigned<T>::type;
- if (static_cast<ShiftType>(shift) < IntegerBitsPlusSign<T>::value) {
- T tmp = x >> shift;
- *result = static_cast<V>(tmp);
- return IsValueInRangeForNumericType<V>(tmp);
- }
- return false;
- }
-};
+template <typename T>
+typename std::enable_if<std::numeric_limits<T>::is_integer &&
+ !std::numeric_limits<T>::is_signed,
+ T>::type
+CheckedNeg(T value, RangeConstraint* validity) {
+ // The only legal unsigned negation is zero.
+ *validity = value ? RANGE_UNDERFLOW : RANGE_VALID;
+ return static_cast<T>(
+ -static_cast<typename SignedIntegerForSize<T>::type>(value));
+}
-template <typename T, typename U, class Enable = void>
-struct CheckedAndOp {};
-
-// For simplicity we support only unsigned integer results.
-template <typename T, typename U>
-struct CheckedAndOp<T,
- U,
- typename std::enable_if<std::is_integral<T>::value &&
- std::is_integral<U>::value>::type> {
- using result_type = typename std::make_unsigned<
- typename MaxExponentPromotion<T, U>::type>::type;
- template <typename V = result_type>
- static bool Do(T x, U y, V* result) {
- result_type tmp = static_cast<result_type>(x) & static_cast<result_type>(y);
- *result = static_cast<V>(tmp);
- return IsValueInRangeForNumericType<V>(tmp);
- }
-};
+template <typename T>
+typename std::enable_if<std::numeric_limits<T>::is_integer &&
+ std::numeric_limits<T>::is_signed,
+ T>::type
+CheckedAbs(T value, RangeConstraint* validity) {
+ *validity =
+ value != std::numeric_limits<T>::min() ? RANGE_VALID : RANGE_OVERFLOW;
+ return static_cast<T>(std::abs(value));
+}
-template <typename T, typename U, class Enable = void>
-struct CheckedOrOp {};
-
-// For simplicity we support only unsigned integers.
-template <typename T, typename U>
-struct CheckedOrOp<T,
- U,
- typename std::enable_if<std::is_integral<T>::value &&
- std::is_integral<U>::value>::type> {
- using result_type = typename std::make_unsigned<
- typename MaxExponentPromotion<T, U>::type>::type;
- template <typename V = result_type>
- static bool Do(T x, U y, V* result) {
- result_type tmp = static_cast<result_type>(x) | static_cast<result_type>(y);
- *result = static_cast<V>(tmp);
- return IsValueInRangeForNumericType<V>(tmp);
- }
-};
+template <typename T>
+typename std::enable_if<std::numeric_limits<T>::is_integer &&
+ !std::numeric_limits<T>::is_signed,
+ T>::type
+CheckedAbs(T value, RangeConstraint* validity) {
+ // T is unsigned, so |value| must already be positive.
+ *validity = RANGE_VALID;
+ return value;
+}
-template <typename T, typename U, class Enable = void>
-struct CheckedXorOp {};
-
-// For simplicity we support only unsigned integers.
-template <typename T, typename U>
-struct CheckedXorOp<T,
- U,
- typename std::enable_if<std::is_integral<T>::value &&
- std::is_integral<U>::value>::type> {
- using result_type = typename std::make_unsigned<
- typename MaxExponentPromotion<T, U>::type>::type;
- template <typename V = result_type>
- static bool Do(T x, U y, V* result) {
- result_type tmp = static_cast<result_type>(x) ^ static_cast<result_type>(y);
- *result = static_cast<V>(tmp);
- return IsValueInRangeForNumericType<V>(tmp);
- }
-};
+template <typename T>
+typename std::enable_if<std::numeric_limits<T>::is_integer &&
+ std::numeric_limits<T>::is_signed,
+ typename UnsignedIntegerForSize<T>::type>::type
+CheckedUnsignedAbs(T value) {
+ typedef typename UnsignedIntegerForSize<T>::type UnsignedT;
+ return value == std::numeric_limits<T>::min()
+ ? static_cast<UnsignedT>(std::numeric_limits<T>::max()) + 1
+ : static_cast<UnsignedT>(std::abs(value));
+}
-// Max doesn't really need to be implemented this way because it can't fail,
-// but it makes the code much cleaner to use the MathOp wrappers.
-template <typename T, typename U, class Enable = void>
-struct CheckedMaxOp {};
-
-template <typename T, typename U>
-struct CheckedMaxOp<
- T,
- U,
- typename std::enable_if<std::is_arithmetic<T>::value &&
- std::is_arithmetic<U>::value>::type> {
- using result_type = typename MaxExponentPromotion<T, U>::type;
- template <typename V = result_type>
- static bool Do(T x, U y, V* result) {
- *result = IsGreater<T, U>::Test(x, y) ? static_cast<result_type>(x)
- : static_cast<result_type>(y);
- return true;
- }
-};
+template <typename T>
+typename std::enable_if<std::numeric_limits<T>::is_integer &&
+ !std::numeric_limits<T>::is_signed,
+ T>::type
+CheckedUnsignedAbs(T value) {
+ // T is unsigned, so |value| must already be positive.
+ return static_cast<T>(value);
+}
-// Min doesn't really need to be implemented this way because it can't fail,
-// but it makes the code much cleaner to use the MathOp wrappers.
-template <typename T, typename U, class Enable = void>
-struct CheckedMinOp {};
-
-template <typename T, typename U>
-struct CheckedMinOp<
- T,
- U,
- typename std::enable_if<std::is_arithmetic<T>::value &&
- std::is_arithmetic<U>::value>::type> {
- using result_type = typename LowestValuePromotion<T, U>::type;
- template <typename V = result_type>
- static bool Do(T x, U y, V* result) {
- *result = IsLess<T, U>::Test(x, y) ? static_cast<result_type>(x)
- : static_cast<result_type>(y);
- return true;
+// These are the floating point stubs that the compiler needs to see. Only the
+// negation operation is ever called.
+#define BASE_FLOAT_ARITHMETIC_STUBS(NAME) \
+ template <typename T> \
+ typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type \
+ Checked##NAME(T, T, RangeConstraint*) { \
+ NOTREACHED(); \
+ return static_cast<T>(0); \
}
-};
-// This is just boilerplate that wraps the standard floating point arithmetic.
-// A macro isn't the nicest solution, but it beats rewriting these repeatedly.
-#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \
- template <typename T, typename U> \
- struct Checked##NAME##Op< \
- T, U, typename std::enable_if<std::is_floating_point<T>::value || \
- std::is_floating_point<U>::value>::type> { \
- using result_type = typename MaxExponentPromotion<T, U>::type; \
- template <typename V> \
- static bool Do(T x, U y, V* result) { \
- using Promotion = typename MaxExponentPromotion<T, U>::type; \
- Promotion presult = x OP y; \
- *result = static_cast<V>(presult); \
- return IsValueInRangeForNumericType<V>(presult); \
- } \
- };
-
-BASE_FLOAT_ARITHMETIC_OPS(Add, +)
-BASE_FLOAT_ARITHMETIC_OPS(Sub, -)
-BASE_FLOAT_ARITHMETIC_OPS(Mul, *)
-BASE_FLOAT_ARITHMETIC_OPS(Div, /)
-
-#undef BASE_FLOAT_ARITHMETIC_OPS
-
-// Wrap the unary operations to allow SFINAE when instantiating integrals versus
-// floating points. These don't perform any overflow checking. Rather, they
-// exhibit well-defined overflow semantics and rely on the caller to detect
-// if an overflow occured.
-
-template <typename T,
- typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
-constexpr T NegateWrapper(T value) {
- using UnsignedT = typename std::make_unsigned<T>::type;
- // This will compile to a NEG on Intel, and is normal negation on ARM.
- return static_cast<T>(UnsignedT(0) - static_cast<UnsignedT>(value));
-}
-
-template <
- typename T,
- typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
-constexpr T NegateWrapper(T value) {
- return -value;
-}
+BASE_FLOAT_ARITHMETIC_STUBS(Add)
+BASE_FLOAT_ARITHMETIC_STUBS(Sub)
+BASE_FLOAT_ARITHMETIC_STUBS(Mul)
+BASE_FLOAT_ARITHMETIC_STUBS(Div)
+BASE_FLOAT_ARITHMETIC_STUBS(Mod)
-template <typename T,
- typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
-constexpr typename std::make_unsigned<T>::type InvertWrapper(T value) {
- return ~value;
-}
+#undef BASE_FLOAT_ARITHMETIC_STUBS
-template <typename T,
- typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
-constexpr T AbsWrapper(T value) {
- return static_cast<T>(SafeUnsignedAbs(value));
+template <typename T>
+typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedNeg(
+ T value,
+ RangeConstraint*) {
+ return static_cast<T>(-value);
}
-template <
- typename T,
- typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
-constexpr T AbsWrapper(T value) {
- return value < 0 ? -value : value;
+template <typename T>
+typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedAbs(
+ T value,
+ RangeConstraint*) {
+ return static_cast<T>(std::abs(value));
}
// Floats carry around their validity state with them, but integers do not. So,
@@ -517,10 +379,10 @@ enum NumericRepresentation {
template <typename NumericType>
struct GetNumericRepresentation {
static const NumericRepresentation value =
- std::is_integral<NumericType>::value
+ std::numeric_limits<NumericType>::is_integer
? NUMERIC_INTEGER
- : (std::is_floating_point<NumericType>::value ? NUMERIC_FLOATING
- : NUMERIC_UNKNOWN);
+ : (std::numeric_limits<NumericType>::is_iec559 ? NUMERIC_FLOATING
+ : NUMERIC_UNKNOWN);
};
template <typename T, NumericRepresentation type =
@@ -531,48 +393,41 @@ class CheckedNumericState {};
template <typename T>
class CheckedNumericState<T, NUMERIC_INTEGER> {
private:
- // is_valid_ precedes value_ because member intializers in the constructors
- // are evaluated in field order, and is_valid_ must be read when initializing
- // value_.
- bool is_valid_;
T value_;
-
- // Ensures that a type conversion does not trigger undefined behavior.
- template <typename Src>
- static constexpr T WellDefinedConversionOrZero(const Src value,
- const bool is_valid) {
- using SrcType = typename internal::UnderlyingType<Src>::type;
- return (std::is_integral<SrcType>::value || is_valid)
- ? static_cast<T>(value)
- : static_cast<T>(0);
- }
+ RangeConstraint validity_ : CHAR_BIT; // Actually requires only two bits.
public:
template <typename Src, NumericRepresentation type>
friend class CheckedNumericState;
- constexpr CheckedNumericState() : is_valid_(true), value_(0) {}
+ CheckedNumericState() : value_(0), validity_(RANGE_VALID) {}
template <typename Src>
- constexpr CheckedNumericState(Src value, bool is_valid)
- : is_valid_(is_valid && IsValueInRangeForNumericType<T>(value)),
- value_(WellDefinedConversionOrZero(value, is_valid_)) {
- static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
+ CheckedNumericState(Src value, RangeConstraint validity)
+ : value_(static_cast<T>(value)),
+ validity_(GetRangeConstraint(validity |
+ DstRangeRelationToSrcRange<T>(value))) {
+ static_assert(std::numeric_limits<Src>::is_specialized,
+ "Argument must be numeric.");
}
// Copy constructor.
template <typename Src>
- constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs)
- : is_valid_(rhs.IsValid()),
- value_(WellDefinedConversionOrZero(rhs.value(), is_valid_)) {}
+ CheckedNumericState(const CheckedNumericState<Src>& rhs)
+ : value_(static_cast<T>(rhs.value())),
+ validity_(GetRangeConstraint(
+ rhs.validity() | DstRangeRelationToSrcRange<T>(rhs.value()))) {}
template <typename Src>
- constexpr explicit CheckedNumericState(Src value)
- : is_valid_(IsValueInRangeForNumericType<T>(value)),
- value_(WellDefinedConversionOrZero(value, is_valid_)) {}
-
- constexpr bool is_valid() const { return is_valid_; }
- constexpr T value() const { return value_; }
+ explicit CheckedNumericState(
+ Src value,
+ typename std::enable_if<std::numeric_limits<Src>::is_specialized,
+ int>::type = 0)
+ : value_(static_cast<T>(value)),
+ validity_(DstRangeRelationToSrcRange<T>(value)) {}
+
+ RangeConstraint validity() const { return validity_; }
+ T value() const { return value_; }
};
// Floating points maintain their own validity, but need translation wrappers.
@@ -581,58 +436,94 @@ class CheckedNumericState<T, NUMERIC_FLOATING> {
private:
T value_;
- // Ensures that a type conversion does not trigger undefined behavior.
- template <typename Src>
- static constexpr T WellDefinedConversionOrNaN(const Src value,
- const bool is_valid) {
- using SrcType = typename internal::UnderlyingType<Src>::type;
- return (StaticDstRangeRelationToSrcRange<T, SrcType>::value ==
- NUMERIC_RANGE_CONTAINED ||
- is_valid)
- ? static_cast<T>(value)
- : std::numeric_limits<T>::quiet_NaN();
- }
-
public:
template <typename Src, NumericRepresentation type>
friend class CheckedNumericState;
- constexpr CheckedNumericState() : value_(0.0) {}
+ CheckedNumericState() : value_(0.0) {}
template <typename Src>
- constexpr CheckedNumericState(Src value, bool is_valid)
- : value_(WellDefinedConversionOrNaN(value, is_valid)) {}
+ CheckedNumericState(
+ Src value,
+ RangeConstraint /*validity*/,
+ typename std::enable_if<std::numeric_limits<Src>::is_integer, int>::type =
+ 0) {
+ switch (DstRangeRelationToSrcRange<T>(value)) {
+ case RANGE_VALID:
+ value_ = static_cast<T>(value);
+ break;
+
+ case RANGE_UNDERFLOW:
+ value_ = -std::numeric_limits<T>::infinity();
+ break;
+
+ case RANGE_OVERFLOW:
+ value_ = std::numeric_limits<T>::infinity();
+ break;
+
+ case RANGE_INVALID:
+ value_ = std::numeric_limits<T>::quiet_NaN();
+ break;
+
+ default:
+ NOTREACHED();
+ }
+ }
template <typename Src>
- constexpr explicit CheckedNumericState(Src value)
- : value_(WellDefinedConversionOrNaN(
- value,
- IsValueInRangeForNumericType<T>(value))) {}
+ explicit CheckedNumericState(
+ Src value,
+ typename std::enable_if<std::numeric_limits<Src>::is_specialized,
+ int>::type = 0)
+ : value_(static_cast<T>(value)) {}
// Copy constructor.
template <typename Src>
- constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs)
- : value_(WellDefinedConversionOrNaN(
- rhs.value(),
- rhs.is_valid() && IsValueInRangeForNumericType<T>(rhs.value()))) {}
-
- constexpr bool is_valid() const {
- // Written this way because std::isfinite is not reliably constexpr.
- // TODO(jschuh): Fix this if the libraries ever get fixed.
- return value_ <= std::numeric_limits<T>::max() &&
- value_ >= std::numeric_limits<T>::lowest();
+ CheckedNumericState(const CheckedNumericState<Src>& rhs)
+ : value_(static_cast<T>(rhs.value())) {}
+
+ RangeConstraint validity() const {
+ return GetRangeConstraint(value_ <= std::numeric_limits<T>::max(),
+ value_ >= -std::numeric_limits<T>::max());
}
- constexpr T value() const { return value_; }
+ T value() const { return value_; }
+};
+
+// For integers less than 128-bit and floats 32-bit or larger, we have the type
+// with the larger maximum exponent take precedence.
+enum ArithmeticPromotionCategory { LEFT_PROMOTION, RIGHT_PROMOTION };
+
+template <typename Lhs,
+ typename Rhs = Lhs,
+ ArithmeticPromotionCategory Promotion =
+ (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
+ ? LEFT_PROMOTION
+ : RIGHT_PROMOTION>
+struct ArithmeticPromotion;
+
+template <typename Lhs, typename Rhs>
+struct ArithmeticPromotion<Lhs, Rhs, LEFT_PROMOTION> {
+ typedef Lhs type;
+};
+
+template <typename Lhs, typename Rhs>
+struct ArithmeticPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
+ typedef Rhs type;
};
-template <template <typename, typename, typename> class M,
- typename L,
- typename R>
-struct MathWrapper {
- using math = M<typename UnderlyingType<L>::type,
- typename UnderlyingType<R>::type,
- void>;
- using type = typename math::result_type;
+// We can statically check if operations on the provided types can wrap, so we
+// can skip the checked operations if they're not needed. So, for an integer we
+// care if the destination type preserves the sign and is twice the width of
+// the source.
+template <typename T, typename Lhs, typename Rhs>
+struct IsIntegerArithmeticSafe {
+ static const bool value = !std::numeric_limits<T>::is_iec559 &&
+ StaticDstRangeRelationToSrcRange<T, Lhs>::value ==
+ NUMERIC_RANGE_CONTAINED &&
+ sizeof(T) >= (2 * sizeof(Lhs)) &&
+ StaticDstRangeRelationToSrcRange<T, Rhs>::value !=
+ NUMERIC_RANGE_CONTAINED &&
+ sizeof(T) >= (2 * sizeof(Rhs));
};
} // namespace internal
diff --git a/base/numerics/safe_numerics_unittest.cc b/base/numerics/safe_numerics_unittest.cc
index ec6d0037c9..4be7ab59d7 100644
--- a/base/numerics/safe_numerics_unittest.cc
+++ b/base/numerics/safe_numerics_unittest.cc
@@ -9,10 +9,8 @@
#include <type_traits>
#include "base/compiler_specific.h"
-#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "base/numerics/safe_math.h"
-#include "base/test/gtest_util.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -22,44 +20,33 @@
using std::numeric_limits;
using base::CheckedNumeric;
-using base::IsValidForType;
-using base::ValueOrDieForType;
-using base::ValueOrDefaultForType;
-using base::MakeCheckedNum;
-using base::CheckMax;
-using base::CheckMin;
-using base::CheckAdd;
-using base::CheckSub;
-using base::CheckMul;
-using base::CheckDiv;
-using base::CheckMod;
-using base::CheckLsh;
-using base::CheckRsh;
using base::checked_cast;
using base::IsValueInRangeForNumericType;
using base::IsValueNegative;
using base::SizeT;
using base::StrictNumeric;
-using base::MakeStrictNum;
using base::saturated_cast;
using base::strict_cast;
using base::internal::MaxExponent;
-using base::internal::IntegerBitsPlusSign;
-using base::internal::RangeCheck;
-
-// These tests deliberately cause arithmetic boundary errors. If the compiler is
-// aggressive enough, it can const detect these errors, so we disable warnings.
+using base::internal::RANGE_VALID;
+using base::internal::RANGE_INVALID;
+using base::internal::RANGE_OVERFLOW;
+using base::internal::RANGE_UNDERFLOW;
+using base::internal::SignedIntegerForSize;
+
+// These tests deliberately cause arithmetic overflows. If the compiler is
+// aggressive enough, it can const fold these overflows. Disable warnings about
+// overflows for const expressions.
#if defined(OS_WIN)
-#pragma warning(disable : 4756) // Arithmetic overflow.
-#pragma warning(disable : 4293) // Invalid shift.
+#pragma warning(disable:4756)
#endif
// This is a helper function for finding the maximum value in Src that can be
// wholy represented as the destination floating-point type.
template <typename Dst, typename Src>
Dst GetMaxConvertibleToFloat() {
- using DstLimits = numeric_limits<Dst>;
- using SrcLimits = numeric_limits<Src>;
+ typedef numeric_limits<Dst> DstLimits;
+ typedef numeric_limits<Src> SrcLimits;
static_assert(SrcLimits::is_specialized, "Source must be numeric.");
static_assert(DstLimits::is_specialized, "Destination must be numeric.");
CHECK(DstLimits::is_iec559);
@@ -74,113 +61,20 @@ Dst GetMaxConvertibleToFloat() {
return static_cast<Dst>(max);
}
-namespace base {
-namespace internal {
-
-// Test corner case promotions used
-static_assert(IsIntegerArithmeticSafe<int32_t, int8_t, int8_t>::value, "");
-static_assert(IsIntegerArithmeticSafe<int32_t, int16_t, int8_t>::value, "");
-static_assert(IsIntegerArithmeticSafe<int32_t, int8_t, int16_t>::value, "");
-static_assert(!IsIntegerArithmeticSafe<int32_t, int32_t, int8_t>::value, "");
-static_assert(BigEnoughPromotion<int16_t, int8_t>::is_contained, "");
-static_assert(BigEnoughPromotion<int32_t, uint32_t>::is_contained, "");
-static_assert(BigEnoughPromotion<intmax_t, int8_t>::is_contained, "");
-static_assert(!BigEnoughPromotion<uintmax_t, int8_t>::is_contained, "");
-static_assert(
- std::is_same<BigEnoughPromotion<int16_t, int8_t>::type, int16_t>::value,
- "");
-static_assert(
- std::is_same<BigEnoughPromotion<int32_t, uint32_t>::type, int64_t>::value,
- "");
-static_assert(
- std::is_same<BigEnoughPromotion<intmax_t, int8_t>::type, intmax_t>::value,
- "");
-static_assert(
- std::is_same<BigEnoughPromotion<uintmax_t, int8_t>::type, uintmax_t>::value,
- "");
-static_assert(BigEnoughPromotion<int16_t, int8_t>::is_contained, "");
-static_assert(BigEnoughPromotion<int32_t, uint32_t>::is_contained, "");
-static_assert(BigEnoughPromotion<intmax_t, int8_t>::is_contained, "");
-static_assert(!BigEnoughPromotion<uintmax_t, int8_t>::is_contained, "");
-static_assert(
- std::is_same<FastIntegerArithmeticPromotion<int16_t, int8_t>::type,
- int32_t>::value,
- "");
-static_assert(
- std::is_same<FastIntegerArithmeticPromotion<int32_t, uint32_t>::type,
- int64_t>::value,
- "");
-static_assert(
- std::is_same<FastIntegerArithmeticPromotion<intmax_t, int8_t>::type,
- intmax_t>::value,
- "");
-static_assert(
- std::is_same<FastIntegerArithmeticPromotion<uintmax_t, int8_t>::type,
- uintmax_t>::value,
- "");
-static_assert(FastIntegerArithmeticPromotion<int16_t, int8_t>::is_contained,
- "");
-static_assert(FastIntegerArithmeticPromotion<int32_t, uint32_t>::is_contained,
- "");
-static_assert(!FastIntegerArithmeticPromotion<intmax_t, int8_t>::is_contained,
- "");
-static_assert(!FastIntegerArithmeticPromotion<uintmax_t, int8_t>::is_contained,
- "");
-
-template <typename U>
-U GetNumericValueForTest(const CheckedNumeric<U>& src) {
- return src.state_.value();
-}
-} // namespace internal.
-} // namespace base.
-
-using base::internal::GetNumericValueForTest;
-
-// Logs the ValueOrDie() failure instead of crashing.
-struct LogOnFailure {
- template <typename T>
- static T HandleFailure() {
- LOG(WARNING) << "ValueOrDie() failed unexpectedly.";
- return T();
- }
-};
-
// Helper macros to wrap displaying the conversion types and line numbers.
#define TEST_EXPECTED_VALIDITY(expected, actual) \
- EXPECT_EQ(expected, (actual).template Cast<Dst>().IsValid()) \
- << "Result test: Value " << GetNumericValueForTest(actual) << " as " \
- << dst << " on line " << line
+ EXPECT_EQ(expected, CheckedNumeric<Dst>(actual).IsValid()) \
+ << "Result test: Value " << +(actual).ValueUnsafe() << " as " << dst \
+ << " on line " << line;
#define TEST_EXPECTED_SUCCESS(actual) TEST_EXPECTED_VALIDITY(true, actual)
#define TEST_EXPECTED_FAILURE(actual) TEST_EXPECTED_VALIDITY(false, actual)
-// We have to handle promotions, so infer the underlying type below from actual.
-#define TEST_EXPECTED_VALUE(expected, actual) \
- EXPECT_EQ(static_cast<typename std::decay<decltype(actual)>::type::type>( \
- expected), \
- ((actual) \
- .template ValueOrDie< \
- typename std::decay<decltype(actual)>::type::type, \
- LogOnFailure>())) \
- << "Result test: Value " << GetNumericValueForTest(actual) << " as " \
- << dst << " on line " << line
-
-// Test the simple pointer arithmetic overrides.
-template <typename Dst>
-void TestStrictPointerMath() {
- Dst dummy_value = 0;
- Dst* dummy_ptr = &dummy_value;
- static const Dst kDummyOffset = 2; // Don't want to go too far.
- EXPECT_EQ(dummy_ptr + kDummyOffset,
- dummy_ptr + StrictNumeric<Dst>(kDummyOffset));
- EXPECT_EQ(dummy_ptr - kDummyOffset,
- dummy_ptr - StrictNumeric<Dst>(kDummyOffset));
- EXPECT_NE(dummy_ptr, dummy_ptr + StrictNumeric<Dst>(kDummyOffset));
- EXPECT_NE(dummy_ptr, dummy_ptr - StrictNumeric<Dst>(kDummyOffset));
- EXPECT_DEATH_IF_SUPPORTED(
- dummy_ptr + StrictNumeric<size_t>(std::numeric_limits<size_t>::max()),
- "");
-}
+#define TEST_EXPECTED_VALUE(expected, actual) \
+ EXPECT_EQ(static_cast<Dst>(expected), \
+ CheckedNumeric<Dst>(actual).ValueUnsafe()) \
+ << "Result test: Value " << +((actual).ValueUnsafe()) << " as " << dst \
+ << " on line " << line;
// Signed integer arithmetic.
template <typename Dst>
@@ -190,52 +84,34 @@ static void TestSpecializedArithmetic(
typename std::enable_if<numeric_limits<Dst>::is_integer &&
numeric_limits<Dst>::is_signed,
int>::type = 0) {
- using DstLimits = numeric_limits<Dst>;
- TEST_EXPECTED_FAILURE(-CheckedNumeric<Dst>(DstLimits::lowest()));
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()).Abs());
+ typedef numeric_limits<Dst> DstLimits;
+ TEST_EXPECTED_FAILURE(-CheckedNumeric<Dst>(DstLimits::min()));
+ TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()).Abs());
TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(-1).Abs());
- TEST_EXPECTED_VALUE(DstLimits::max(),
- MakeCheckedNum(-DstLimits::max()).Abs());
TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::max()) + -1);
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) + -1);
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) +
- DstLimits::lowest());
+ TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()) + -1);
+ TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(-DstLimits::max()) +
+ -DstLimits::max());
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) - 1);
- TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::lowest()) - -1);
+ TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()) - 1);
+ TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()) - -1);
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) -
- DstLimits::lowest());
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) -
+ -DstLimits::max());
+ TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(-DstLimits::max()) -
DstLimits::max());
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) * 2);
+ TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()) * 2);
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) / -1);
+ TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()) / -1);
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(-1) / 2);
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) * -1);
- TEST_EXPECTED_VALUE(DstLimits::max(),
- CheckedNumeric<Dst>(DstLimits::lowest() + 1) * Dst(-1));
- TEST_EXPECTED_VALUE(DstLimits::max(),
- CheckedNumeric<Dst>(-1) * Dst(DstLimits::lowest() + 1));
- TEST_EXPECTED_VALUE(DstLimits::lowest(),
- CheckedNumeric<Dst>(DstLimits::lowest()) * Dst(1));
- TEST_EXPECTED_VALUE(DstLimits::lowest(),
- CheckedNumeric<Dst>(1) * Dst(DstLimits::lowest()));
- TEST_EXPECTED_VALUE(DstLimits::lowest(),
- MakeCheckedNum(DstLimits::lowest()).UnsignedAbs());
- TEST_EXPECTED_VALUE(DstLimits::max(),
- MakeCheckedNum(DstLimits::max()).UnsignedAbs());
- TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(0).UnsignedAbs());
- TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1).UnsignedAbs());
- TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(-1).UnsignedAbs());
// Modulus is legal only for integers.
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>() % 1);
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1);
TEST_EXPECTED_VALUE(-1, CheckedNumeric<Dst>(-1) % 2);
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(-1) % -2);
- TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::lowest()) % 2);
+ TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::min()) % 2);
TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(DstLimits::max()) % 2);
// Test all the different modulus combinations.
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % CheckedNumeric<Dst>(1));
@@ -243,30 +119,6 @@ static void TestSpecializedArithmetic(
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1);
CheckedNumeric<Dst> checked_dst = 1;
TEST_EXPECTED_VALUE(0, checked_dst %= 1);
- // Test that div by 0 is avoided but returns invalid result.
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) % 0);
- // Test bit shifts.
- volatile Dst negative_one = -1;
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) << negative_one);
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1)
- << (IntegerBitsPlusSign<Dst>::value - 1));
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(0)
- << IntegerBitsPlusSign<Dst>::value);
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) << 1);
- TEST_EXPECTED_VALUE(
- static_cast<Dst>(1) << (IntegerBitsPlusSign<Dst>::value - 2),
- CheckedNumeric<Dst>(1) << (IntegerBitsPlusSign<Dst>::value - 2));
- TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(0)
- << (IntegerBitsPlusSign<Dst>::value - 1));
- TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) << 0);
- TEST_EXPECTED_VALUE(2, CheckedNumeric<Dst>(1) << 1);
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) >>
- IntegerBitsPlusSign<Dst>::value);
- TEST_EXPECTED_VALUE(
- 0, CheckedNumeric<Dst>(1) >> (IntegerBitsPlusSign<Dst>::value - 1));
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) >> negative_one);
-
- TestStrictPointerMath<Dst>();
}
// Unsigned integer arithmetic.
@@ -277,30 +129,24 @@ static void TestSpecializedArithmetic(
typename std::enable_if<numeric_limits<Dst>::is_integer &&
!numeric_limits<Dst>::is_signed,
int>::type = 0) {
- using DstLimits = numeric_limits<Dst>;
- TEST_EXPECTED_SUCCESS(-CheckedNumeric<Dst>(DstLimits::lowest()));
- TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::lowest()).Abs());
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) + -1);
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) - 1);
- TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::lowest()) * 2);
+ typedef numeric_limits<Dst> DstLimits;
+ TEST_EXPECTED_SUCCESS(-CheckedNumeric<Dst>(DstLimits::min()));
+ TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()).Abs());
+ TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()) + -1);
+ TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()) - 1);
+ TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::min()) * 2);
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) / 2);
- TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::lowest()).UnsignedAbs());
+ TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()).UnsignedAbs());
TEST_EXPECTED_SUCCESS(
- CheckedNumeric<typename std::make_signed<Dst>::type>(
- std::numeric_limits<typename std::make_signed<Dst>::type>::lowest())
+ CheckedNumeric<typename SignedIntegerForSize<Dst>::type>(
+ std::numeric_limits<typename SignedIntegerForSize<Dst>::type>::min())
.UnsignedAbs());
- TEST_EXPECTED_VALUE(DstLimits::lowest(),
- MakeCheckedNum(DstLimits::lowest()).UnsignedAbs());
- TEST_EXPECTED_VALUE(DstLimits::max(),
- MakeCheckedNum(DstLimits::max()).UnsignedAbs());
- TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(0).UnsignedAbs());
- TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1).UnsignedAbs());
// Modulus is legal only for integers.
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>() % 1);
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1);
TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) % 2);
- TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::lowest()) % 2);
+ TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::min()) % 2);
TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(DstLimits::max()) % 2);
// Test all the different modulus combinations.
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % CheckedNumeric<Dst>(1));
@@ -308,49 +154,6 @@ static void TestSpecializedArithmetic(
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1);
CheckedNumeric<Dst> checked_dst = 1;
TEST_EXPECTED_VALUE(0, checked_dst %= 1);
- // Test that div by 0 is avoided but returns invalid result.
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) % 0);
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1)
- << IntegerBitsPlusSign<Dst>::value);
- // Test bit shifts.
- volatile int negative_one = -1;
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) << negative_one);
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1)
- << IntegerBitsPlusSign<Dst>::value);
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(0)
- << IntegerBitsPlusSign<Dst>::value);
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) << 1);
- TEST_EXPECTED_VALUE(
- static_cast<Dst>(1) << (IntegerBitsPlusSign<Dst>::value - 1),
- CheckedNumeric<Dst>(1) << (IntegerBitsPlusSign<Dst>::value - 1));
- TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) << 0);
- TEST_EXPECTED_VALUE(2, CheckedNumeric<Dst>(1) << 1);
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) >>
- IntegerBitsPlusSign<Dst>::value);
- TEST_EXPECTED_VALUE(
- 0, CheckedNumeric<Dst>(1) >> (IntegerBitsPlusSign<Dst>::value - 1));
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) >> negative_one);
- TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) & 1);
- TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) & 0);
- TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(0) & 1);
- TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) & 0);
- TEST_EXPECTED_VALUE(std::numeric_limits<Dst>::max(),
- MakeCheckedNum(DstLimits::max()) & -1);
- TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) | 1);
- TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) | 0);
- TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(0) | 1);
- TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(0) | 0);
- TEST_EXPECTED_VALUE(std::numeric_limits<Dst>::max(),
- CheckedNumeric<Dst>(0) | static_cast<Dst>(-1));
- TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) ^ 1);
- TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) ^ 0);
- TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(0) ^ 1);
- TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(0) ^ 0);
- TEST_EXPECTED_VALUE(std::numeric_limits<Dst>::max(),
- CheckedNumeric<Dst>(0) ^ static_cast<Dst>(-1));
- TEST_EXPECTED_VALUE(DstLimits::max(), ~CheckedNumeric<Dst>(0));
-
- TestStrictPointerMath<Dst>();
}
// Floating point arithmetic.
@@ -359,31 +162,32 @@ void TestSpecializedArithmetic(
const char* dst,
int line,
typename std::enable_if<numeric_limits<Dst>::is_iec559, int>::type = 0) {
- using DstLimits = numeric_limits<Dst>;
- TEST_EXPECTED_SUCCESS(-CheckedNumeric<Dst>(DstLimits::lowest()));
+ typedef numeric_limits<Dst> DstLimits;
+ TEST_EXPECTED_SUCCESS(-CheckedNumeric<Dst>(DstLimits::min()));
- TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::lowest()).Abs());
+ TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()).Abs());
TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(-1).Abs());
- TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::lowest()) + -1);
+ TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()) + -1);
TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::max()) + 1);
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) +
- DstLimits::lowest());
+ TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(-DstLimits::max()) +
+ -DstLimits::max());
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) -
- DstLimits::lowest());
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) -
+ -DstLimits::max());
+ TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(-DstLimits::max()) -
DstLimits::max());
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) * 2);
+ TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()) * 2);
TEST_EXPECTED_VALUE(-0.5, CheckedNumeric<Dst>(-1.0) / 2);
+ EXPECT_EQ(static_cast<Dst>(1.0), CheckedNumeric<Dst>(1.0).ValueFloating());
}
// Generic arithmetic tests.
template <typename Dst>
static void TestArithmetic(const char* dst, int line) {
- using DstLimits = numeric_limits<Dst>;
+ typedef numeric_limits<Dst> DstLimits;
EXPECT_EQ(true, CheckedNumeric<Dst>().IsValid());
EXPECT_EQ(false,
@@ -418,13 +222,11 @@ static void TestArithmetic(const char* dst, int line) {
TEST_EXPECTED_VALUE(1, checked_dst /= 1);
// Generic negation.
- if (DstLimits::is_signed) {
- TEST_EXPECTED_VALUE(0, -CheckedNumeric<Dst>());
- TEST_EXPECTED_VALUE(-1, -CheckedNumeric<Dst>(1));
- TEST_EXPECTED_VALUE(1, -CheckedNumeric<Dst>(-1));
- TEST_EXPECTED_VALUE(static_cast<Dst>(DstLimits::max() * -1),
- -CheckedNumeric<Dst>(DstLimits::max()));
- }
+ TEST_EXPECTED_VALUE(0, -CheckedNumeric<Dst>());
+ TEST_EXPECTED_VALUE(-1, -CheckedNumeric<Dst>(1));
+ TEST_EXPECTED_VALUE(1, -CheckedNumeric<Dst>(-1));
+ TEST_EXPECTED_VALUE(static_cast<Dst>(DstLimits::max() * -1),
+ -CheckedNumeric<Dst>(DstLimits::max()));
// Generic absolute value.
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>().Abs());
@@ -435,43 +237,32 @@ static void TestArithmetic(const char* dst, int line) {
// Generic addition.
TEST_EXPECTED_VALUE(1, (CheckedNumeric<Dst>() + 1));
TEST_EXPECTED_VALUE(2, (CheckedNumeric<Dst>(1) + 1));
- if (numeric_limits<Dst>::is_signed)
- TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(-1) + 1));
- TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::lowest()) + 1);
+ TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(-1) + 1));
+ TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()) + 1);
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) +
DstLimits::max());
// Generic subtraction.
+ TEST_EXPECTED_VALUE(-1, (CheckedNumeric<Dst>() - 1));
TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(1) - 1));
+ TEST_EXPECTED_VALUE(-2, (CheckedNumeric<Dst>(-1) - 1));
TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::max()) - 1);
- if (numeric_limits<Dst>::is_signed) {
- TEST_EXPECTED_VALUE(-1, (CheckedNumeric<Dst>() - 1));
- TEST_EXPECTED_VALUE(-2, (CheckedNumeric<Dst>(-1) - 1));
- } else {
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) - -1);
- }
// Generic multiplication.
TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>() * 1));
TEST_EXPECTED_VALUE(1, (CheckedNumeric<Dst>(1) * 1));
+ TEST_EXPECTED_VALUE(-2, (CheckedNumeric<Dst>(-1) * 2));
TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(0) * 0));
- if (numeric_limits<Dst>::is_signed) {
- TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(-1) * 0));
- TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(0) * -1));
- TEST_EXPECTED_VALUE(-2, (CheckedNumeric<Dst>(-1) * 2));
- } else {
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) * -2);
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) *
- CheckedNumeric<uintmax_t>(-2));
- }
+ TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(-1) * 0));
+ TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(0) * -1));
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) *
DstLimits::max());
// Generic division.
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>() / 1);
TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) / 1);
- TEST_EXPECTED_VALUE(DstLimits::lowest() / 2,
- CheckedNumeric<Dst>(DstLimits::lowest()) / 2);
+ TEST_EXPECTED_VALUE(DstLimits::min() / 2,
+ CheckedNumeric<Dst>(DstLimits::min()) / 2);
TEST_EXPECTED_VALUE(DstLimits::max() / 2,
CheckedNumeric<Dst>(DstLimits::max()) / 2);
@@ -513,114 +304,28 @@ enum NumericConversionType {
template <typename Dst, typename Src, NumericConversionType conversion>
struct TestNumericConversion {};
-enum RangeConstraint {
- RANGE_VALID = 0x0, // Value can be represented by the destination type.
- RANGE_UNDERFLOW = 0x1, // Value would underflow.
- RANGE_OVERFLOW = 0x2, // Value would overflow.
- RANGE_INVALID = RANGE_UNDERFLOW | RANGE_OVERFLOW // Invalid (i.e. NaN).
-};
-
-// These are some wrappers to make the tests a bit cleaner.
-constexpr RangeConstraint RangeCheckToEnum(const RangeCheck constraint) {
- return static_cast<RangeConstraint>(
- static_cast<int>(constraint.IsOverflowFlagSet()) << 1 |
- static_cast<int>(constraint.IsUnderflowFlagSet()));
-}
-
// EXPECT_EQ wrappers providing specific detail on test failures.
-#define TEST_EXPECTED_RANGE(expected, actual) \
- EXPECT_EQ(expected, \
- RangeCheckToEnum( \
- base::internal::DstRangeRelationToSrcRange<Dst>(actual))) \
- << "Conversion test: " << src << " value " << actual << " to " << dst \
- << " on line " << line
-
-template <typename Dst, typename Src>
-void TestStrictComparison() {
- using DstLimits = numeric_limits<Dst>;
- using SrcLimits = numeric_limits<Src>;
- static_assert(StrictNumeric<Src>(SrcLimits::lowest()) < DstLimits::max(), "");
- static_assert(StrictNumeric<Src>(SrcLimits::lowest()) < SrcLimits::max(), "");
- static_assert(!(StrictNumeric<Src>(SrcLimits::lowest()) >= DstLimits::max()),
- "");
- static_assert(!(StrictNumeric<Src>(SrcLimits::lowest()) >= SrcLimits::max()),
- "");
- static_assert(StrictNumeric<Src>(SrcLimits::lowest()) <= DstLimits::max(),
- "");
- static_assert(StrictNumeric<Src>(SrcLimits::lowest()) <= SrcLimits::max(),
- "");
- static_assert(!(StrictNumeric<Src>(SrcLimits::lowest()) > DstLimits::max()),
- "");
- static_assert(!(StrictNumeric<Src>(SrcLimits::lowest()) > SrcLimits::max()),
- "");
- static_assert(StrictNumeric<Src>(SrcLimits::max()) > DstLimits::lowest(), "");
- static_assert(StrictNumeric<Src>(SrcLimits::max()) > SrcLimits::lowest(), "");
- static_assert(!(StrictNumeric<Src>(SrcLimits::max()) <= DstLimits::lowest()),
- "");
- static_assert(!(StrictNumeric<Src>(SrcLimits::max()) <= SrcLimits::lowest()),
- "");
- static_assert(StrictNumeric<Src>(SrcLimits::max()) >= DstLimits::lowest(),
- "");
- static_assert(StrictNumeric<Src>(SrcLimits::max()) >= SrcLimits::lowest(),
- "");
- static_assert(!(StrictNumeric<Src>(SrcLimits::max()) < DstLimits::lowest()),
- "");
- static_assert(!(StrictNumeric<Src>(SrcLimits::max()) < SrcLimits::lowest()),
- "");
- static_assert(StrictNumeric<Src>(static_cast<Src>(1)) == static_cast<Dst>(1),
- "");
- static_assert(StrictNumeric<Src>(static_cast<Src>(1)) != static_cast<Dst>(0),
- "");
- static_assert(StrictNumeric<Src>(SrcLimits::max()) != static_cast<Dst>(0),
- "");
- static_assert(StrictNumeric<Src>(SrcLimits::max()) != DstLimits::lowest(),
- "");
- static_assert(
- !(StrictNumeric<Src>(static_cast<Src>(1)) != static_cast<Dst>(1)), "");
- static_assert(
- !(StrictNumeric<Src>(static_cast<Src>(1)) == static_cast<Dst>(0)), "");
-
- // Due to differences in float handling between compilers, these aren't
- // compile-time constants everywhere. So, we use run-time tests.
- EXPECT_EQ(
- SrcLimits::max(),
- MakeCheckedNum(SrcLimits::max()).Max(DstLimits::lowest()).ValueOrDie());
- EXPECT_EQ(
- DstLimits::max(),
- MakeCheckedNum(SrcLimits::lowest()).Max(DstLimits::max()).ValueOrDie());
- EXPECT_EQ(
- DstLimits::lowest(),
- MakeCheckedNum(SrcLimits::max()).Min(DstLimits::lowest()).ValueOrDie());
- EXPECT_EQ(
- SrcLimits::lowest(),
- MakeCheckedNum(SrcLimits::lowest()).Min(DstLimits::max()).ValueOrDie());
- EXPECT_EQ(SrcLimits::lowest(), CheckMin(MakeStrictNum(1), MakeCheckedNum(0),
- DstLimits::max(), SrcLimits::lowest())
- .ValueOrDie());
- EXPECT_EQ(DstLimits::max(), CheckMax(MakeStrictNum(1), MakeCheckedNum(0),
- DstLimits::max(), SrcLimits::lowest())
- .ValueOrDie());
-}
+#define TEST_EXPECTED_RANGE(expected, actual) \
+ EXPECT_EQ(expected, base::internal::DstRangeRelationToSrcRange<Dst>(actual)) \
+ << "Conversion test: " << src << " value " << actual << " to " << dst \
+ << " on line " << line;
template <typename Dst, typename Src>
struct TestNumericConversion<Dst, Src, SIGN_PRESERVING_VALUE_PRESERVING> {
static void Test(const char *dst, const char *src, int line) {
- using SrcLimits = numeric_limits<Src>;
- using DstLimits = numeric_limits<Dst>;
- // Integral to floating.
+ typedef numeric_limits<Src> SrcLimits;
+ typedef numeric_limits<Dst> DstLimits;
+ // Integral to floating.
static_assert((DstLimits::is_iec559 && SrcLimits::is_integer) ||
- // Not floating to integral and...
- (!(DstLimits::is_integer && SrcLimits::is_iec559) &&
- // Same sign, same numeric, source is narrower or same.
- ((SrcLimits::is_signed == DstLimits::is_signed &&
- MaxExponent<Dst>::value >= MaxExponent<Src>::value) ||
- // Or signed destination and source is smaller
- (DstLimits::is_signed &&
- MaxExponent<Dst>::value >= MaxExponent<Src>::value))),
+ // Not floating to integral and...
+ (!(DstLimits::is_integer && SrcLimits::is_iec559) &&
+ // Same sign, same numeric, source is narrower or same.
+ ((SrcLimits::is_signed == DstLimits::is_signed &&
+ sizeof(Dst) >= sizeof(Src)) ||
+ // Or signed destination and source is smaller
+ (DstLimits::is_signed && sizeof(Dst) > sizeof(Src)))),
"Comparison must be sign preserving and value preserving");
- TestStrictComparison<Dst, Src>();
-
const CheckedNumeric<Dst> checked_dst = SrcLimits::max();
TEST_EXPECTED_SUCCESS(checked_dst);
if (MaxExponent<Dst>::value > MaxExponent<Src>::value) {
@@ -645,7 +350,7 @@ struct TestNumericConversion<Dst, Src, SIGN_PRESERVING_VALUE_PRESERVING> {
TEST_EXPECTED_RANGE(RANGE_INVALID, SrcLimits::quiet_NaN());
} else if (numeric_limits<Src>::is_signed) {
TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(-1));
- TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::lowest());
+ TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::min());
}
}
};
@@ -653,15 +358,14 @@ struct TestNumericConversion<Dst, Src, SIGN_PRESERVING_VALUE_PRESERVING> {
template <typename Dst, typename Src>
struct TestNumericConversion<Dst, Src, SIGN_PRESERVING_NARROW> {
static void Test(const char *dst, const char *src, int line) {
- using SrcLimits = numeric_limits<Src>;
- using DstLimits = numeric_limits<Dst>;
+ typedef numeric_limits<Src> SrcLimits;
+ typedef numeric_limits<Dst> DstLimits;
static_assert(SrcLimits::is_signed == DstLimits::is_signed,
"Destination and source sign must be the same");
- static_assert(MaxExponent<Dst>::value <= MaxExponent<Src>::value,
+ static_assert(sizeof(Dst) < sizeof(Src) ||
+ (DstLimits::is_integer && SrcLimits::is_iec559),
"Destination must be narrower than source");
- TestStrictComparison<Dst, Src>();
-
const CheckedNumeric<Dst> checked_dst;
TEST_EXPECTED_FAILURE(checked_dst + SrcLimits::max());
TEST_EXPECTED_VALUE(1, checked_dst + static_cast<Src>(1));
@@ -685,15 +389,15 @@ struct TestNumericConversion<Dst, Src, SIGN_PRESERVING_NARROW> {
TEST_EXPECTED_RANGE(
RANGE_VALID,
static_cast<Src>(GetMaxConvertibleToFloat<Src, Dst>()));
- TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(DstLimits::lowest()));
+ TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(DstLimits::min()));
}
} else if (SrcLimits::is_signed) {
TEST_EXPECTED_VALUE(-1, checked_dst - static_cast<Src>(1));
- TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::lowest());
+ TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::min());
TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(-1));
} else {
TEST_EXPECTED_FAILURE(checked_dst - static_cast<Src>(1));
- TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::lowest());
+ TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::min());
}
}
};
@@ -701,21 +405,19 @@ struct TestNumericConversion<Dst, Src, SIGN_PRESERVING_NARROW> {
template <typename Dst, typename Src>
struct TestNumericConversion<Dst, Src, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL> {
static void Test(const char *dst, const char *src, int line) {
- using SrcLimits = numeric_limits<Src>;
- using DstLimits = numeric_limits<Dst>;
- static_assert(MaxExponent<Dst>::value >= MaxExponent<Src>::value,
+ typedef numeric_limits<Src> SrcLimits;
+ typedef numeric_limits<Dst> DstLimits;
+ static_assert(sizeof(Dst) >= sizeof(Src),
"Destination must be equal or wider than source.");
static_assert(SrcLimits::is_signed, "Source must be signed");
static_assert(!DstLimits::is_signed, "Destination must be unsigned");
- TestStrictComparison<Dst, Src>();
-
const CheckedNumeric<Dst> checked_dst;
TEST_EXPECTED_VALUE(SrcLimits::max(), checked_dst + SrcLimits::max());
TEST_EXPECTED_FAILURE(checked_dst + static_cast<Src>(-1));
- TEST_EXPECTED_FAILURE(checked_dst + SrcLimits::lowest());
+ TEST_EXPECTED_FAILURE(checked_dst + -SrcLimits::max());
- TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::lowest());
+ TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::min());
TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::max());
TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1));
TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, static_cast<Src>(-1));
@@ -725,32 +427,24 @@ struct TestNumericConversion<Dst, Src, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL> {
template <typename Dst, typename Src>
struct TestNumericConversion<Dst, Src, SIGN_TO_UNSIGN_NARROW> {
static void Test(const char *dst, const char *src, int line) {
- using SrcLimits = numeric_limits<Src>;
- using DstLimits = numeric_limits<Dst>;
- static_assert(MaxExponent<Dst>::value < MaxExponent<Src>::value,
+ typedef numeric_limits<Src> SrcLimits;
+ typedef numeric_limits<Dst> DstLimits;
+ static_assert((DstLimits::is_integer && SrcLimits::is_iec559) ||
+ (sizeof(Dst) < sizeof(Src)),
"Destination must be narrower than source.");
static_assert(SrcLimits::is_signed, "Source must be signed.");
static_assert(!DstLimits::is_signed, "Destination must be unsigned.");
- TestStrictComparison<Dst, Src>();
-
const CheckedNumeric<Dst> checked_dst;
TEST_EXPECTED_VALUE(1, checked_dst + static_cast<Src>(1));
TEST_EXPECTED_FAILURE(checked_dst + SrcLimits::max());
TEST_EXPECTED_FAILURE(checked_dst + static_cast<Src>(-1));
- TEST_EXPECTED_FAILURE(checked_dst + SrcLimits::lowest());
+ TEST_EXPECTED_FAILURE(checked_dst + -SrcLimits::max());
TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::max());
TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1));
TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, static_cast<Src>(-1));
-
- // Additional saturation tests.
- EXPECT_EQ(DstLimits::max(), saturated_cast<Dst>(SrcLimits::max())) << src;
- EXPECT_EQ(DstLimits::lowest(), saturated_cast<Dst>(SrcLimits::lowest()));
-
if (SrcLimits::is_iec559) {
- EXPECT_EQ(Dst(0), saturated_cast<Dst>(SrcLimits::quiet_NaN()));
-
TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::max() * -1);
TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::infinity());
TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::infinity() * -1);
@@ -765,10 +459,10 @@ struct TestNumericConversion<Dst, Src, SIGN_TO_UNSIGN_NARROW> {
TEST_EXPECTED_RANGE(
RANGE_VALID,
static_cast<Src>(GetMaxConvertibleToFloat<Src, Dst>()));
- TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(DstLimits::lowest()));
+ TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(DstLimits::min()));
}
} else {
- TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::lowest());
+ TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::min());
}
}
};
@@ -776,27 +470,21 @@ struct TestNumericConversion<Dst, Src, SIGN_TO_UNSIGN_NARROW> {
template <typename Dst, typename Src>
struct TestNumericConversion<Dst, Src, UNSIGN_TO_SIGN_NARROW_OR_EQUAL> {
static void Test(const char *dst, const char *src, int line) {
- using SrcLimits = numeric_limits<Src>;
- using DstLimits = numeric_limits<Dst>;
- static_assert(MaxExponent<Dst>::value <= MaxExponent<Src>::value,
+ typedef numeric_limits<Src> SrcLimits;
+ typedef numeric_limits<Dst> DstLimits;
+ static_assert(sizeof(Dst) <= sizeof(Src),
"Destination must be narrower or equal to source.");
static_assert(!SrcLimits::is_signed, "Source must be unsigned.");
static_assert(DstLimits::is_signed, "Destination must be signed.");
- TestStrictComparison<Dst, Src>();
-
const CheckedNumeric<Dst> checked_dst;
TEST_EXPECTED_VALUE(1, checked_dst + static_cast<Src>(1));
TEST_EXPECTED_FAILURE(checked_dst + SrcLimits::max());
- TEST_EXPECTED_VALUE(SrcLimits::lowest(), checked_dst + SrcLimits::lowest());
+ TEST_EXPECTED_VALUE(SrcLimits::min(), checked_dst + SrcLimits::min());
- TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::lowest());
+ TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::min());
TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::max());
TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1));
-
- // Additional saturation tests.
- EXPECT_EQ(DstLimits::max(), saturated_cast<Dst>(SrcLimits::max()));
- EXPECT_EQ(Dst(0), saturated_cast<Dst>(SrcLimits::lowest()));
}
};
@@ -895,43 +583,6 @@ TEST(SafeNumerics, SizeTOperations) {
TEST_NUMERIC_CONVERSION(int, size_t, UNSIGN_TO_SIGN_NARROW_OR_EQUAL);
}
-// A one-off test to ensure StrictNumeric won't resolve to an incorrect type.
-// If this fails we'll just get a compiler error on an ambiguous overload.
-int TestOverload(int) { // Overload fails.
- return 0;
-}
-uint8_t TestOverload(uint8_t) { // Overload fails.
- return 0;
-}
-size_t TestOverload(size_t) { // Overload succeeds.
- return 0;
-}
-
-static_assert(
- std::is_same<decltype(TestOverload(StrictNumeric<int>())), int>::value,
- "");
-static_assert(std::is_same<decltype(TestOverload(StrictNumeric<size_t>())),
- size_t>::value,
- "");
-
-template <typename T>
-struct CastTest1 {
- static constexpr T NaN() { return -1; }
- static constexpr T max() { return numeric_limits<T>::max() - 1; }
- static constexpr T Overflow() { return max(); }
- static constexpr T lowest() { return numeric_limits<T>::lowest() + 1; }
- static constexpr T Underflow() { return lowest(); }
-};
-
-template <typename T>
-struct CastTest2 {
- static constexpr T NaN() { return 11; }
- static constexpr T max() { return 10; }
- static constexpr T Overflow() { return max(); }
- static constexpr T lowest() { return 1; }
- static constexpr T Underflow() { return lowest(); }
-};
-
TEST(SafeNumerics, CastTests) {
// MSVC catches and warns that we're forcing saturation in these tests.
// Since that's intentional, we need to shut this warning off.
@@ -945,7 +596,7 @@ TEST(SafeNumerics, CastTests) {
double double_large = numeric_limits<double>::max();
double double_infinity = numeric_limits<float>::infinity();
double double_large_int = numeric_limits<int>::max();
- double double_small_int = numeric_limits<int>::lowest();
+ double double_small_int = numeric_limits<int>::min();
// Just test that the casts compile, since the other tests cover logic.
EXPECT_EQ(0, checked_cast<int>(static_cast<size_t>(0)));
@@ -961,9 +612,9 @@ TEST(SafeNumerics, CastTests) {
EXPECT_FALSE(CheckedNumeric<unsigned>(StrictNumeric<int>(-1)).IsValid());
EXPECT_TRUE(IsValueNegative(-1));
- EXPECT_TRUE(IsValueNegative(numeric_limits<int>::lowest()));
- EXPECT_FALSE(IsValueNegative(numeric_limits<unsigned>::lowest()));
- EXPECT_TRUE(IsValueNegative(numeric_limits<double>::lowest()));
+ EXPECT_TRUE(IsValueNegative(numeric_limits<int>::min()));
+ EXPECT_FALSE(IsValueNegative(numeric_limits<unsigned>::min()));
+ EXPECT_TRUE(IsValueNegative(-numeric_limits<double>::max()));
EXPECT_FALSE(IsValueNegative(0));
EXPECT_FALSE(IsValueNegative(1));
EXPECT_FALSE(IsValueNegative(0u));
@@ -990,83 +641,27 @@ TEST(SafeNumerics, CastTests) {
EXPECT_EQ(saturated_cast<int>(double_large), numeric_limits<int>::max());
EXPECT_EQ(saturated_cast<float>(double_large), double_infinity);
EXPECT_EQ(saturated_cast<float>(-double_large), -double_infinity);
- EXPECT_EQ(numeric_limits<int>::lowest(),
- saturated_cast<int>(double_small_int));
+ EXPECT_EQ(numeric_limits<int>::min(), saturated_cast<int>(double_small_int));
EXPECT_EQ(numeric_limits<int>::max(), saturated_cast<int>(double_large_int));
- // Test the saturated cast overrides.
- using FloatLimits = numeric_limits<float>;
- using IntLimits = numeric_limits<int>;
- EXPECT_EQ(-1, (saturated_cast<int, CastTest1>(FloatLimits::quiet_NaN())));
- EXPECT_EQ(CastTest1<int>::max(),
- (saturated_cast<int, CastTest1>(FloatLimits::infinity())));
- EXPECT_EQ(CastTest1<int>::max(),
- (saturated_cast<int, CastTest1>(FloatLimits::max())));
- EXPECT_EQ(CastTest1<int>::max(),
- (saturated_cast<int, CastTest1>(float(IntLimits::max()))));
- EXPECT_EQ(CastTest1<int>::lowest(),
- (saturated_cast<int, CastTest1>(-FloatLimits::infinity())));
- EXPECT_EQ(CastTest1<int>::lowest(),
- (saturated_cast<int, CastTest1>(FloatLimits::lowest())));
- EXPECT_EQ(0, (saturated_cast<int, CastTest1>(0.0)));
- EXPECT_EQ(1, (saturated_cast<int, CastTest1>(1.0)));
- EXPECT_EQ(-1, (saturated_cast<int, CastTest1>(-1.0)));
- EXPECT_EQ(0, (saturated_cast<int, CastTest1>(0)));
- EXPECT_EQ(1, (saturated_cast<int, CastTest1>(1)));
- EXPECT_EQ(-1, (saturated_cast<int, CastTest1>(-1)));
- EXPECT_EQ(CastTest1<int>::lowest(),
- (saturated_cast<int, CastTest1>(float(IntLimits::lowest()))));
- EXPECT_EQ(11, (saturated_cast<int, CastTest2>(FloatLimits::quiet_NaN())));
- EXPECT_EQ(10, (saturated_cast<int, CastTest2>(FloatLimits::infinity())));
- EXPECT_EQ(10, (saturated_cast<int, CastTest2>(FloatLimits::max())));
- EXPECT_EQ(1, (saturated_cast<int, CastTest2>(-FloatLimits::infinity())));
- EXPECT_EQ(1, (saturated_cast<int, CastTest2>(FloatLimits::lowest())));
- EXPECT_EQ(1, (saturated_cast<int, CastTest2>(0U)));
-
float not_a_number = std::numeric_limits<float>::infinity() -
std::numeric_limits<float>::infinity();
EXPECT_TRUE(std::isnan(not_a_number));
EXPECT_EQ(0, saturated_cast<int>(not_a_number));
+}
+
+#if GTEST_HAS_DEATH_TEST
- // Test the CheckedNumeric value extractions functions.
- auto int8_min = MakeCheckedNum(numeric_limits<int8_t>::lowest());
- auto int8_max = MakeCheckedNum(numeric_limits<int8_t>::max());
- auto double_max = MakeCheckedNum(numeric_limits<double>::max());
- static_assert(
- std::is_same<int16_t,
- decltype(int8_min.ValueOrDie<int16_t>())::type>::value,
- "ValueOrDie returning incorrect type.");
- static_assert(
- std::is_same<int16_t,
- decltype(int8_min.ValueOrDefault<int16_t>(0))::type>::value,
- "ValueOrDefault returning incorrect type.");
- EXPECT_FALSE(IsValidForType<uint8_t>(int8_min));
- EXPECT_TRUE(IsValidForType<uint8_t>(int8_max));
- EXPECT_EQ(static_cast<int>(numeric_limits<int8_t>::lowest()),
- ValueOrDieForType<int>(int8_min));
- EXPECT_TRUE(IsValidForType<uint32_t>(int8_max));
- EXPECT_EQ(static_cast<int>(numeric_limits<int8_t>::max()),
- ValueOrDieForType<int>(int8_max));
- EXPECT_EQ(0, ValueOrDefaultForType<int>(double_max, 0));
- uint8_t uint8_dest = 0;
- int16_t int16_dest = 0;
- double double_dest = 0;
- EXPECT_TRUE(int8_max.AssignIfValid(&uint8_dest));
- EXPECT_EQ(static_cast<uint8_t>(numeric_limits<int8_t>::max()), uint8_dest);
- EXPECT_FALSE(int8_min.AssignIfValid(&uint8_dest));
- EXPECT_TRUE(int8_max.AssignIfValid(&int16_dest));
- EXPECT_EQ(static_cast<int16_t>(numeric_limits<int8_t>::max()), int16_dest);
- EXPECT_TRUE(int8_min.AssignIfValid(&int16_dest));
- EXPECT_EQ(static_cast<int16_t>(numeric_limits<int8_t>::lowest()), int16_dest);
- EXPECT_FALSE(double_max.AssignIfValid(&uint8_dest));
- EXPECT_FALSE(double_max.AssignIfValid(&int16_dest));
- EXPECT_TRUE(double_max.AssignIfValid(&double_dest));
- EXPECT_EQ(numeric_limits<double>::max(), double_dest);
- EXPECT_EQ(1, checked_cast<int>(StrictNumeric<int>(1)));
- EXPECT_EQ(1, saturated_cast<int>(StrictNumeric<int>(1)));
- EXPECT_EQ(1, strict_cast<int>(StrictNumeric<int>(1)));
+TEST(SafeNumerics, SaturatedCastChecks) {
+ float not_a_number = std::numeric_limits<float>::infinity() -
+ std::numeric_limits<float>::infinity();
+ EXPECT_TRUE(std::isnan(not_a_number));
+ EXPECT_DEATH((saturated_cast<int, base::SaturatedCastNaNBehaviorCheck>(
+ not_a_number)), "");
}
+#endif // GTEST_HAS_DEATH_TEST
+
TEST(SafeNumerics, IsValueInRangeForNumericType) {
EXPECT_TRUE(IsValueInRangeForNumericType<uint32_t>(0));
EXPECT_TRUE(IsValueInRangeForNumericType<uint32_t>(1));
@@ -1077,9 +672,9 @@ TEST(SafeNumerics, IsValueInRangeForNumericType) {
EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>(UINT64_C(0x100000000)));
EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>(UINT64_C(0x100000001)));
EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>(
- std::numeric_limits<int32_t>::lowest()));
+ std::numeric_limits<int32_t>::min()));
EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>(
- std::numeric_limits<int64_t>::lowest()));
+ std::numeric_limits<int64_t>::min()));
EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(0));
EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(1));
@@ -1093,13 +688,13 @@ TEST(SafeNumerics, IsValueInRangeForNumericType) {
EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(INT64_C(0xffffffff)));
EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(INT64_C(0x100000000)));
EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(
- std::numeric_limits<int32_t>::lowest()));
+ std::numeric_limits<int32_t>::min()));
EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(
- static_cast<int64_t>(std::numeric_limits<int32_t>::lowest())));
+ static_cast<int64_t>(std::numeric_limits<int32_t>::min())));
EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(
- static_cast<int64_t>(std::numeric_limits<int32_t>::lowest()) - 1));
+ static_cast<int64_t>(std::numeric_limits<int32_t>::min()) - 1));
EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(
- std::numeric_limits<int64_t>::lowest()));
+ std::numeric_limits<int64_t>::min()));
EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(0));
EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(1));
@@ -1110,10 +705,10 @@ TEST(SafeNumerics, IsValueInRangeForNumericType) {
EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(UINT64_C(0x100000000)));
EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(UINT64_C(0x100000001)));
EXPECT_FALSE(IsValueInRangeForNumericType<uint64_t>(
- std::numeric_limits<int32_t>::lowest()));
+ std::numeric_limits<int32_t>::min()));
EXPECT_FALSE(IsValueInRangeForNumericType<uint64_t>(INT64_C(-1)));
EXPECT_FALSE(IsValueInRangeForNumericType<uint64_t>(
- std::numeric_limits<int64_t>::lowest()));
+ std::numeric_limits<int64_t>::min()));
EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(0));
EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(1));
@@ -1135,11 +730,11 @@ TEST(SafeNumerics, IsValueInRangeForNumericType) {
EXPECT_FALSE(
IsValueInRangeForNumericType<int64_t>(UINT64_C(0xffffffffffffffff)));
EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(
- std::numeric_limits<int32_t>::lowest()));
+ std::numeric_limits<int32_t>::min()));
EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(
- static_cast<int64_t>(std::numeric_limits<int32_t>::lowest())));
+ static_cast<int64_t>(std::numeric_limits<int32_t>::min())));
EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(
- std::numeric_limits<int64_t>::lowest()));
+ std::numeric_limits<int64_t>::min()));
}
TEST(SafeNumerics, CompoundNumericOperations) {
@@ -1165,22 +760,3 @@ TEST(SafeNumerics, CompoundNumericOperations) {
too_large /= d;
EXPECT_FALSE(too_large.IsValid());
}
-
-TEST(SafeNumerics, VariadicNumericOperations) {
- auto a = CheckAdd(1, 2UL, MakeCheckedNum(3LL), 4).ValueOrDie();
- EXPECT_EQ(static_cast<decltype(a)::type>(10), a);
- auto b = CheckSub(MakeCheckedNum(20.0), 2UL, 4).ValueOrDie();
- EXPECT_EQ(static_cast<decltype(b)::type>(14.0), b);
- auto c = CheckMul(20.0, MakeCheckedNum(1), 5, 3UL).ValueOrDie();
- EXPECT_EQ(static_cast<decltype(c)::type>(300.0), c);
- auto d = CheckDiv(20.0, 2.0, MakeCheckedNum(5LL), -4).ValueOrDie();
- EXPECT_EQ(static_cast<decltype(d)::type>(-.5), d);
- auto e = CheckMod(MakeCheckedNum(20), 3).ValueOrDie();
- EXPECT_EQ(static_cast<decltype(e)::type>(2), e);
- auto f = CheckLsh(1, MakeCheckedNum(2)).ValueOrDie();
- EXPECT_EQ(static_cast<decltype(f)::type>(4), f);
- auto g = CheckRsh(4, MakeCheckedNum(2)).ValueOrDie();
- EXPECT_EQ(static_cast<decltype(g)::type>(1), g);
- auto h = CheckRsh(CheckAdd(1, 1, 1, 1), CheckSub(4, 2)).ValueOrDie();
- EXPECT_EQ(static_cast<decltype(h)::type>(1), h);
-}
diff --git a/base/observer_list.h b/base/observer_list.h
index 0572ba6500..afe1f46cd6 100644
--- a/base/observer_list.h
+++ b/base/observer_list.h
@@ -11,7 +11,6 @@
#include <limits>
#include <vector>
-#include "base/gtest_prod_util.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
@@ -47,14 +46,11 @@
// }
//
// void NotifyFoo() {
-// for (auto& observer : observer_list_)
-// observer.OnFoo(this);
+// FOR_EACH_OBSERVER(Observer, observer_list_, OnFoo(this));
// }
//
// void NotifyBar(int x, int y) {
-// for (FooList::iterator i = observer_list.begin(),
-// e = observer_list.end(); i != e; ++i)
-// i->OnBar(this, x, y);
+// FOR_EACH_OBSERVER(Observer, observer_list_, OnBar(this, x, y));
// }
//
// private:
@@ -84,66 +80,20 @@ class ObserverListBase
NOTIFY_EXISTING_ONLY
};
- // An iterator class that can be used to access the list of observers.
- template <class ContainerType>
- class Iter {
+ // An iterator class that can be used to access the list of observers. See
+ // also the FOR_EACH_OBSERVER macro defined below.
+ class Iterator {
public:
- Iter();
- explicit Iter(ContainerType* list);
- ~Iter();
-
- // A workaround for C2244. MSVC requires fully qualified type name for
- // return type on a function definition to match a function declaration.
- using ThisType =
- typename ObserverListBase<ObserverType>::template Iter<ContainerType>;
-
- bool operator==(const Iter& other) const;
- bool operator!=(const Iter& other) const;
- ThisType& operator++();
- ObserverType* operator->() const;
- ObserverType& operator*() const;
+ explicit Iterator(ObserverListBase<ObserverType>* list);
+ ~Iterator();
+ ObserverType* GetNext();
private:
- FRIEND_TEST_ALL_PREFIXES(ObserverListTest, BasicStdIterator);
- FRIEND_TEST_ALL_PREFIXES(ObserverListTest, StdIteratorRemoveFront);
-
- ObserverType* GetCurrent() const;
- void EnsureValidIndex();
-
- size_t clamped_max_index() const {
- return std::min(max_index_, list_->observers_.size());
- }
-
- bool is_end() const { return !list_ || index_ == clamped_max_index(); }
-
WeakPtr<ObserverListBase<ObserverType>> list_;
- // When initially constructed and each time the iterator is incremented,
- // |index_| is guaranteed to point to a non-null index if the iterator
- // has not reached the end of the ObserverList.
size_t index_;
size_t max_index_;
};
- using Iterator = Iter<ObserverListBase<ObserverType>>;
-
- using iterator = Iter<ObserverListBase<ObserverType>>;
- iterator begin() {
- // An optimization: do not involve weak pointers for empty list.
- // Note: can't use ?: operator here due to some MSVC bug (unit tests fail)
- if (observers_.empty())
- return iterator();
- return iterator(this);
- }
- iterator end() { return iterator(); }
-
- using const_iterator = Iter<const ObserverListBase<ObserverType>>;
- const_iterator begin() const {
- if (observers_.empty())
- return const_iterator();
- return const_iterator(this);
- }
- const_iterator end() const { return const_iterator(); }
-
ObserverListBase() : notify_depth_(0), type_(NOTIFY_ALL) {}
explicit ObserverListBase(NotificationType type)
: notify_depth_(0), type_(type) {}
@@ -174,99 +124,37 @@ class ObserverListBase
int notify_depth_;
NotificationType type_;
- template <class ContainerType>
- friend class Iter;
+ friend class ObserverListBase::Iterator;
DISALLOW_COPY_AND_ASSIGN(ObserverListBase);
};
template <class ObserverType>
-template <class ContainerType>
-ObserverListBase<ObserverType>::Iter<ContainerType>::Iter()
- : index_(0), max_index_(0) {}
-
-template <class ObserverType>
-template <class ContainerType>
-ObserverListBase<ObserverType>::Iter<ContainerType>::Iter(ContainerType* list)
- : list_(const_cast<ObserverListBase<ObserverType>*>(list)->AsWeakPtr()),
+ObserverListBase<ObserverType>::Iterator::Iterator(
+ ObserverListBase<ObserverType>* list)
+ : list_(list->AsWeakPtr()),
index_(0),
max_index_(list->type_ == NOTIFY_ALL ? std::numeric_limits<size_t>::max()
: list->observers_.size()) {
- EnsureValidIndex();
- DCHECK(list_);
++list_->notify_depth_;
}
template <class ObserverType>
-template <class ContainerType>
-ObserverListBase<ObserverType>::Iter<ContainerType>::~Iter() {
- if (list_ && --list_->notify_depth_ == 0)
+ObserverListBase<ObserverType>::Iterator::~Iterator() {
+ if (list_.get() && --list_->notify_depth_ == 0)
list_->Compact();
}
template <class ObserverType>
-template <class ContainerType>
-bool ObserverListBase<ObserverType>::Iter<ContainerType>::operator==(
- const Iter& other) const {
- if (is_end() && other.is_end())
- return true;
- return list_.get() == other.list_.get() && index_ == other.index_;
-}
-
-template <class ObserverType>
-template <class ContainerType>
-bool ObserverListBase<ObserverType>::Iter<ContainerType>::operator!=(
- const Iter& other) const {
- return !operator==(other);
-}
-
-template <class ObserverType>
-template <class ContainerType>
-typename ObserverListBase<ObserverType>::template Iter<ContainerType>&
- ObserverListBase<ObserverType>::Iter<ContainerType>::operator++() {
- if (list_) {
- ++index_;
- EnsureValidIndex();
- }
- return *this;
-}
-
-template <class ObserverType>
-template <class ContainerType>
-ObserverType* ObserverListBase<ObserverType>::Iter<ContainerType>::operator->()
- const {
- ObserverType* current = GetCurrent();
- DCHECK(current);
- return current;
-}
-
-template <class ObserverType>
-template <class ContainerType>
-ObserverType& ObserverListBase<ObserverType>::Iter<ContainerType>::operator*()
- const {
- ObserverType* current = GetCurrent();
- DCHECK(current);
- return *current;
-}
-
-template <class ObserverType>
-template <class ContainerType>
-ObserverType* ObserverListBase<ObserverType>::Iter<ContainerType>::GetCurrent()
- const {
- if (!list_)
+ObserverType* ObserverListBase<ObserverType>::Iterator::GetNext() {
+ if (!list_.get())
return nullptr;
- return index_ < clamped_max_index() ? list_->observers_[index_] : nullptr;
-}
-
-template <class ObserverType>
-template <class ContainerType>
-void ObserverListBase<ObserverType>::Iter<ContainerType>::EnsureValidIndex() {
- if (!list_)
- return;
-
- size_t max_index = clamped_max_index();
- while (index_ < max_index && !list_->observers_[index_])
+ ListType& observers = list_->observers_;
+ // Advance if the current element is null
+ size_t max_index = std::min(max_index_, observers.size());
+ while (index_ < max_index && !observers[index_])
++index_;
+ return index_ < max_index ? observers[index_++] : nullptr;
}
template <class ObserverType>
@@ -317,8 +205,9 @@ void ObserverListBase<ObserverType>::Clear() {
template <class ObserverType>
void ObserverListBase<ObserverType>::Compact() {
- observers_.erase(std::remove(observers_.begin(), observers_.end(), nullptr),
- observers_.end());
+ observers_.erase(
+ std::remove(observers_.begin(), observers_.end(), nullptr),
+ observers_.end());
}
template <class ObserverType, bool check_empty = false>
@@ -344,6 +233,17 @@ class ObserverList : public ObserverListBase<ObserverType> {
}
};
+#define FOR_EACH_OBSERVER(ObserverType, observer_list, func) \
+ do { \
+ if ((observer_list).might_have_observers()) { \
+ typename base::ObserverListBase<ObserverType>::Iterator \
+ it_inside_observer_macro(&observer_list); \
+ ObserverType* obs; \
+ while ((obs = it_inside_observer_macro.GetNext()) != nullptr) \
+ obs->func; \
+ } \
+ } while (0)
+
} // namespace base
#endif // BASE_OBSERVER_LIST_H_
diff --git a/base/observer_list_threadsafe.h b/base/observer_list_threadsafe.h
index afb1010b67..fe783542f4 100644
--- a/base/observer_list_threadsafe.h
+++ b/base/observer_list_threadsafe.h
@@ -7,17 +7,17 @@
#include <algorithm>
#include <map>
-#include <memory>
#include <tuple>
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/macros.h"
-#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
#include "base/observer_list.h"
#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread_task_runner_handle.h"
@@ -55,27 +55,56 @@
///////////////////////////////////////////////////////////////////////////////
namespace base {
-namespace internal {
-template <typename ObserverType, typename Method>
-struct Dispatcher;
+// Forward declaration for ObserverListThreadSafeTraits.
+template <class ObserverType>
+class ObserverListThreadSafe;
-template <typename ObserverType, typename ReceiverType, typename... Params>
-struct Dispatcher<ObserverType, void(ReceiverType::*)(Params...)> {
- static void Run(void(ReceiverType::* m)(Params...),
- Params... params, ObserverType* obj) {
- (obj->*m)(std::forward<Params>(params)...);
+namespace internal {
+
+// An UnboundMethod is a wrapper for a method where the actual object is
+// provided at Run dispatch time.
+template <class T, class Method, class Params>
+class UnboundMethod {
+ public:
+ UnboundMethod(Method m, const Params& p) : m_(m), p_(p) {
+ static_assert((internal::ParamsUseScopedRefptrCorrectly<Params>::value),
+ "bad unbound method params");
}
+ void Run(T* obj) const {
+ DispatchToMethod(obj, m_, p_);
+ }
+ private:
+ Method m_;
+ Params p_;
};
} // namespace internal
+// This class is used to work around VS2005 not accepting:
+//
+// friend class
+// base::RefCountedThreadSafe<ObserverListThreadSafe<ObserverType>>;
+//
+// Instead of friending the class, we could friend the actual function
+// which calls delete. However, this ends up being
+// RefCountedThreadSafe::DeleteInternal(), which is private. So we
+// define our own templated traits class so we can friend it.
+template <class T>
+struct ObserverListThreadSafeTraits {
+ static void Destruct(const ObserverListThreadSafe<T>* x) {
+ delete x;
+ }
+};
+
template <class ObserverType>
class ObserverListThreadSafe
- : public RefCountedThreadSafe<ObserverListThreadSafe<ObserverType>> {
+ : public RefCountedThreadSafe<
+ ObserverListThreadSafe<ObserverType>,
+ ObserverListThreadSafeTraits<ObserverType>> {
public:
- using NotificationType =
- typename ObserverList<ObserverType>::NotificationType;
+ typedef typename ObserverList<ObserverType>::NotificationType
+ NotificationType;
ObserverListThreadSafe()
: type_(ObserverListBase<ObserverType>::NOTIFY_ALL) {}
@@ -84,19 +113,17 @@ class ObserverListThreadSafe
// Add an observer to the list. An observer should not be added to
// the same list more than once.
void AddObserver(ObserverType* obs) {
- // If there is no ThreadTaskRunnerHandle, it is impossible to notify on it,
+ // If there is not a current MessageLoop, it is impossible to notify on it,
// so do not add the observer.
- if (!ThreadTaskRunnerHandle::IsSet())
+ if (!MessageLoop::current())
return;
ObserverList<ObserverType>* list = nullptr;
PlatformThreadId thread_id = PlatformThread::CurrentId();
{
AutoLock lock(list_lock_);
- if (observer_lists_.find(thread_id) == observer_lists_.end()) {
- observer_lists_[thread_id] =
- base::MakeUnique<ObserverListContext>(type_);
- }
+ if (observer_lists_.find(thread_id) == observer_lists_.end())
+ observer_lists_[thread_id] = new ObserverListContext(type_);
list = &(observer_lists_[thread_id]->list);
}
list->AddObserver(obs);
@@ -108,24 +135,32 @@ class ObserverListThreadSafe
// If the observer to be removed is in the list, RemoveObserver MUST
// be called from the same thread which called AddObserver.
void RemoveObserver(ObserverType* obs) {
+ ObserverListContext* context = nullptr;
+ ObserverList<ObserverType>* list = nullptr;
PlatformThreadId thread_id = PlatformThread::CurrentId();
{
AutoLock lock(list_lock_);
- auto it = observer_lists_.find(thread_id);
+ typename ObserversListMap::iterator it = observer_lists_.find(thread_id);
if (it == observer_lists_.end()) {
// This will happen if we try to remove an observer on a thread
// we never added an observer for.
return;
}
- ObserverList<ObserverType>& list = it->second->list;
-
- list.RemoveObserver(obs);
+ context = it->second;
+ list = &context->list;
- // If that was the last observer in the list, remove the ObserverList
- // entirely.
- if (list.size() == 0)
+ // If we're about to remove the last observer from the list,
+ // then we can remove this observer_list entirely.
+ if (list->HasObserver(obs) && list->size() == 1)
observer_lists_.erase(it);
}
+ list->RemoveObserver(obs);
+
+ // If RemoveObserver is called from a notification, the size will be
+ // nonzero. Instead of deleting here, the NotifyWrapper will delete
+ // when it finishes iterating.
+ if (list->size() == 0)
+ delete context;
}
// Verifies that the list is currently empty (i.e. there are no observers).
@@ -139,25 +174,27 @@ class ObserverListThreadSafe
// Note, these calls are effectively asynchronous. You cannot assume
// that at the completion of the Notify call that all Observers have
// been Notified. The notification may still be pending delivery.
- template <typename Method, typename... Params>
+ template <class Method, class... Params>
void Notify(const tracked_objects::Location& from_here,
- Method m, Params&&... params) {
- Callback<void(ObserverType*)> method =
- Bind(&internal::Dispatcher<ObserverType, Method>::Run,
- m, std::forward<Params>(params)...);
+ Method m,
+ const Params&... params) {
+ internal::UnboundMethod<ObserverType, Method, std::tuple<Params...>> method(
+ m, std::make_tuple(params...));
AutoLock lock(list_lock_);
for (const auto& entry : observer_lists_) {
- ObserverListContext* context = entry.second.get();
+ ObserverListContext* context = entry.second;
context->task_runner->PostTask(
from_here,
- Bind(&ObserverListThreadSafe<ObserverType>::NotifyWrapper,
+ Bind(&ObserverListThreadSafe<ObserverType>::template NotifyWrapper<
+ Method, std::tuple<Params...>>,
this, context, method));
}
}
private:
- friend class RefCountedThreadSafe<ObserverListThreadSafe<ObserverType>>;
+ // See comment above ObserverListThreadSafeTraits' definition.
+ friend struct ObserverListThreadSafeTraits<ObserverType>;
struct ObserverListContext {
explicit ObserverListContext(NotificationType type)
@@ -171,28 +208,35 @@ class ObserverListThreadSafe
};
~ObserverListThreadSafe() {
+ STLDeleteValues(&observer_lists_);
}
// Wrapper which is called to fire the notifications for each thread's
// ObserverList. This function MUST be called on the thread which owns
// the unsafe ObserverList.
- void NotifyWrapper(ObserverListContext* context,
- const Callback<void(ObserverType*)>& method) {
+ template <class Method, class Params>
+ void NotifyWrapper(
+ ObserverListContext* context,
+ const internal::UnboundMethod<ObserverType, Method, Params>& method) {
// Check that this list still needs notifications.
{
AutoLock lock(list_lock_);
- auto it = observer_lists_.find(PlatformThread::CurrentId());
+ typename ObserversListMap::iterator it =
+ observer_lists_.find(PlatformThread::CurrentId());
// The ObserverList could have been removed already. In fact, it could
// have been removed and then re-added! If the master list's loop
// does not match this one, then we do not need to finish this
// notification.
- if (it == observer_lists_.end() || it->second.get() != context)
+ if (it == observer_lists_.end() || it->second != context)
return;
}
- for (auto& observer : context->list) {
- method.Run(&observer);
+ {
+ typename ObserverList<ObserverType>::Iterator it(&context->list);
+ ObserverType* obs;
+ while ((obs = it.GetNext()) != nullptr)
+ method.Run(obs);
}
// If there are no more observers on the list, we can now delete it.
@@ -202,22 +246,23 @@ class ObserverListThreadSafe
// Remove |list| if it's not already removed.
// This can happen if multiple observers got removed in a notification.
// See http://crbug.com/55725.
- auto it = observer_lists_.find(PlatformThread::CurrentId());
- if (it != observer_lists_.end() && it->second.get() == context)
+ typename ObserversListMap::iterator it =
+ observer_lists_.find(PlatformThread::CurrentId());
+ if (it != observer_lists_.end() && it->second == context)
observer_lists_.erase(it);
}
+ delete context;
}
}
- mutable Lock list_lock_; // Protects the observer_lists_.
-
// Key by PlatformThreadId because in tests, clients can attempt to remove
- // observers without a SingleThreadTaskRunner. If this were keyed by
- // SingleThreadTaskRunner, that operation would be silently ignored, leaving
- // garbage in the ObserverList.
- std::map<PlatformThreadId, std::unique_ptr<ObserverListContext>>
- observer_lists_;
+ // observers without a MessageLoop. If this were keyed by MessageLoop, that
+ // operation would be silently ignored, leaving garbage in the ObserverList.
+ typedef std::map<PlatformThreadId, ObserverListContext*>
+ ObserversListMap;
+ mutable Lock list_lock_; // Protects the observer_lists_.
+ ObserversListMap observer_lists_;
const NotificationType type_;
DISALLOW_COPY_AND_ASSIGN(ObserverListThreadSafe);
diff --git a/base/observer_list_unittest.cc b/base/observer_list_unittest.cc
index c5e556bd9d..097a2ed28b 100644
--- a/base/observer_list_unittest.cc
+++ b/base/observer_list_unittest.cc
@@ -22,17 +22,13 @@ class Foo {
public:
virtual void Observe(int x) = 0;
virtual ~Foo() {}
- virtual int GetValue() const { return 0; }
};
class Adder : public Foo {
public:
explicit Adder(int scaler) : total(0), scaler_(scaler) {}
- ~Adder() override {}
-
void Observe(int x) override { total += x * scaler_; }
- int GetValue() const override { return total; }
-
+ ~Adder() override {}
int total;
private:
@@ -41,28 +37,16 @@ class Adder : public Foo {
class Disrupter : public Foo {
public:
- Disrupter(ObserverList<Foo>* list, Foo* doomed, bool remove_self)
- : list_(list), doomed_(doomed), remove_self_(remove_self) {}
Disrupter(ObserverList<Foo>* list, Foo* doomed)
- : Disrupter(list, doomed, false) {}
- Disrupter(ObserverList<Foo>* list, bool remove_self)
- : Disrupter(list, nullptr, remove_self) {}
-
- ~Disrupter() override {}
-
- void Observe(int x) override {
- if (remove_self_)
- list_->RemoveObserver(this);
- if (doomed_)
- list_->RemoveObserver(doomed_);
+ : list_(list),
+ doomed_(doomed) {
}
-
- void SetDoomed(Foo* doomed) { doomed_ = doomed; }
+ ~Disrupter() override {}
+ void Observe(int x) override { list_->RemoveObserver(doomed_); }
private:
ObserverList<Foo>* list_;
Foo* doomed_;
- bool remove_self_;
};
class ThreadSafeDisrupter : public Foo {
@@ -83,19 +67,21 @@ template <typename ObserverListType>
class AddInObserve : public Foo {
public:
explicit AddInObserve(ObserverListType* observer_list)
- : observer_list(observer_list), to_add_() {}
-
- void SetToAdd(Foo* to_add) { to_add_ = to_add; }
+ : added(false),
+ observer_list(observer_list),
+ adder(1) {
+ }
void Observe(int x) override {
- if (to_add_) {
- observer_list->AddObserver(to_add_);
- to_add_ = nullptr;
+ if (!added) {
+ added = true;
+ observer_list->AddObserver(&adder);
}
}
+ bool added;
ObserverListType* observer_list;
- Foo* to_add_;
+ Adder adder;
};
@@ -126,6 +112,8 @@ class AddRemoveThread : public PlatformThread::Delegate,
FROM_HERE,
base::Bind(&AddRemoveThread::AddTask, weak_factory_.GetWeakPtr()));
RunLoop().Run();
+ //LOG(ERROR) << "Loop 0x" << std::hex << loop_ << " done. " <<
+ // count_observes_ << ", " << count_addtask_;
delete loop_;
loop_ = reinterpret_cast<MessageLoop*>(0xdeadbeef);
delete this;
@@ -188,8 +176,6 @@ class AddRemoveThread : public PlatformThread::Delegate,
base::WeakPtrFactory<AddRemoveThread> weak_factory_;
};
-} // namespace
-
TEST(ObserverListTest, BasicTest) {
ObserverList<Foo> observer_list;
Adder a(1), b(-1), c(1), d(-1), e(-1);
@@ -201,8 +187,7 @@ TEST(ObserverListTest, BasicTest) {
EXPECT_TRUE(observer_list.HasObserver(&a));
EXPECT_FALSE(observer_list.HasObserver(&c));
- for (auto& observer : observer_list)
- observer.Observe(10);
+ FOR_EACH_OBSERVER(Foo, observer_list, Observe(10));
observer_list.AddObserver(&evil);
observer_list.AddObserver(&c);
@@ -211,8 +196,7 @@ TEST(ObserverListTest, BasicTest) {
// Removing an observer not in the list should do nothing.
observer_list.RemoveObserver(&e);
- for (auto& observer : observer_list)
- observer.Observe(10);
+ FOR_EACH_OBSERVER(Foo, observer_list, Observe(10));
EXPECT_EQ(20, a.total);
EXPECT_EQ(-20, b.total);
@@ -221,52 +205,6 @@ TEST(ObserverListTest, BasicTest) {
EXPECT_EQ(0, e.total);
}
-TEST(ObserverListTest, DisruptSelf) {
- ObserverList<Foo> observer_list;
- Adder a(1), b(-1), c(1), d(-1);
- Disrupter evil(&observer_list, true);
-
- observer_list.AddObserver(&a);
- observer_list.AddObserver(&b);
-
- for (auto& observer : observer_list)
- observer.Observe(10);
-
- observer_list.AddObserver(&evil);
- observer_list.AddObserver(&c);
- observer_list.AddObserver(&d);
-
- for (auto& observer : observer_list)
- observer.Observe(10);
-
- EXPECT_EQ(20, a.total);
- EXPECT_EQ(-20, b.total);
- EXPECT_EQ(10, c.total);
- EXPECT_EQ(-10, d.total);
-}
-
-TEST(ObserverListTest, DisruptBefore) {
- ObserverList<Foo> observer_list;
- Adder a(1), b(-1), c(1), d(-1);
- Disrupter evil(&observer_list, &b);
-
- observer_list.AddObserver(&a);
- observer_list.AddObserver(&b);
- observer_list.AddObserver(&evil);
- observer_list.AddObserver(&c);
- observer_list.AddObserver(&d);
-
- for (auto& observer : observer_list)
- observer.Observe(10);
- for (auto& observer : observer_list)
- observer.Observe(10);
-
- EXPECT_EQ(20, a.total);
- EXPECT_EQ(-10, b.total);
- EXPECT_EQ(20, c.total);
- EXPECT_EQ(-20, d.total);
-}
-
TEST(ObserverListThreadSafeTest, BasicTest) {
MessageLoop loop;
@@ -495,24 +433,20 @@ TEST(ObserverListTest, Existing) {
ObserverList<Foo> observer_list(ObserverList<Foo>::NOTIFY_EXISTING_ONLY);
Adder a(1);
AddInObserve<ObserverList<Foo> > b(&observer_list);
- Adder c(1);
- b.SetToAdd(&c);
observer_list.AddObserver(&a);
observer_list.AddObserver(&b);
- for (auto& observer : observer_list)
- observer.Observe(1);
+ FOR_EACH_OBSERVER(Foo, observer_list, Observe(1));
- EXPECT_FALSE(b.to_add_);
+ EXPECT_TRUE(b.added);
// B's adder should not have been notified because it was added during
// notification.
- EXPECT_EQ(0, c.total);
+ EXPECT_EQ(0, b.adder.total);
// Notify again to make sure b's adder is notified.
- for (auto& observer : observer_list)
- observer.Observe(1);
- EXPECT_EQ(1, c.total);
+ FOR_EACH_OBSERVER(Foo, observer_list, Observe(1));
+ EXPECT_EQ(1, b.adder.total);
}
// Same as above, but for ObserverListThreadSafe
@@ -522,8 +456,6 @@ TEST(ObserverListThreadSafeTest, Existing) {
new ObserverListThreadSafe<Foo>(ObserverList<Foo>::NOTIFY_EXISTING_ONLY));
Adder a(1);
AddInObserve<ObserverListThreadSafe<Foo> > b(observer_list.get());
- Adder c(1);
- b.SetToAdd(&c);
observer_list->AddObserver(&a);
observer_list->AddObserver(&b);
@@ -531,15 +463,15 @@ TEST(ObserverListThreadSafeTest, Existing) {
observer_list->Notify(FROM_HERE, &Foo::Observe, 1);
RunLoop().RunUntilIdle();
- EXPECT_FALSE(b.to_add_);
+ EXPECT_TRUE(b.added);
// B's adder should not have been notified because it was added during
// notification.
- EXPECT_EQ(0, c.total);
+ EXPECT_EQ(0, b.adder.total);
// Notify again to make sure b's adder is notified.
observer_list->Notify(FROM_HERE, &Foo::Observe, 1);
RunLoop().RunUntilIdle();
- EXPECT_EQ(1, c.total);
+ EXPECT_EQ(1, b.adder.total);
}
class AddInClearObserve : public Foo {
@@ -569,8 +501,7 @@ TEST(ObserverListTest, ClearNotifyAll) {
observer_list.AddObserver(&a);
- for (auto& observer : observer_list)
- observer.Observe(1);
+ FOR_EACH_OBSERVER(Foo, observer_list, Observe(1));
EXPECT_TRUE(a.added());
EXPECT_EQ(1, a.adder().total)
<< "Adder should observe once and have sum of 1.";
@@ -582,8 +513,7 @@ TEST(ObserverListTest, ClearNotifyExistingOnly) {
observer_list.AddObserver(&a);
- for (auto& observer : observer_list)
- observer.Observe(1);
+ FOR_EACH_OBSERVER(Foo, observer_list, Observe(1));
EXPECT_TRUE(a.added());
EXPECT_EQ(0, a.adder().total)
<< "Adder should not observe, so sum should still be 0.";
@@ -606,330 +536,10 @@ TEST(ObserverListTest, IteratorOutlivesList) {
ListDestructor a(observer_list);
observer_list->AddObserver(&a);
- for (auto& observer : *observer_list)
- observer.Observe(0);
+ FOR_EACH_OBSERVER(Foo, *observer_list, Observe(0));
// If this test fails, there'll be Valgrind errors when this function goes out
// of scope.
}
-TEST(ObserverListTest, BasicStdIterator) {
- using FooList = ObserverList<Foo>;
- FooList observer_list;
-
- // An optimization: begin() and end() do not involve weak pointers on
- // empty list.
- EXPECT_FALSE(observer_list.begin().list_);
- EXPECT_FALSE(observer_list.end().list_);
-
- // Iterate over empty list: no effect, no crash.
- for (auto& i : observer_list)
- i.Observe(10);
-
- Adder a(1), b(-1), c(1), d(-1);
-
- observer_list.AddObserver(&a);
- observer_list.AddObserver(&b);
- observer_list.AddObserver(&c);
- observer_list.AddObserver(&d);
-
- for (FooList::iterator i = observer_list.begin(), e = observer_list.end();
- i != e; ++i)
- i->Observe(1);
-
- EXPECT_EQ(1, a.total);
- EXPECT_EQ(-1, b.total);
- EXPECT_EQ(1, c.total);
- EXPECT_EQ(-1, d.total);
-
- // Check an iteration over a 'const view' for a given container.
- const FooList& const_list = observer_list;
- for (FooList::const_iterator i = const_list.begin(), e = const_list.end();
- i != e; ++i) {
- EXPECT_EQ(1, std::abs(i->GetValue()));
- }
-
- for (const auto& o : const_list)
- EXPECT_EQ(1, std::abs(o.GetValue()));
-}
-
-TEST(ObserverListTest, StdIteratorRemoveItself) {
- ObserverList<Foo> observer_list;
- Adder a(1), b(-1), c(1), d(-1);
- Disrupter disrupter(&observer_list, true);
-
- observer_list.AddObserver(&a);
- observer_list.AddObserver(&b);
- observer_list.AddObserver(&disrupter);
- observer_list.AddObserver(&c);
- observer_list.AddObserver(&d);
-
- for (auto& o : observer_list)
- o.Observe(1);
-
- for (auto& o : observer_list)
- o.Observe(10);
-
- EXPECT_EQ(11, a.total);
- EXPECT_EQ(-11, b.total);
- EXPECT_EQ(11, c.total);
- EXPECT_EQ(-11, d.total);
-}
-
-TEST(ObserverListTest, StdIteratorRemoveBefore) {
- ObserverList<Foo> observer_list;
- Adder a(1), b(-1), c(1), d(-1);
- Disrupter disrupter(&observer_list, &b);
-
- observer_list.AddObserver(&a);
- observer_list.AddObserver(&b);
- observer_list.AddObserver(&disrupter);
- observer_list.AddObserver(&c);
- observer_list.AddObserver(&d);
-
- for (auto& o : observer_list)
- o.Observe(1);
-
- for (auto& o : observer_list)
- o.Observe(10);
-
- EXPECT_EQ(11, a.total);
- EXPECT_EQ(-1, b.total);
- EXPECT_EQ(11, c.total);
- EXPECT_EQ(-11, d.total);
-}
-
-TEST(ObserverListTest, StdIteratorRemoveAfter) {
- ObserverList<Foo> observer_list;
- Adder a(1), b(-1), c(1), d(-1);
- Disrupter disrupter(&observer_list, &c);
-
- observer_list.AddObserver(&a);
- observer_list.AddObserver(&b);
- observer_list.AddObserver(&disrupter);
- observer_list.AddObserver(&c);
- observer_list.AddObserver(&d);
-
- for (auto& o : observer_list)
- o.Observe(1);
-
- for (auto& o : observer_list)
- o.Observe(10);
-
- EXPECT_EQ(11, a.total);
- EXPECT_EQ(-11, b.total);
- EXPECT_EQ(0, c.total);
- EXPECT_EQ(-11, d.total);
-}
-
-TEST(ObserverListTest, StdIteratorRemoveAfterFront) {
- ObserverList<Foo> observer_list;
- Adder a(1), b(-1), c(1), d(-1);
- Disrupter disrupter(&observer_list, &a);
-
- observer_list.AddObserver(&a);
- observer_list.AddObserver(&disrupter);
- observer_list.AddObserver(&b);
- observer_list.AddObserver(&c);
- observer_list.AddObserver(&d);
-
- for (auto& o : observer_list)
- o.Observe(1);
-
- for (auto& o : observer_list)
- o.Observe(10);
-
- EXPECT_EQ(1, a.total);
- EXPECT_EQ(-11, b.total);
- EXPECT_EQ(11, c.total);
- EXPECT_EQ(-11, d.total);
-}
-
-TEST(ObserverListTest, StdIteratorRemoveBeforeBack) {
- ObserverList<Foo> observer_list;
- Adder a(1), b(-1), c(1), d(-1);
- Disrupter disrupter(&observer_list, &d);
-
- observer_list.AddObserver(&a);
- observer_list.AddObserver(&b);
- observer_list.AddObserver(&c);
- observer_list.AddObserver(&disrupter);
- observer_list.AddObserver(&d);
-
- for (auto& o : observer_list)
- o.Observe(1);
-
- for (auto& o : observer_list)
- o.Observe(10);
-
- EXPECT_EQ(11, a.total);
- EXPECT_EQ(-11, b.total);
- EXPECT_EQ(11, c.total);
- EXPECT_EQ(0, d.total);
-}
-
-TEST(ObserverListTest, StdIteratorRemoveFront) {
- using FooList = ObserverList<Foo>;
- FooList observer_list;
- Adder a(1), b(-1), c(1), d(-1);
- Disrupter disrupter(&observer_list, true);
-
- observer_list.AddObserver(&disrupter);
- observer_list.AddObserver(&a);
- observer_list.AddObserver(&b);
- observer_list.AddObserver(&c);
- observer_list.AddObserver(&d);
-
- bool test_disruptor = true;
- for (FooList::iterator i = observer_list.begin(), e = observer_list.end();
- i != e; ++i) {
- i->Observe(1);
- // Check that second call to i->Observe() would crash here.
- if (test_disruptor) {
- EXPECT_FALSE(i.GetCurrent());
- test_disruptor = false;
- }
- }
-
- for (auto& o : observer_list)
- o.Observe(10);
-
- EXPECT_EQ(11, a.total);
- EXPECT_EQ(-11, b.total);
- EXPECT_EQ(11, c.total);
- EXPECT_EQ(-11, d.total);
-}
-
-TEST(ObserverListTest, StdIteratorRemoveBack) {
- ObserverList<Foo> observer_list;
- Adder a(1), b(-1), c(1), d(-1);
- Disrupter disrupter(&observer_list, true);
-
- observer_list.AddObserver(&a);
- observer_list.AddObserver(&b);
- observer_list.AddObserver(&c);
- observer_list.AddObserver(&d);
- observer_list.AddObserver(&disrupter);
-
- for (auto& o : observer_list)
- o.Observe(1);
-
- for (auto& o : observer_list)
- o.Observe(10);
-
- EXPECT_EQ(11, a.total);
- EXPECT_EQ(-11, b.total);
- EXPECT_EQ(11, c.total);
- EXPECT_EQ(-11, d.total);
-}
-
-TEST(ObserverListTest, NestedLoop) {
- ObserverList<Foo> observer_list;
- Adder a(1), b(-1), c(1), d(-1);
- Disrupter disrupter(&observer_list, true);
-
- observer_list.AddObserver(&disrupter);
- observer_list.AddObserver(&a);
- observer_list.AddObserver(&b);
- observer_list.AddObserver(&c);
- observer_list.AddObserver(&d);
-
- for (auto& o : observer_list) {
- o.Observe(10);
-
- for (auto& o : observer_list)
- o.Observe(1);
- }
-
- EXPECT_EQ(15, a.total);
- EXPECT_EQ(-15, b.total);
- EXPECT_EQ(15, c.total);
- EXPECT_EQ(-15, d.total);
-}
-
-TEST(ObserverListTest, NonCompactList) {
- ObserverList<Foo> observer_list;
- Adder a(1), b(-1);
-
- Disrupter disrupter1(&observer_list, true);
- Disrupter disrupter2(&observer_list, true);
-
- // Disrupt itself and another one.
- disrupter1.SetDoomed(&disrupter2);
-
- observer_list.AddObserver(&disrupter1);
- observer_list.AddObserver(&disrupter2);
- observer_list.AddObserver(&a);
- observer_list.AddObserver(&b);
-
- for (auto& o : observer_list) {
- // Get the { nullptr, nullptr, &a, &b } non-compact list
- // on the first inner pass.
- o.Observe(10);
-
- for (auto& o : observer_list)
- o.Observe(1);
- }
-
- EXPECT_EQ(13, a.total);
- EXPECT_EQ(-13, b.total);
-}
-
-TEST(ObserverListTest, BecomesEmptyThanNonEmpty) {
- ObserverList<Foo> observer_list;
- Adder a(1), b(-1);
-
- Disrupter disrupter1(&observer_list, true);
- Disrupter disrupter2(&observer_list, true);
-
- // Disrupt itself and another one.
- disrupter1.SetDoomed(&disrupter2);
-
- observer_list.AddObserver(&disrupter1);
- observer_list.AddObserver(&disrupter2);
-
- bool add_observers = true;
- for (auto& o : observer_list) {
- // Get the { nullptr, nullptr } empty list on the first inner pass.
- o.Observe(10);
-
- for (auto& o : observer_list)
- o.Observe(1);
-
- if (add_observers) {
- observer_list.AddObserver(&a);
- observer_list.AddObserver(&b);
- add_observers = false;
- }
- }
-
- EXPECT_EQ(12, a.total);
- EXPECT_EQ(-12, b.total);
-}
-
-TEST(ObserverListTest, AddObserverInTheLastObserve) {
- using FooList = ObserverList<Foo>;
- FooList observer_list;
-
- AddInObserve<FooList> a(&observer_list);
- Adder b(-1);
-
- a.SetToAdd(&b);
- observer_list.AddObserver(&a);
-
- auto it = observer_list.begin();
- while (it != observer_list.end()) {
- auto& observer = *it;
- // Intentionally increment the iterator before calling Observe(). The
- // ObserverList starts with only one observer, and it == observer_list.end()
- // should be true after the next line.
- ++it;
- // However, the first Observe() call will add a second observer: at this
- // point, it != observer_list.end() should be true, and Observe() should be
- // called on the newly added observer on the next iteration of the loop.
- observer.Observe(10);
- }
-
- EXPECT_EQ(-10, b.total);
-}
-
+} // namespace
} // namespace base
diff --git a/base/optional.h b/base/optional.h
index cf65ad7dac..b468964ae3 100644
--- a/base/optional.h
+++ b/base/optional.h
@@ -8,6 +8,7 @@
#include <type_traits>
#include "base/logging.h"
+#include "base/memory/aligned_memory.h"
#include "base/template_util.h"
namespace base {
@@ -34,70 +35,28 @@ namespace internal {
template <typename T, bool = base::is_trivially_destructible<T>::value>
struct OptionalStorage {
- // Initializing |empty_| here instead of using default member initializing
- // to avoid errors in g++ 4.8.
- constexpr OptionalStorage() : empty_('\0') {}
-
- constexpr explicit OptionalStorage(const T& value)
- : is_null_(false), value_(value) {}
-
- // TODO(alshabalin): Can't use 'constexpr' with std::move until C++14.
- explicit OptionalStorage(T&& value)
- : is_null_(false), value_(std::move(value)) {}
-
- // TODO(alshabalin): Can't use 'constexpr' with std::forward until C++14.
- template <class... Args>
- explicit OptionalStorage(base::in_place_t, Args&&... args)
- : is_null_(false), value_(std::forward<Args>(args)...) {}
-
// When T is not trivially destructible we must call its
// destructor before deallocating its memory.
~OptionalStorage() {
if (!is_null_)
- value_.~T();
+ buffer_.template data_as<T>()->~T();
}
bool is_null_ = true;
- union {
- // |empty_| exists so that the union will always be initialized, even when
- // it doesn't contain a value. Union members must be initialized for the
- // constructor to be 'constexpr'.
- char empty_;
- T value_;
- };
+ base::AlignedMemory<sizeof(T), ALIGNOF(T)> buffer_;
};
template <typename T>
struct OptionalStorage<T, true> {
- // Initializing |empty_| here instead of using default member initializing
- // to avoid errors in g++ 4.8.
- constexpr OptionalStorage() : empty_('\0') {}
-
- constexpr explicit OptionalStorage(const T& value)
- : is_null_(false), value_(value) {}
-
- // TODO(alshabalin): Can't use 'constexpr' with std::move until C++14.
- explicit OptionalStorage(T&& value)
- : is_null_(false), value_(std::move(value)) {}
-
- // TODO(alshabalin): Can't use 'constexpr' with std::forward until C++14.
- template <class... Args>
- explicit OptionalStorage(base::in_place_t, Args&&... args)
- : is_null_(false), value_(std::forward<Args>(args)...) {}
-
- // When T is trivially destructible (i.e. its destructor does nothing) there
- // is no need to call it. Explicitly defaulting the destructor means it's not
- // user-provided. Those two together make this destructor trivial.
+ // When T is trivially destructible (i.e. its destructor does nothing)
+ // there is no need to call it.
+ // Since |base::AlignedMemory| is just an array its destructor
+ // is trivial. Explicitly defaulting the destructor means it's not
+ // user-provided. All of this together make this destructor trivial.
~OptionalStorage() = default;
bool is_null_ = true;
- union {
- // |empty_| exists so that the union will always be initialized, even when
- // it doesn't contain a value. Union members must be initialized for the
- // constructor to be 'constexpr'.
- char empty_;
- T value_;
- };
+ base::AlignedMemory<sizeof(T), ALIGNOF(T)> buffer_;
};
} // namespace internal
@@ -120,9 +79,8 @@ class Optional {
public:
using value_type = T;
- constexpr Optional() {}
-
- constexpr Optional(base::nullopt_t) {}
+ constexpr Optional() = default;
+ Optional(base::nullopt_t) : Optional() {}
Optional(const Optional& other) {
if (!other.storage_.is_null_)
@@ -134,15 +92,14 @@ class Optional {
Init(std::move(other.value()));
}
- constexpr Optional(const T& value) : storage_(value) {}
+ Optional(const T& value) { Init(value); }
- // TODO(alshabalin): Can't use 'constexpr' with std::move until C++14.
- Optional(T&& value) : storage_(std::move(value)) {}
+ Optional(T&& value) { Init(std::move(value)); }
- // TODO(alshabalin): Can't use 'constexpr' with std::forward until C++14.
template <class... Args>
- explicit Optional(base::in_place_t, Args&&... args)
- : storage_(base::in_place, std::forward<Args>(args)...) {}
+ explicit Optional(base::in_place_t, Args&&... args) {
+ emplace(std::forward<Args>(args)...);
+ }
~Optional() = default;
@@ -206,32 +163,30 @@ class Optional {
constexpr explicit operator bool() const { return !storage_.is_null_; }
- constexpr bool has_value() const { return !storage_.is_null_; }
-
// TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
// meant to be 'constexpr const'.
T& value() & {
DCHECK(!storage_.is_null_);
- return storage_.value_;
+ return *storage_.buffer_.template data_as<T>();
}
// TODO(mlamouri): can't use 'constexpr' with DCHECK.
const T& value() const& {
DCHECK(!storage_.is_null_);
- return storage_.value_;
+ return *storage_.buffer_.template data_as<T>();
}
// TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
// meant to be 'constexpr const'.
T&& value() && {
DCHECK(!storage_.is_null_);
- return std::move(storage_.value_);
+ return std::move(*storage_.buffer_.template data_as<T>());
}
// TODO(mlamouri): can't use 'constexpr' with DCHECK.
const T&& value() const&& {
DCHECK(!storage_.is_null_);
- return std::move(storage_.value_);
+ return std::move(*storage_.buffer_.template data_as<T>());
}
template <class U>
@@ -262,10 +217,10 @@ class Optional {
if (storage_.is_null_ != other.storage_.is_null_) {
if (storage_.is_null_) {
- Init(std::move(other.storage_.value_));
+ Init(std::move(*other.storage_.buffer_.template data_as<T>()));
other.FreeIfNeeded();
} else {
- other.Init(std::move(storage_.value_));
+ other.Init(std::move(*storage_.buffer_.template data_as<T>()));
FreeIfNeeded();
}
return;
@@ -276,10 +231,6 @@ class Optional {
swap(**this, *other);
}
- void reset() {
- FreeIfNeeded();
- }
-
template <class... Args>
void emplace(Args&&... args) {
FreeIfNeeded();
@@ -289,20 +240,20 @@ class Optional {
private:
void Init(const T& value) {
DCHECK(storage_.is_null_);
- new (&storage_.value_) T(value);
+ new (storage_.buffer_.void_data()) T(value);
storage_.is_null_ = false;
}
void Init(T&& value) {
DCHECK(storage_.is_null_);
- new (&storage_.value_) T(std::move(value));
+ new (storage_.buffer_.void_data()) T(std::move(value));
storage_.is_null_ = false;
}
template <class... Args>
void Init(Args&&... args) {
DCHECK(storage_.is_null_);
- new (&storage_.value_) T(std::forward<Args>(args)...);
+ new (storage_.buffer_.void_data()) T(std::forward<Args>(args)...);
storage_.is_null_ = false;
}
@@ -310,20 +261,20 @@ class Optional {
if (storage_.is_null_)
Init(value);
else
- storage_.value_ = value;
+ *storage_.buffer_.template data_as<T>() = value;
}
void InitOrAssign(T&& value) {
if (storage_.is_null_)
Init(std::move(value));
else
- storage_.value_ = std::move(value);
+ *storage_.buffer_.template data_as<T>() = std::move(value);
}
void FreeIfNeeded() {
if (storage_.is_null_)
return;
- storage_.value_.~T();
+ storage_.buffer_.template data_as<T>()->~T();
storage_.is_null_ = true;
}
diff --git a/base/optional_unittest.cc b/base/optional_unittest.cc
index 83025e8bda..d6bf263691 100644
--- a/base/optional_unittest.cc
+++ b/base/optional_unittest.cc
@@ -98,7 +98,7 @@ static_assert(
TEST(OptionalTest, DefaultConstructor) {
{
- constexpr Optional<float> o;
+ Optional<float> o;
EXPECT_FALSE(o);
}
@@ -144,28 +144,21 @@ TEST(OptionalTest, CopyConstructor) {
TEST(OptionalTest, ValueConstructor) {
{
- constexpr float value = 0.1f;
- constexpr Optional<float> o(value);
-
+ Optional<float> o(0.1f);
EXPECT_TRUE(o);
- EXPECT_EQ(value, o.value());
+ EXPECT_EQ(o.value(), 0.1f);
}
{
- std::string value("foo");
- Optional<std::string> o(value);
-
+ Optional<std::string> o("foo");
EXPECT_TRUE(o);
- EXPECT_EQ(value, o.value());
+ EXPECT_EQ(o.value(), "foo");
}
{
- TestObject value(3, 0.1);
- Optional<TestObject> o(value);
-
- EXPECT_TRUE(o);
- EXPECT_EQ(TestObject::State::COPY_CONSTRUCTED, o->state());
- EXPECT_EQ(value, o.value());
+ Optional<TestObject> o(TestObject(3, 0.1));
+ EXPECT_TRUE(!!o);
+ EXPECT_TRUE(o.value() == TestObject(3, 0.1));
}
}
@@ -205,28 +198,35 @@ TEST(OptionalTest, MoveConstructor) {
TEST(OptionalTest, MoveValueConstructor) {
{
- float value = 0.1f;
- Optional<float> o(std::move(value));
+ Optional<float> first(0.1f);
+ Optional<float> second(std::move(first.value()));
- EXPECT_TRUE(o);
- EXPECT_EQ(0.1f, o.value());
+ EXPECT_TRUE(second);
+ EXPECT_EQ(second.value(), 0.1f);
+
+ EXPECT_TRUE(first);
}
{
- std::string value("foo");
- Optional<std::string> o(std::move(value));
+ Optional<std::string> first("foo");
+ Optional<std::string> second(std::move(first.value()));
- EXPECT_TRUE(o);
- EXPECT_EQ("foo", o.value());
+ EXPECT_TRUE(second);
+ EXPECT_EQ("foo", second.value());
+
+ EXPECT_TRUE(first);
}
{
- TestObject value(3, 0.1);
- Optional<TestObject> o(std::move(value));
+ Optional<TestObject> first(TestObject(3, 0.1));
+ Optional<TestObject> second(std::move(first.value()));
- EXPECT_TRUE(o);
- EXPECT_EQ(TestObject::State::MOVE_CONSTRUCTED, o->state());
- EXPECT_EQ(TestObject(3, 0.1), o.value());
+ EXPECT_TRUE(!!second);
+ EXPECT_EQ(TestObject::State::MOVE_CONSTRUCTED, second->state());
+ EXPECT_TRUE(TestObject(3, 0.1) == second.value());
+
+ EXPECT_TRUE(!!first);
+ EXPECT_EQ(TestObject::State::MOVED_FROM, first->state());
}
}
@@ -251,7 +251,7 @@ TEST(OptionalTest, ConstructorForwardArguments) {
}
TEST(OptionalTest, NulloptConstructor) {
- constexpr Optional<int> a(base::nullopt);
+ Optional<int> a = base::nullopt;
EXPECT_FALSE(a);
}
@@ -1298,49 +1298,4 @@ TEST(OptionalTest, Hash_UseInSet) {
EXPECT_NE(setOptInt.end(), setOptInt.find(3));
}
-TEST(OptionalTest, HasValue) {
- Optional<int> a;
- EXPECT_FALSE(a.has_value());
-
- a = 42;
- EXPECT_TRUE(a.has_value());
-
- a = nullopt;
- EXPECT_FALSE(a.has_value());
-
- a = 0;
- EXPECT_TRUE(a.has_value());
-
- a = Optional<int>();
- EXPECT_FALSE(a.has_value());
-}
-
-TEST(OptionalTest, Reset_int) {
- Optional<int> a(0);
- EXPECT_TRUE(a.has_value());
- EXPECT_EQ(0, a.value());
-
- a.reset();
- EXPECT_FALSE(a.has_value());
- EXPECT_EQ(-1, a.value_or(-1));
-}
-
-TEST(OptionalTest, Reset_Object) {
- Optional<TestObject> a(TestObject(0, 0.1));
- EXPECT_TRUE(a.has_value());
- EXPECT_EQ(TestObject(0, 0.1), a.value());
-
- a.reset();
- EXPECT_FALSE(a.has_value());
- EXPECT_EQ(TestObject(42, 0.0), a.value_or(TestObject(42, 0.0)));
-}
-
-TEST(OptionalTest, Reset_NoOp) {
- Optional<int> a;
- EXPECT_FALSE(a.has_value());
-
- a.reset();
- EXPECT_FALSE(a.has_value());
-}
-
} // namespace base
diff --git a/base/pending_task.cc b/base/pending_task.cc
index b2f95b4c45..73834bd460 100644
--- a/base/pending_task.cc
+++ b/base/pending_task.cc
@@ -4,17 +4,22 @@
#include "base/pending_task.h"
-#include "base/message_loop/message_loop.h"
#include "base/tracked_objects.h"
namespace base {
PendingTask::PendingTask(const tracked_objects::Location& posted_from,
- OnceClosure task)
- : PendingTask(posted_from, std::move(task), TimeTicks(), true) {}
+ base::Closure task)
+ : base::TrackingInfo(posted_from, TimeTicks()),
+ task(std::move(task)),
+ posted_from(posted_from),
+ sequence_num(0),
+ nestable(true),
+ is_high_res(false) {
+}
PendingTask::PendingTask(const tracked_objects::Location& posted_from,
- OnceClosure task,
+ base::Closure task,
TimeTicks delayed_run_time,
bool nestable)
: base::TrackingInfo(posted_from, delayed_run_time),
@@ -23,17 +28,6 @@ PendingTask::PendingTask(const tracked_objects::Location& posted_from,
sequence_num(0),
nestable(nestable),
is_high_res(false) {
- const PendingTask* parent_task =
- MessageLoop::current() ? MessageLoop::current()->current_pending_task_
- : nullptr;
- if (parent_task) {
- task_backtrace[0] = parent_task->posted_from.program_counter();
- std::copy(parent_task->task_backtrace.begin(),
- parent_task->task_backtrace.end() - 1,
- task_backtrace.begin() + 1);
- } else {
- task_backtrace.fill(nullptr);
- }
}
PendingTask::PendingTask(PendingTask&& other) = default;
diff --git a/base/pending_task.h b/base/pending_task.h
index 7f3fccd882..5761653397 100644
--- a/base/pending_task.h
+++ b/base/pending_task.h
@@ -5,7 +5,6 @@
#ifndef BASE_PENDING_TASK_H_
#define BASE_PENDING_TASK_H_
-#include <array>
#include <queue>
#include "base/base_export.h"
@@ -19,9 +18,10 @@ namespace base {
// Contains data about a pending task. Stored in TaskQueue and DelayedTaskQueue
// for use by classes that queue and execute tasks.
struct BASE_EXPORT PendingTask : public TrackingInfo {
- PendingTask(const tracked_objects::Location& posted_from, OnceClosure task);
PendingTask(const tracked_objects::Location& posted_from,
- OnceClosure task,
+ Closure task);
+ PendingTask(const tracked_objects::Location& posted_from,
+ Closure task,
TimeTicks delayed_run_time,
bool nestable);
PendingTask(PendingTask&& other);
@@ -33,14 +33,11 @@ struct BASE_EXPORT PendingTask : public TrackingInfo {
bool operator<(const PendingTask& other) const;
// The task to run.
- OnceClosure task;
+ Closure task;
// The site this PendingTask was posted from.
tracked_objects::Location posted_from;
- // Task backtrace.
- std::array<const void*, 4> task_backtrace;
-
// Secondary sort key for run time.
int sequence_num;
diff --git a/base/pickle.cc b/base/pickle.cc
index 0079b3979b..4ef167b089 100644
--- a/base/pickle.cc
+++ b/base/pickle.cc
@@ -233,6 +233,7 @@ void PickleSizer::AddBytes(int length) {
void PickleSizer::AddAttachment() {
// From IPC::Message::WriteAttachment
+ AddBool();
AddInt();
}
diff --git a/base/posix/global_descriptors.cc b/base/posix/global_descriptors.cc
index 8da808e52d..6c187838ad 100644
--- a/base/posix/global_descriptors.cc
+++ b/base/posix/global_descriptors.cc
@@ -47,22 +47,6 @@ int GlobalDescriptors::MaybeGet(Key key) const {
return -1;
}
-base::ScopedFD GlobalDescriptors::TakeFD(
- Key key,
- base::MemoryMappedFile::Region* region) {
- base::ScopedFD fd;
- for (Mapping::iterator i = descriptors_.begin(); i != descriptors_.end();
- ++i) {
- if (i->key == key) {
- *region = i->region;
- fd.reset(i->fd);
- descriptors_.erase(i);
- break;
- }
- }
- return fd;
-}
-
void GlobalDescriptors::Set(Key key, int fd) {
Set(key, fd, base::MemoryMappedFile::Region::kWholeFile);
}
diff --git a/base/posix/global_descriptors.h b/base/posix/global_descriptors.h
index 9d68761f23..edb299de5c 100644
--- a/base/posix/global_descriptors.h
+++ b/base/posix/global_descriptors.h
@@ -13,7 +13,6 @@
#include <stdint.h>
#include "base/files/memory_mapped_file.h"
-#include "base/files/scoped_file.h"
#include "base/memory/singleton.h"
namespace base {
@@ -35,10 +34,6 @@ namespace base {
// It maps from an abstract key to a descriptor. If independent modules each
// need to define keys, then values should be chosen randomly so as not to
// collide.
-//
-// Note that this class is deprecated and passing file descriptor should ideally
-// be done through the command line and using FileDescriptorStore.
-// See https://crbugs.com/detail?id=692619
class BASE_EXPORT GlobalDescriptors {
public:
typedef uint32_t Key;
@@ -57,7 +52,18 @@ class BASE_EXPORT GlobalDescriptors {
// Often we want a canonical descriptor for a given Key. In this case, we add
// the following constant to the key value:
+#if !defined(OS_ANDROID)
static const int kBaseDescriptor = 3; // 0, 1, 2 are already taken.
+#else
+ // 3 used by __android_log_write().
+ // 4 used by... something important on Android M.
+ // 5 used by... something important on Android L... on low-end devices.
+ // TODO(amistry): An Android, this mechanism is only used for tests since the
+ // content child launcher spawns a process by creating a new Activity using
+ // the Android APIs. For tests, come up with a way that doesn't require using
+ // a pre-defined fd.
+ static const int kBaseDescriptor = 6;
+#endif
// Return the singleton instance of GlobalDescriptors.
static GlobalDescriptors* GetInstance();
@@ -68,11 +74,6 @@ class BASE_EXPORT GlobalDescriptors {
// Get a descriptor given a key. Returns -1 on error.
int MaybeGet(Key key) const;
- // Returns a descriptor given a key and removes it from this class mappings.
- // Also populates |region|.
- // It is a fatal error if the key is not known.
- base::ScopedFD TakeFD(Key key, base::MemoryMappedFile::Region* region);
-
// Get a region given a key. It is a fatal error if the key is not known.
base::MemoryMappedFile::Region GetRegion(Key key) const;
diff --git a/base/post_task_and_reply_with_result_internal.h b/base/post_task_and_reply_with_result_internal.h
deleted file mode 100644
index 1456129324..0000000000
--- a/base/post_task_and_reply_with_result_internal.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2016 The Chromium 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_POST_TASK_AND_REPLY_WITH_RESULT_INTERNAL_H_
-#define BASE_POST_TASK_AND_REPLY_WITH_RESULT_INTERNAL_H_
-
-#include <utility>
-
-#include "base/callback.h"
-
-namespace base {
-
-namespace internal {
-
-// Adapts a function that produces a result via a return value to
-// one that returns via an output parameter.
-template <typename ReturnType>
-void ReturnAsParamAdapter(const Callback<ReturnType(void)>& func,
- ReturnType* result) {
- *result = func.Run();
-}
-
-// Adapts a T* result to a callblack that expects a T.
-template <typename TaskReturnType, typename ReplyArgType>
-void ReplyAdapter(const Callback<void(ReplyArgType)>& callback,
- TaskReturnType* result) {
- callback.Run(std::move(*result));
-}
-
-} // namespace internal
-
-} // namespace base
-
-#endif // BASE_POST_TASK_AND_REPLY_WITH_RESULT_INTERNAL_H_
diff --git a/base/power_monitor/power_monitor_device_source.h b/base/power_monitor/power_monitor_device_source.h
index 4ecd2d981b..2dabac8865 100644
--- a/base/power_monitor/power_monitor_device_source.h
+++ b/base/power_monitor/power_monitor_device_source.h
@@ -7,6 +7,8 @@
#include "base/base_export.h"
#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/observer_list_threadsafe.h"
#include "base/power_monitor/power_monitor_source.h"
#include "base/power_monitor/power_observer.h"
#include "build/build_config.h"
@@ -14,8 +16,17 @@
#if defined(OS_WIN)
#include <windows.h>
+// Windows HiRes timers drain the battery faster so we need to know the battery
+// status. This isn't true for other platforms.
+#define ENABLE_BATTERY_MONITORING 1
+#else
+#undef ENABLE_BATTERY_MONITORING
#endif // !OS_WIN
+#if defined(ENABLE_BATTERY_MONITORING)
+#include "base/timer/timer.h"
+#endif // defined(ENABLE_BATTERY_MONITORING)
+
#if defined(OS_IOS)
#include <objc/runtime.h>
#endif // OS_IOS
@@ -82,11 +93,19 @@ class BASE_EXPORT PowerMonitorDeviceSource : public PowerMonitorSource {
// false otherwise.
bool IsOnBatteryPowerImpl() override;
+ // Checks the battery status and notifies observers if the battery
+ // status has changed.
+ void BatteryCheck();
+
#if defined(OS_IOS)
// Holds pointers to system event notification observers.
std::vector<id> notification_observers_;
#endif
+#if defined(ENABLE_BATTERY_MONITORING)
+ base::OneShotTimer delayed_battery_check_;
+#endif
+
#if defined(OS_WIN)
PowerMessageWindow power_message_window_;
#endif
diff --git a/base/power_monitor/power_monitor_source.h b/base/power_monitor/power_monitor_source.h
index b69cbf8317..e63f4f82bf 100644
--- a/base/power_monitor/power_monitor_source.h
+++ b/base/power_monitor/power_monitor_source.h
@@ -49,14 +49,9 @@ class BASE_EXPORT PowerMonitorSource {
// false otherwise.
virtual bool IsOnBatteryPowerImpl() = 0;
- // Sets the initial state for |on_battery_power_|, which defaults to false
- // since not all implementations can provide the value at construction. May
- // only be called before a base::PowerMonitor has been created.
- void SetInitialOnBatteryPowerState(bool on_battery_power);
-
private:
- bool on_battery_power_ = false;
- bool suspended_ = false;
+ bool on_battery_power_;
+ bool suspended_;
// This lock guards access to on_battery_power_, to ensure that
// IsOnBatteryPower can be called from any thread.
diff --git a/base/process/internal_linux.cc b/base/process/internal_linux.cc
index c7820040ce..d286f4e753 100644
--- a/base/process/internal_linux.cc
+++ b/base/process/internal_linux.cc
@@ -133,10 +133,9 @@ size_t GetProcStatsFieldAsSizeT(const std::vector<std::string>& proc_stats,
return StringToSizeT(proc_stats[field_num], &value) ? value : 0;
}
-int64_t ReadStatFileAndGetFieldAsInt64(const FilePath& stat_file,
- ProcStatsFields field_num) {
+int64_t ReadProcStatsAndGetFieldAsInt64(pid_t pid, ProcStatsFields field_num) {
std::string stats_data;
- if (!ReadProcFile(stat_file, &stats_data))
+ if (!ReadProcStats(pid, &stats_data))
return 0;
std::vector<std::string> proc_stats;
if (!ParseProcStats(stats_data, &proc_stats))
@@ -144,16 +143,6 @@ int64_t ReadStatFileAndGetFieldAsInt64(const FilePath& stat_file,
return GetProcStatsFieldAsInt64(proc_stats, field_num);
}
-int64_t ReadProcStatsAndGetFieldAsInt64(pid_t pid, ProcStatsFields field_num) {
- FilePath stat_file = internal::GetProcPidDir(pid).Append(kStatFile);
- return ReadStatFileAndGetFieldAsInt64(stat_file, field_num);
-}
-
-int64_t ReadProcSelfStatsAndGetFieldAsInt64(ProcStatsFields field_num) {
- FilePath stat_file = FilePath(kProcDir).Append("self").Append(kStatFile);
- return ReadStatFileAndGetFieldAsInt64(stat_file, field_num);
-}
-
size_t ReadProcStatsAndGetFieldAsSizeT(pid_t pid,
ProcStatsFields field_num) {
std::string stats_data;
@@ -181,32 +170,6 @@ Time GetBootTime() {
return Time::FromTimeT(btime);
}
-TimeDelta GetUserCpuTimeSinceBoot() {
- FilePath path("/proc/stat");
- std::string contents;
- if (!ReadProcFile(path, &contents))
- return TimeDelta();
-
- ProcStatMap proc_stat;
- ParseProcStat(contents, &proc_stat);
- ProcStatMap::const_iterator cpu_it = proc_stat.find("cpu");
- if (cpu_it == proc_stat.end())
- return TimeDelta();
-
- std::vector<std::string> cpu = SplitString(
- cpu_it->second, kWhitespaceASCII, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
-
- if (cpu.size() < 2 || cpu[0] != "cpu")
- return TimeDelta();
-
- uint64_t user;
- uint64_t nice;
- if (!StringToUint64(cpu[0], &user) || !StringToUint64(cpu[1], &nice))
- return TimeDelta();
-
- return ClockTicksToTimeDelta(user + nice);
-}
-
TimeDelta ClockTicksToTimeDelta(int clock_ticks) {
// This queries the /proc-specific scaling factor which is
// conceptually the system hertz. To dump this value on another
diff --git a/base/process/internal_linux.h b/base/process/internal_linux.h
index 99d0fd5af1..ba793f7cc7 100644
--- a/base/process/internal_linux.h
+++ b/base/process/internal_linux.h
@@ -72,12 +72,9 @@ int64_t GetProcStatsFieldAsInt64(const std::vector<std::string>& proc_stats,
size_t GetProcStatsFieldAsSizeT(const std::vector<std::string>& proc_stats,
ProcStatsFields field_num);
-// Convenience wrappers around GetProcStatsFieldAsInt64(), ParseProcStats() and
+// Convenience wrapper around GetProcStatsFieldAsInt64(), ParseProcStats() and
// ReadProcStats(). See GetProcStatsFieldAsInt64() for details.
-int64_t ReadStatsFilendGetFieldAsInt64(const FilePath& stat_file,
- ProcStatsFields field_num);
int64_t ReadProcStatsAndGetFieldAsInt64(pid_t pid, ProcStatsFields field_num);
-int64_t ReadProcSelfStatsAndGetFieldAsInt64(ProcStatsFields field_num);
// Same as ReadProcStatsAndGetFieldAsInt64() but for size_t values.
size_t ReadProcStatsAndGetFieldAsSizeT(pid_t pid,
@@ -86,9 +83,6 @@ size_t ReadProcStatsAndGetFieldAsSizeT(pid_t pid,
// Returns the time that the OS started. Clock ticks are relative to this.
Time GetBootTime();
-// Returns the amount of time spent in user space since boot across all CPUs.
-TimeDelta GetUserCpuTimeSinceBoot();
-
// Converts Linux clock ticks to a wall time delta.
TimeDelta ClockTicksToTimeDelta(int clock_ticks);
diff --git a/base/process/kill.h b/base/process/kill.h
index 6d410e02a0..c664f33262 100644
--- a/base/process/kill.h
+++ b/base/process/kill.h
@@ -18,16 +18,6 @@ namespace base {
class ProcessFilter;
-#if defined(OS_WIN)
-namespace win {
-
-// See definition in sandbox/win/src/sandbox_types.h
-const DWORD kSandboxFatalMemoryExceeded = 7012;
-
-} // namespace win
-
-#endif // OS_WIN
-
// Return status values from GetTerminationStatus. Don't use these as
// exit code arguments to KillProcess*(), use platform/application
// specific values instead.
@@ -49,7 +39,6 @@ enum TerminationStatus {
TERMINATION_STATUS_OOM_PROTECTED, // child was protected from oom kill
#endif
TERMINATION_STATUS_LAUNCH_FAILED, // child process never launched
- TERMINATION_STATUS_OOM, // Process died due to oom
TERMINATION_STATUS_MAX_ENUM
};
diff --git a/base/process/kill_posix.cc b/base/process/kill_posix.cc
index 4dc60ef2a2..85470e05f9 100644
--- a/base/process/kill_posix.cc
+++ b/base/process/kill_posix.cc
@@ -52,8 +52,6 @@ TerminationStatus GetTerminationStatusImpl(ProcessHandle handle,
case SIGFPE:
case SIGILL:
case SIGSEGV:
- case SIGTRAP:
- case SIGSYS:
return TERMINATION_STATUS_PROCESS_CRASHED;
case SIGKILL:
#if defined(OS_CHROMEOS)
diff --git a/base/process/launch.cc b/base/process/launch.cc
index c03e1a75db..3ca5155a12 100644
--- a/base/process/launch.cc
+++ b/base/process/launch.cc
@@ -7,11 +7,43 @@
namespace base {
-LaunchOptions::LaunchOptions() = default;
+LaunchOptions::LaunchOptions()
+ : wait(false),
+#if defined(OS_WIN)
+ start_hidden(false),
+ handles_to_inherit(NULL),
+ inherit_handles(false),
+ as_user(NULL),
+ empty_desktop_name(false),
+ job_handle(NULL),
+ stdin_handle(NULL),
+ stdout_handle(NULL),
+ stderr_handle(NULL),
+ force_breakaway_from_job_(false)
+#else
+ clear_environ(false),
+ fds_to_remap(NULL),
+ maximize_rlimits(NULL),
+ new_process_group(false)
+#if defined(OS_LINUX)
+ , clone_flags(0)
+ , allow_new_privs(false)
+ , kill_on_parent_death(false)
+#endif // OS_LINUX
+#if defined(OS_POSIX)
+ , pre_exec_delegate(NULL)
+#endif // OS_POSIX
+#if defined(OS_CHROMEOS)
+ , ctrl_terminal_fd(-1)
+#endif // OS_CHROMEOS
+#endif // !defined(OS_WIN)
+ {
+}
LaunchOptions::LaunchOptions(const LaunchOptions& other) = default;
-LaunchOptions::~LaunchOptions() = default;
+LaunchOptions::~LaunchOptions() {
+}
LaunchOptions LaunchOptionsForTest() {
LaunchOptions options;
diff --git a/base/process/launch.h b/base/process/launch.h
index be8f6e73b9..b8c02597a6 100644
--- a/base/process/launch.h
+++ b/base/process/launch.h
@@ -63,17 +63,17 @@ struct BASE_EXPORT LaunchOptions {
~LaunchOptions();
// If true, wait for the process to complete.
- bool wait = false;
+ bool wait;
// If not empty, change to this directory before executing the new process.
base::FilePath current_directory;
#if defined(OS_WIN)
- bool start_hidden = false;
+ bool start_hidden;
// If non-null, inherit exactly the list of handles in this vector (these
// handles must be inheritable).
- HandlesToInheritVector* handles_to_inherit = nullptr;
+ HandlesToInheritVector* handles_to_inherit;
// If true, the new process inherits handles from the parent. In production
// code this flag should be used only when running short-lived, trusted
@@ -81,7 +81,7 @@ struct BASE_EXPORT LaunchOptions {
// leak to the child process, causing errors such as open socket hangs.
// Note: If |handles_to_inherit| is non-null, this flag is ignored and only
// those handles will be inherited.
- bool inherit_handles = false;
+ bool inherit_handles;
// If non-null, runs as if the user represented by the token had launched it.
// Whether the application is visible on the interactive desktop depends on
@@ -90,29 +90,29 @@ struct BASE_EXPORT LaunchOptions {
// To avoid hard to diagnose problems, when specified this loads the
// environment variables associated with the user and if this operation fails
// the entire call fails as well.
- UserTokenHandle as_user = nullptr;
+ UserTokenHandle as_user;
// If true, use an empty string for the desktop name.
- bool empty_desktop_name = false;
+ bool empty_desktop_name;
// If non-null, launches the application in that job object. The process will
// be terminated immediately and LaunchProcess() will fail if assignment to
// the job object fails.
- HANDLE job_handle = nullptr;
+ HANDLE job_handle;
// Handles for the redirection of stdin, stdout and stderr. The handles must
// be inheritable. Caller should either set all three of them or none (i.e.
// there is no way to redirect stderr without redirecting stdin). The
// |inherit_handles| flag must be set to true when redirecting stdio stream.
- HANDLE stdin_handle = nullptr;
- HANDLE stdout_handle = nullptr;
- HANDLE stderr_handle = nullptr;
+ HANDLE stdin_handle;
+ HANDLE stdout_handle;
+ HANDLE stderr_handle;
// If set to true, ensures that the child process is launched with the
// CREATE_BREAKAWAY_FROM_JOB flag which allows it to breakout of the parent
// job if any.
- bool force_breakaway_from_job_ = false;
-#else // !defined(OS_WIN)
+ bool force_breakaway_from_job_;
+#else
// Set/unset environment variables. These are applied on top of the parent
// process environment. Empty (the default) means to inherit the same
// environment. See AlterEnvironment().
@@ -120,58 +120,53 @@ struct BASE_EXPORT LaunchOptions {
// Clear the environment for the new process before processing changes from
// |environ|.
- bool clear_environ = false;
+ bool clear_environ;
// If non-null, remap file descriptors according to the mapping of
// src fd->dest fd to propagate FDs into the child process.
// This pointer is owned by the caller and must live through the
// call to LaunchProcess().
- const FileHandleMappingVector* fds_to_remap = nullptr;
+ const FileHandleMappingVector* fds_to_remap;
// Each element is an RLIMIT_* constant that should be raised to its
// rlim_max. This pointer is owned by the caller and must live through
// the call to LaunchProcess().
- const std::vector<int>* maximize_rlimits = nullptr;
+ const std::vector<int>* maximize_rlimits;
// If true, start the process in a new process group, instead of
// inheriting the parent's process group. The pgid of the child process
// will be the same as its pid.
- bool new_process_group = false;
+ bool new_process_group;
#if defined(OS_LINUX)
// If non-zero, start the process using clone(), using flags as provided.
// Unlike in clone, clone_flags may not contain a custom termination signal
// that is sent to the parent when the child dies. The termination signal will
// always be set to SIGCHLD.
- int clone_flags = 0;
+ int clone_flags;
// By default, child processes will have the PR_SET_NO_NEW_PRIVS bit set. If
// true, then this bit will not be set in the new child process.
- bool allow_new_privs = false;
+ bool allow_new_privs;
// Sets parent process death signal to SIGKILL.
- bool kill_on_parent_death = false;
+ bool kill_on_parent_death;
#endif // defined(OS_LINUX)
#if defined(OS_POSIX)
- // If not empty, launch the specified executable instead of
- // cmdline.GetProgram(). This is useful when it is necessary to pass a custom
- // argv[0].
- base::FilePath real_path;
-
// If non-null, a delegate to be run immediately prior to executing the new
// program in the child process.
//
// WARNING: If LaunchProcess is called in the presence of multiple threads,
// code running in this delegate essentially needs to be async-signal safe
// (see man 7 signal for a list of allowed functions).
- PreExecDelegate* pre_exec_delegate = nullptr;
+ PreExecDelegate* pre_exec_delegate;
#endif // defined(OS_POSIX)
#if defined(OS_CHROMEOS)
// If non-negative, the specified file descriptor will be set as the launched
// process' controlling terminal.
- int ctrl_terminal_fd = -1;
+ int ctrl_terminal_fd;
#endif // defined(OS_CHROMEOS)
#endif // !defined(OS_WIN)
};
@@ -275,12 +270,6 @@ BASE_EXPORT bool GetAppOutputWithExitCode(const CommandLine& cl,
BASE_EXPORT void RaiseProcessToHighPriority();
#if defined(OS_MACOSX)
-// An implementation of LaunchProcess() that uses posix_spawn() instead of
-// fork()+exec(). This does not support the |pre_exec_delegate| and
-// |current_directory| options.
-Process LaunchProcessPosixSpawn(const std::vector<std::string>& argv,
- const LaunchOptions& options);
-
// Restore the default exception handler, setting it to Apple Crash Reporter
// (ReportCrash). When forking and execing a new process, the child will
// inherit the parent's exception ports, which may be set to the Breakpad
diff --git a/base/process/launch_mac.cc b/base/process/launch_mac.cc
index 3732bc1ecc..5895eae435 100644
--- a/base/process/launch_mac.cc
+++ b/base/process/launch_mac.cc
@@ -4,75 +4,13 @@
#include "base/process/launch.h"
-#include <crt_externs.h>
#include <mach/mach.h>
-#include <spawn.h>
-#include <string.h>
-#include <sys/wait.h>
+#include <servers/bootstrap.h>
#include "base/logging.h"
-#include "base/posix/eintr_wrapper.h"
-#include "base/threading/thread_restrictions.h"
namespace base {
-namespace {
-
-// DPSXCHECK is a Debug Posix Spawn Check macro. The posix_spawn* family of
-// functions return an errno value, as opposed to setting errno directly. This
-// macro emulates a DPCHECK().
-#define DPSXCHECK(expr) \
- do { \
- int rv = (expr); \
- DCHECK_EQ(rv, 0) << #expr << ": -" << rv << " " << strerror(rv); \
- } while (0)
-
-class PosixSpawnAttr {
- public:
- PosixSpawnAttr() { DPSXCHECK(posix_spawnattr_init(&attr_)); }
-
- ~PosixSpawnAttr() { DPSXCHECK(posix_spawnattr_destroy(&attr_)); }
-
- posix_spawnattr_t* get() { return &attr_; }
-
- private:
- posix_spawnattr_t attr_;
-};
-
-class PosixSpawnFileActions {
- public:
- PosixSpawnFileActions() {
- DPSXCHECK(posix_spawn_file_actions_init(&file_actions_));
- }
-
- ~PosixSpawnFileActions() {
- DPSXCHECK(posix_spawn_file_actions_destroy(&file_actions_));
- }
-
- void Open(int filedes, const char* path, int mode) {
- DPSXCHECK(posix_spawn_file_actions_addopen(&file_actions_, filedes, path,
- mode, 0));
- }
-
- void Dup2(int filedes, int newfiledes) {
- DPSXCHECK(
- posix_spawn_file_actions_adddup2(&file_actions_, filedes, newfiledes));
- }
-
- void Inherit(int filedes) {
- DPSXCHECK(posix_spawn_file_actions_addinherit_np(&file_actions_, filedes));
- }
-
- const posix_spawn_file_actions_t* get() const { return &file_actions_; }
-
- private:
- posix_spawn_file_actions_t file_actions_;
-
- DISALLOW_COPY_AND_ASSIGN(PosixSpawnFileActions);
-};
-
-} // namespace
-
void RestoreDefaultExceptionHandler() {
// This function is tailored to remove the Breakpad exception handler.
// exception_mask matches s_exception_mask in
@@ -90,93 +28,4 @@ void RestoreDefaultExceptionHandler() {
EXCEPTION_DEFAULT, THREAD_STATE_NONE);
}
-Process LaunchProcessPosixSpawn(const std::vector<std::string>& argv,
- const LaunchOptions& options) {
- DCHECK(!options.pre_exec_delegate)
- << "LaunchProcessPosixSpawn does not support PreExecDelegate";
- DCHECK(options.current_directory.empty())
- << "LaunchProcessPosixSpawn does not support current_directory";
-
- PosixSpawnAttr attr;
-
- short flags = POSIX_SPAWN_CLOEXEC_DEFAULT;
- if (options.new_process_group) {
- flags |= POSIX_SPAWN_SETPGROUP;
- DPSXCHECK(posix_spawnattr_setpgroup(attr.get(), 0));
- }
- DPSXCHECK(posix_spawnattr_setflags(attr.get(), flags));
-
- PosixSpawnFileActions file_actions;
-
- // Process file descriptors for the child. By default, LaunchProcess will
- // open stdin to /dev/null and inherit stdout and stderr.
- bool inherit_stdout = true, inherit_stderr = true;
- bool null_stdin = true;
- if (options.fds_to_remap) {
- for (const auto& dup2_pair : *options.fds_to_remap) {
- if (dup2_pair.second == STDIN_FILENO) {
- null_stdin = false;
- } else if (dup2_pair.second == STDOUT_FILENO) {
- inherit_stdout = false;
- } else if (dup2_pair.second == STDERR_FILENO) {
- inherit_stderr = false;
- }
-
- if (dup2_pair.first == dup2_pair.second) {
- file_actions.Inherit(dup2_pair.second);
- } else {
- file_actions.Dup2(dup2_pair.first, dup2_pair.second);
- }
- }
- }
-
- if (null_stdin) {
- file_actions.Open(STDIN_FILENO, "/dev/null", O_RDONLY);
- }
- if (inherit_stdout) {
- file_actions.Inherit(STDOUT_FILENO);
- }
- if (inherit_stderr) {
- file_actions.Inherit(STDERR_FILENO);
- }
-
- std::unique_ptr<char* []> argv_cstr(new char*[argv.size() + 1]);
- for (size_t i = 0; i < argv.size(); i++) {
- argv_cstr[i] = const_cast<char*>(argv[i].c_str());
- }
- argv_cstr[argv.size()] = nullptr;
-
- std::unique_ptr<char* []> owned_environ;
- char** new_environ = options.clear_environ ? nullptr : *_NSGetEnviron();
- if (!options.environ.empty()) {
- owned_environ = AlterEnvironment(new_environ, options.environ);
- new_environ = owned_environ.get();
- }
-
- const char* executable_path = !options.real_path.empty()
- ? options.real_path.value().c_str()
- : argv_cstr[0];
-
- // Use posix_spawnp as some callers expect to have PATH consulted.
- pid_t pid;
- int rv = posix_spawnp(&pid, executable_path, file_actions.get(), attr.get(),
- &argv_cstr[0], new_environ);
-
- if (rv != 0) {
- DLOG(ERROR) << "posix_spawnp(" << executable_path << "): -" << rv << " "
- << strerror(rv);
- return Process();
- }
-
- if (options.wait) {
- // While this isn't strictly disk IO, waiting for another process to
- // finish is the sort of thing ThreadRestrictions is trying to prevent.
- base::ThreadRestrictions::AssertIOAllowed();
- pid_t ret = HANDLE_EINTR(waitpid(pid, nullptr, 0));
- DPCHECK(ret > 0);
- }
-
- return Process(pid);
-}
-
} // namespace base
diff --git a/base/process/launch_posix.cc b/base/process/launch_posix.cc
index 19effa2ce5..4fb1018276 100644
--- a/base/process/launch_posix.cc
+++ b/base/process/launch_posix.cc
@@ -60,8 +60,6 @@
#if defined(OS_MACOSX)
#include <crt_externs.h>
#include <sys/event.h>
-
-#include "base/feature_list.h"
#else
extern char** environ;
#endif
@@ -72,11 +70,6 @@ namespace base {
namespace {
-#if defined(OS_MACOSX)
-const Feature kMacLaunchProcessPosixSpawn{"MacLaunchProcessPosixSpawn",
- FEATURE_ENABLED_BY_DEFAULT};
-#endif
-
// Get the process's "environment" (i.e. the thing that setenv/getenv
// work with).
char** GetEnvironment() {
@@ -303,15 +296,6 @@ Process LaunchProcess(const CommandLine& cmdline,
Process LaunchProcess(const std::vector<std::string>& argv,
const LaunchOptions& options) {
-#if defined(OS_MACOSX)
- if (FeatureList::IsEnabled(kMacLaunchProcessPosixSpawn)) {
- // TODO(rsesek): Do this unconditionally. There is one user for each of
- // these two options. https://crbug.com/179923.
- if (!options.pre_exec_delegate && options.current_directory.empty())
- return LaunchProcessPosixSpawn(argv, options);
- }
-#endif
-
size_t fd_shuffle_size = 0;
if (options.fds_to_remap) {
fd_shuffle_size = options.fds_to_remap->size();
@@ -508,10 +492,7 @@ Process LaunchProcess(const std::vector<std::string>& argv,
options.pre_exec_delegate->RunAsyncSafe();
}
- const char* executable_path = !options.real_path.empty() ?
- options.real_path.value().c_str() : argv_cstr[0];
-
- execvp(executable_path, argv_cstr.get());
+ execvp(argv_cstr[0], argv_cstr.get());
RAW_LOG(ERROR, "LaunchProcess: failed to execvp:");
RAW_LOG(ERROR, argv_cstr[0]);
diff --git a/base/process/memory.cc b/base/process/memory.cc
deleted file mode 100644
index 6349c08ca0..0000000000
--- a/base/process/memory.cc
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/debug/alias.h"
-#include "base/logging.h"
-#include "base/process/memory.h"
-#include "build/build_config.h"
-
-namespace base {
-
-// Defined in memory_win.cc for Windows.
-#if !defined(OS_WIN)
-
-namespace {
-
-// Breakpad server classifies base::`anonymous namespace'::OnNoMemory as
-// out-of-memory crash.
-NOINLINE void OnNoMemory(size_t size) {
- size_t tmp_size = size;
- base::debug::Alias(&tmp_size);
- LOG(FATAL) << "Out of memory. size=" << tmp_size;
-}
-
-} // namespace
-
-void TerminateBecauseOutOfMemory(size_t size) {
- OnNoMemory(size);
-}
-
-#endif
-
-// Defined in memory_mac.mm for Mac.
-#if !defined(OS_MACOSX)
-
-bool UncheckedCalloc(size_t num_items, size_t size, void** result) {
- const size_t alloc_size = num_items * size;
-
- // Overflow check
- if (size && ((alloc_size / size) != num_items)) {
- *result = NULL;
- return false;
- }
-
- if (!UncheckedMalloc(alloc_size, result))
- return false;
-
- memset(*result, 0, alloc_size);
- return true;
-}
-
-#endif
-
-} // namespace base
diff --git a/base/process/memory.h b/base/process/memory.h
deleted file mode 100644
index 77911cfc35..0000000000
--- a/base/process/memory.h
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_PROCESS_MEMORY_H_
-#define BASE_PROCESS_MEMORY_H_
-
-#include <stddef.h>
-
-#include "base/base_export.h"
-#include "base/process/process_handle.h"
-#include "build/build_config.h"
-
-#ifdef PVALLOC_AVAILABLE
-// Build config explicitly tells us whether or not pvalloc is available.
-#elif defined(LIBC_GLIBC) && !defined(USE_TCMALLOC)
-#define PVALLOC_AVAILABLE 1
-#else
-#define PVALLOC_AVAILABLE 0
-#endif
-
-namespace base {
-
-// Enables 'terminate on heap corruption' flag. Helps protect against heap
-// overflow. Has no effect if the OS doesn't provide the necessary facility.
-BASE_EXPORT void EnableTerminationOnHeapCorruption();
-
-// Turns on process termination if memory runs out.
-BASE_EXPORT void EnableTerminationOnOutOfMemory();
-
-// Terminates process. Should be called only for out of memory errors.
-// Crash reporting classifies such crashes as OOM.
-BASE_EXPORT void TerminateBecauseOutOfMemory(size_t size);
-
-#if defined(OS_LINUX) || defined(OS_ANDROID)
-BASE_EXPORT extern size_t g_oom_size;
-
-// The maximum allowed value for the OOM score.
-const int kMaxOomScore = 1000;
-
-// This adjusts /proc/<pid>/oom_score_adj so the Linux OOM killer will
-// prefer to kill certain process types over others. The range for the
-// adjustment is [-1000, 1000], with [0, 1000] being user accessible.
-// If the Linux system doesn't support the newer oom_score_adj range
-// of [0, 1000], then we revert to using the older oom_adj, and
-// translate the given value into [0, 15]. Some aliasing of values
-// may occur in that case, of course.
-BASE_EXPORT bool AdjustOOMScore(ProcessId process, int score);
-#endif
-
-#if defined(OS_WIN)
-namespace win {
-
-// Custom Windows exception code chosen to indicate an out of memory error.
-// See https://msdn.microsoft.com/en-us/library/het71c37.aspx.
-// "To make sure that you do not define a code that conflicts with an existing
-// exception code" ... "The resulting error code should therefore have the
-// highest four bits set to hexadecimal E."
-// 0xe0000008 was chosen arbitrarily, as 0x00000008 is ERROR_NOT_ENOUGH_MEMORY.
-const DWORD kOomExceptionCode = 0xe0000008;
-
-} // namespace win
-#endif
-
-// Special allocator functions for callers that want to check for OOM.
-// These will not abort if the allocation fails even if
-// EnableTerminationOnOutOfMemory has been called.
-// This can be useful for huge and/or unpredictable size memory allocations.
-// Please only use this if you really handle the case when the allocation
-// fails. Doing otherwise would risk security.
-// These functions may still crash on OOM when running under memory tools,
-// specifically ASan and other sanitizers.
-// Return value tells whether the allocation succeeded. If it fails |result| is
-// set to NULL, otherwise it holds the memory address.
-BASE_EXPORT WARN_UNUSED_RESULT bool UncheckedMalloc(size_t size,
- void** result);
-BASE_EXPORT WARN_UNUSED_RESULT bool UncheckedCalloc(size_t num_items,
- size_t size,
- void** result);
-
-} // namespace base
-
-#endif // BASE_PROCESS_MEMORY_H_
diff --git a/base/process/memory_linux.cc b/base/process/memory_linux.cc
deleted file mode 100644
index 985bc54eb6..0000000000
--- a/base/process/memory_linux.cc
+++ /dev/null
@@ -1,212 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/process/memory.h"
-
-#include <stddef.h>
-
-#include <new>
-
-#include "base/allocator/allocator_shim.h"
-#include "base/allocator/features.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/logging.h"
-#include "base/process/internal_linux.h"
-#include "base/strings/string_number_conversions.h"
-#include "build/build_config.h"
-
-#if defined(USE_TCMALLOC)
-#include "third_party/tcmalloc/chromium/src/gperftools/tcmalloc.h"
-#endif
-
-namespace base {
-
-size_t g_oom_size = 0U;
-
-namespace {
-
-void OnNoMemorySize(size_t size) {
- g_oom_size = size;
-
- if (size != 0)
- LOG(FATAL) << "Out of memory, size = " << size;
- LOG(FATAL) << "Out of memory.";
-}
-
-void OnNoMemory() {
- OnNoMemorySize(0);
-}
-
-} // namespace
-
-// TODO(primiano): Once the unified shim is on by default (crbug.com/550886)
-// get rid of the code in this entire #if section. The whole termination-on-OOM
-// logic is implemented in the shim.
-#if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) && \
- !defined(THREAD_SANITIZER) && !defined(LEAK_SANITIZER) && \
- !BUILDFLAG(USE_EXPERIMENTAL_ALLOCATOR_SHIM)
-
-#if defined(LIBC_GLIBC) && !defined(USE_TCMALLOC)
-
-extern "C" {
-void* __libc_malloc(size_t size);
-void* __libc_realloc(void* ptr, size_t size);
-void* __libc_calloc(size_t nmemb, size_t size);
-void* __libc_valloc(size_t size);
-#if PVALLOC_AVAILABLE == 1
-void* __libc_pvalloc(size_t size);
-#endif
-void* __libc_memalign(size_t alignment, size_t size);
-
-// Overriding the system memory allocation functions:
-//
-// For security reasons, we want malloc failures to be fatal. Too much code
-// doesn't check for a NULL return value from malloc and unconditionally uses
-// the resulting pointer. If the first offset that they try to access is
-// attacker controlled, then the attacker can direct the code to access any
-// part of memory.
-//
-// Thus, we define all the standard malloc functions here and mark them as
-// visibility 'default'. This means that they replace the malloc functions for
-// all Chromium code and also for all code in shared libraries. There are tests
-// for this in process_util_unittest.cc.
-//
-// If we are using tcmalloc, then the problem is moot since tcmalloc handles
-// this for us. Thus this code is in a !defined(USE_TCMALLOC) block.
-//
-// If we are testing the binary with AddressSanitizer, we should not
-// redefine malloc and let AddressSanitizer do it instead.
-//
-// We call the real libc functions in this code by using __libc_malloc etc.
-// Previously we tried using dlsym(RTLD_NEXT, ...) but that failed depending on
-// the link order. Since ld.so needs calloc during symbol resolution, it
-// defines its own versions of several of these functions in dl-minimal.c.
-// Depending on the runtime library order, dlsym ended up giving us those
-// functions and bad things happened. See crbug.com/31809
-//
-// This means that any code which calls __libc_* gets the raw libc versions of
-// these functions.
-
-#define DIE_ON_OOM_1(function_name) \
- void* function_name(size_t) __attribute__ ((visibility("default"))); \
- \
- void* function_name(size_t size) { \
- void* ret = __libc_##function_name(size); \
- if (ret == NULL && size != 0) \
- OnNoMemorySize(size); \
- return ret; \
- }
-
-#define DIE_ON_OOM_2(function_name, arg1_type) \
- void* function_name(arg1_type, size_t) \
- __attribute__ ((visibility("default"))); \
- \
- void* function_name(arg1_type arg1, size_t size) { \
- void* ret = __libc_##function_name(arg1, size); \
- if (ret == NULL && size != 0) \
- OnNoMemorySize(size); \
- return ret; \
- }
-
-DIE_ON_OOM_1(malloc)
-DIE_ON_OOM_1(valloc)
-#if PVALLOC_AVAILABLE == 1
-DIE_ON_OOM_1(pvalloc)
-#endif
-
-DIE_ON_OOM_2(calloc, size_t)
-DIE_ON_OOM_2(realloc, void*)
-DIE_ON_OOM_2(memalign, size_t)
-
-// posix_memalign has a unique signature and doesn't have a __libc_ variant.
-int posix_memalign(void** ptr, size_t alignment, size_t size)
- __attribute__ ((visibility("default")));
-
-int posix_memalign(void** ptr, size_t alignment, size_t size) {
- // This will use the safe version of memalign, above.
- *ptr = memalign(alignment, size);
- return 0;
-}
-
-} // extern C
-
-#else
-
-// TODO(mostynb@opera.com): dlsym dance
-
-#endif // LIBC_GLIBC && !USE_TCMALLOC
-
-#endif // !*_SANITIZER
-
-void EnableTerminationOnHeapCorruption() {
- // On Linux, there nothing to do AFAIK.
-}
-
-void EnableTerminationOnOutOfMemory() {
- // Set the new-out of memory handler.
- std::set_new_handler(&OnNoMemory);
- // If we're using glibc's allocator, the above functions will override
- // malloc and friends and make them die on out of memory.
-
-#if BUILDFLAG(USE_EXPERIMENTAL_ALLOCATOR_SHIM)
- allocator::SetCallNewHandlerOnMallocFailure(true);
-#elif defined(USE_TCMALLOC)
- // For tcmalloc, we need to tell it to behave like new.
- tc_set_new_mode(1);
-#endif
-}
-
-// NOTE: This is not the only version of this function in the source:
-// the setuid sandbox (in process_util_linux.c, in the sandbox source)
-// also has its own C version.
-bool AdjustOOMScore(ProcessId process, int score) {
- if (score < 0 || score > kMaxOomScore)
- return false;
-
- FilePath oom_path(internal::GetProcPidDir(process));
-
- // Attempt to write the newer oom_score_adj file first.
- FilePath oom_file = oom_path.AppendASCII("oom_score_adj");
- if (PathExists(oom_file)) {
- std::string score_str = IntToString(score);
- DVLOG(1) << "Adjusting oom_score_adj of " << process << " to "
- << score_str;
- int score_len = static_cast<int>(score_str.length());
- return (score_len == WriteFile(oom_file, score_str.c_str(), score_len));
- }
-
- // If the oom_score_adj file doesn't exist, then we write the old
- // style file and translate the oom_adj score to the range 0-15.
- oom_file = oom_path.AppendASCII("oom_adj");
- if (PathExists(oom_file)) {
- // Max score for the old oom_adj range. Used for conversion of new
- // values to old values.
- const int kMaxOldOomScore = 15;
-
- int converted_score = score * kMaxOldOomScore / kMaxOomScore;
- std::string score_str = IntToString(converted_score);
- DVLOG(1) << "Adjusting oom_adj of " << process << " to " << score_str;
- int score_len = static_cast<int>(score_str.length());
- return (score_len == WriteFile(oom_file, score_str.c_str(), score_len));
- }
-
- return false;
-}
-
-bool UncheckedMalloc(size_t size, void** result) {
-#if BUILDFLAG(USE_EXPERIMENTAL_ALLOCATOR_SHIM)
- *result = allocator::UncheckedAlloc(size);
-#elif defined(MEMORY_TOOL_REPLACES_ALLOCATOR) || \
- (!defined(LIBC_GLIBC) && !defined(USE_TCMALLOC))
- *result = malloc(size);
-#elif defined(LIBC_GLIBC) && !defined(USE_TCMALLOC)
- *result = __libc_malloc(size);
-#elif defined(USE_TCMALLOC)
- *result = tc_malloc_skip_new_handler(size);
-#endif
- return *result != NULL;
-}
-
-} // namespace base
diff --git a/base/process/memory_stubs.cc b/base/process/memory_stubs.cc
deleted file mode 100644
index 67deb4f58b..0000000000
--- a/base/process/memory_stubs.cc
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/process/memory.h"
-
-#include <stddef.h>
-#include <stdlib.h>
-
-#include "base/compiler_specific.h"
-
-namespace base {
-
-void EnableTerminationOnOutOfMemory() {
-}
-
-void EnableTerminationOnHeapCorruption() {
-}
-
-bool AdjustOOMScore(ProcessId process, int score) {
- ALLOW_UNUSED_PARAM(process);
- ALLOW_UNUSED_PARAM(score);
- return false;
-}
-
-void TerminateBecauseOutOfMemory(size_t size) {
- ALLOW_UNUSED_PARAM(size);
- abort();
-}
-
-// UncheckedMalloc and Calloc exist so that platforms making use of
-// EnableTerminationOnOutOfMemory have a way to allocate memory without
-// crashing. This _stubs.cc file is for platforms that do not support
-// EnableTerminationOnOutOfMemory (note the empty implementation above). As
-// such, these two Unchecked.alloc functions need only trivially pass-through to
-// their respective stdlib function since those functions will return null on a
-// failure to allocate.
-
-bool UncheckedMalloc(size_t size, void** result) {
- *result = malloc(size);
- return *result != nullptr;
-}
-
-bool UncheckedCalloc(size_t num_items, size_t size, void** result) {
- *result = calloc(num_items, size);
- return *result != nullptr;
-}
-
-} // namespace base
diff --git a/base/process/port_provider_mac.cc b/base/process/port_provider_mac.cc
index 23d214c3f3..ac13949ac8 100644
--- a/base/process/port_provider_mac.cc
+++ b/base/process/port_provider_mac.cc
@@ -21,8 +21,7 @@ void PortProvider::RemoveObserver(Observer* observer) {
void PortProvider::NotifyObservers(ProcessHandle process) {
base::AutoLock l(lock_);
- for (auto& observer : observer_list_)
- observer.OnReceivedTaskPort(process);
+ FOR_EACH_OBSERVER(Observer, observer_list_, OnReceivedTaskPort(process));
}
} // namespace base
diff --git a/base/process/process.h b/base/process/process.h
index fc2add24c5..70c8260193 100644
--- a/base/process/process.h
+++ b/base/process/process.h
@@ -15,17 +15,8 @@
#include "base/win/scoped_handle.h"
#endif
-#if defined(OS_MACOSX)
-#include "base/feature_list.h"
-#include "base/process/port_provider_mac.h"
-#endif
-
namespace base {
-#if defined(OS_MACOSX)
-extern const Feature kMacAllowBackgroundingProcesses;
-#endif
-
// Provides a move-only encapsulation of a process.
//
// This object is not tied to the lifetime of the underlying process: the
@@ -76,9 +67,6 @@ class BASE_EXPORT Process {
// Returns true if processes can be backgrounded.
static bool CanBackgroundProcesses();
- // Terminates the current process immediately with |exit_code|.
- static void TerminateCurrentProcessImmediately(int exit_code);
-
// Returns true if this objects represents a valid process.
bool IsValid() const;
@@ -111,35 +99,13 @@ class BASE_EXPORT Process {
// any process.
// NOTE: |exit_code| is optional, nullptr can be passed if the exit code is
// not required.
- bool WaitForExit(int* exit_code) const;
+ bool WaitForExit(int* exit_code);
// Same as WaitForExit() but only waits for up to |timeout|.
// NOTE: |exit_code| is optional, nullptr can be passed if the exit code
// is not required.
- bool WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) const;
-
-#if defined(OS_MACOSX)
- // The Mac needs a Mach port in order to manipulate a process's priority,
- // and there's no good way to get that from base given the pid. These Mac
- // variants of the IsProcessBackgrounded and SetProcessBackgrounded API take
- // a port provider for this reason. See crbug.com/460102
- //
- // A process is backgrounded when its task priority is
- // |TASK_BACKGROUND_APPLICATION|.
- //
- // Returns true if the port_provider can locate a task port for the process
- // and it is backgrounded. If port_provider is null, returns false.
- bool IsProcessBackgrounded(PortProvider* port_provider) const;
-
- // Set the process as backgrounded. If value is
- // true, the priority of the associated task will be set to
- // TASK_BACKGROUND_APPLICATION. If value is false, the
- // priority of the process will be set to TASK_FOREGROUND_APPLICATION.
- //
- // Returns true if the priority was changed, false otherwise. If
- // |port_provider| is null, this is a no-op and it returns false.
- bool SetProcessBackgrounded(PortProvider* port_provider, bool value);
-#else
+ bool WaitForExitWithTimeout(TimeDelta timeout, int* exit_code);
+
// A process is backgrounded when it's priority is lower than normal.
// Return true if this process is backgrounded, false otherwise.
bool IsProcessBackgrounded() const;
@@ -149,7 +115,7 @@ class BASE_EXPORT Process {
// will be made "normal" - equivalent to default process priority.
// Returns true if the priority was changed, false otherwise.
bool SetProcessBackgrounded(bool value);
-#endif // defined(OS_MACOSX)
+
// Returns an integer representing the priority of a process. The meaning
// of this value is OS dependent.
int GetPriority() const;
diff --git a/base/process/process_metrics.cc b/base/process/process_metrics.cc
index a38930a208..0b38726431 100644
--- a/base/process/process_metrics.cc
+++ b/base/process/process_metrics.cc
@@ -46,7 +46,7 @@ std::unique_ptr<Value> SystemMetrics::ToValue() const {
return std::move(res);
}
-std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateCurrentProcessMetrics() {
+ProcessMetrics* ProcessMetrics::CreateCurrentProcessMetrics() {
#if !defined(OS_MACOSX) || defined(OS_IOS)
return CreateProcessMetrics(base::GetCurrentProcessHandle());
#else
@@ -84,9 +84,8 @@ int ProcessMetrics::CalculateIdleWakeupsPerSecond(
last_idle_wakeups_time_ = time;
last_absolute_idle_wakeups_ = absolute_idle_wakeups;
+ // Round to average wakeups per second.
int64_t wakeups_delta_for_ms = wakeups_delta * Time::kMicrosecondsPerSecond;
- // Round the result up by adding 1/2 (the second term resolves to 1/2 without
- // dropping down into floating point).
return (wakeups_delta_for_ms + time_delta / 2) / time_delta;
}
#else
diff --git a/base/process/process_metrics.h b/base/process/process_metrics.h
index 71d6042e00..57cb3abec0 100644
--- a/base/process/process_metrics.h
+++ b/base/process/process_metrics.h
@@ -11,7 +11,6 @@
#include <stddef.h>
#include <stdint.h>
-#include <memory>
#include <string>
#include "base/base_export.h"
@@ -104,22 +103,22 @@ class BASE_EXPORT ProcessMetrics {
~ProcessMetrics();
// Creates a ProcessMetrics for the specified process.
+ // The caller owns the returned object.
#if !defined(OS_MACOSX) || defined(OS_IOS)
- static std::unique_ptr<ProcessMetrics> CreateProcessMetrics(
- ProcessHandle process);
+ static ProcessMetrics* CreateProcessMetrics(ProcessHandle process);
#else
// The port provider needs to outlive the ProcessMetrics object returned by
// this function. If NULL is passed as provider, the returned object
// only returns valid metrics if |process| is the current process.
- static std::unique_ptr<ProcessMetrics> CreateProcessMetrics(
- ProcessHandle process,
- PortProvider* port_provider);
+ static ProcessMetrics* CreateProcessMetrics(ProcessHandle process,
+ PortProvider* port_provider);
#endif // !defined(OS_MACOSX) || defined(OS_IOS)
// Creates a ProcessMetrics for the current process. This a cross-platform
// convenience wrapper for CreateProcessMetrics().
- static std::unique_ptr<ProcessMetrics> CreateCurrentProcessMetrics();
+ // The caller owns the returned object.
+ static ProcessMetrics* CreateCurrentProcessMetrics();
// Returns the current space allocated for the pagefile, in bytes (these pages
// may or may not be in memory). On Linux, this returns the total virtual
@@ -136,7 +135,8 @@ class BASE_EXPORT ProcessMetrics {
// memory currently allocated to a process that cannot be shared. Returns
// false on platform specific error conditions. Note: |private_bytes|
// returns 0 on unsupported OSes: prior to XP SP2.
- bool GetMemoryBytes(size_t* private_bytes, size_t* shared_bytes) const;
+ bool GetMemoryBytes(size_t* private_bytes,
+ size_t* shared_bytes);
// Fills a CommittedKBytes with both resident and paged
// memory usage as per definition of CommittedBytes.
void GetCommittedKBytes(CommittedKBytes* usage) const;
@@ -144,9 +144,6 @@ class BASE_EXPORT ProcessMetrics {
// usage in bytes, as per definition of WorkingSetBytes. Note that this
// function is somewhat expensive on Windows (a few ms per process).
bool GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const;
- // Computes pss (proportional set size) of a process. Note that this
- // function is somewhat expensive on Windows (a few ms per process).
- bool GetProportionalSetSizeBytes(uint64_t* pss_bytes) const;
#if defined(OS_MACOSX)
// Fills both CommitedKBytes and WorkingSetKBytes in a single operation. This
@@ -154,10 +151,6 @@ class BASE_EXPORT ProcessMetrics {
// system call.
bool GetCommittedAndWorkingSetKBytes(CommittedKBytes* usage,
WorkingSetKBytes* ws_usage) const;
- // Returns private, shared, and total resident bytes.
- bool GetMemoryBytes(size_t* private_bytes,
- size_t* shared_bytes,
- size_t* resident_bytes) const;
#endif
// Returns the CPU usage in percent since the last time this method or
@@ -302,9 +295,9 @@ struct BASE_EXPORT SystemMemoryInfoKB {
int dirty;
// vmstats data.
- unsigned long pswpin;
- unsigned long pswpout;
- unsigned long pgmajfault;
+ int pswpin;
+ int pswpout;
+ int pgmajfault;
#endif // defined(OS_ANDROID) || defined(OS_LINUX)
#if defined(OS_CHROMEOS)
@@ -381,9 +374,6 @@ BASE_EXPORT bool IsValidDiskName(const std::string& candidate);
// Retrieves data from /proc/diskstats about system-wide disk I/O.
// Fills in the provided |diskinfo| structure. Returns true on success.
BASE_EXPORT bool GetSystemDiskInfo(SystemDiskInfo* diskinfo);
-
-// Returns the amount of time spent in user space since boot across all CPUs.
-BASE_EXPORT TimeDelta GetUserCpuTimeSinceBoot();
#endif // defined(OS_LINUX) || defined(OS_ANDROID)
#if defined(OS_CHROMEOS)
diff --git a/base/process/process_metrics_linux.cc b/base/process/process_metrics_linux.cc
index 5d542cc675..3d27656d6a 100644
--- a/base/process/process_metrics_linux.cc
+++ b/base/process/process_metrics_linux.cc
@@ -17,7 +17,6 @@
#include "base/files/dir_reader_posix.h"
#include "base/files/file_util.h"
#include "base/logging.h"
-#include "base/memory/ptr_util.h"
#include "base/process/internal_linux.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
@@ -73,8 +72,8 @@ size_t ReadProcStatusAndGetFieldAsSizeT(pid_t pid, const std::string& field) {
const std::string& key = pairs[i].first;
const std::string& value_str = pairs[i].second;
if (key == field) {
- std::vector<StringPiece> split_value_str =
- SplitStringPiece(value_str, " ", TRIM_WHITESPACE, SPLIT_WANT_ALL);
+ std::vector<StringPiece> split_value_str = SplitStringPiece(
+ value_str, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
if (split_value_str.size() != 2 || split_value_str[1] != "kB") {
NOTREACHED();
return 0;
@@ -164,9 +163,8 @@ int GetProcessCPU(pid_t pid) {
} // namespace
// static
-std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics(
- ProcessHandle process) {
- return WrapUnique(new ProcessMetrics(process));
+ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) {
+ return new ProcessMetrics(process);
}
// On linux, we return vsize.
@@ -192,7 +190,7 @@ size_t ProcessMetrics::GetPeakWorkingSetSize() const {
}
bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
- size_t* shared_bytes) const {
+ size_t* shared_bytes) {
WorkingSetKBytes ws_usage;
if (!GetWorkingSetKBytes(&ws_usage))
return false;
@@ -356,7 +354,8 @@ bool ProcessMetrics::GetWorkingSetKBytesTotmaps(WorkingSetKBytes *ws_usage)
}
std::vector<std::string> totmaps_fields = SplitString(
- totmaps_data, kWhitespaceASCII, KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
+ totmaps_data, base::kWhitespaceASCII, base::KEEP_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY);
DCHECK_EQ("Pss:", totmaps_fields[kPssIndex-1]);
DCHECK_EQ("Private_Clean:", totmaps_fields[kPrivate_CleanIndex - 1]);
@@ -407,8 +406,8 @@ bool ProcessMetrics::GetWorkingSetKBytesStatm(WorkingSetKBytes* ws_usage)
return false;
}
- std::vector<StringPiece> statm_vec =
- SplitStringPiece(statm, " ", TRIM_WHITESPACE, SPLIT_WANT_ALL);
+ std::vector<StringPiece> statm_vec = SplitStringPiece(
+ statm, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
if (statm_vec.size() != 7)
return false; // Not the format we expect.
@@ -687,16 +686,12 @@ bool ParseProcVmstat(const std::string& vmstat_data,
if (tokens.size() != 2)
continue;
- uint64_t val;
- if (!StringToUint64(tokens[1], &val))
- continue;
-
if (tokens[0] == "pswpin") {
- meminfo->pswpin = val;
+ StringToInt(tokens[1], &meminfo->pswpin);
} else if (tokens[0] == "pswpout") {
- meminfo->pswpout = val;
+ StringToInt(tokens[1], &meminfo->pswpout);
} else if (tokens[0] == "pgmajfault") {
- meminfo->pgmajfault = val;
+ StringToInt(tokens[1], &meminfo->pgmajfault);
}
}
@@ -912,10 +907,6 @@ bool GetSystemDiskInfo(SystemDiskInfo* diskinfo) {
return true;
}
-TimeDelta GetUserCpuTimeSinceBoot() {
- return internal::GetUserCpuTimeSinceBoot();
-}
-
#if defined(OS_CHROMEOS)
std::unique_ptr<Value> SwapInfo::ToValue() const {
std::unique_ptr<DictionaryValue> res(new DictionaryValue());
diff --git a/base/process/process_metrics_mac.cc b/base/process/process_metrics_mac.cc
index 51f5fd4e16..8b5d5644ff 100644
--- a/base/process/process_metrics_mac.cc
+++ b/base/process/process_metrics_mac.cc
@@ -15,7 +15,6 @@
#include "base/logging.h"
#include "base/mac/mach_logging.h"
#include "base/mac/scoped_mach_port.h"
-#include "base/memory/ptr_util.h"
#include "base/sys_info.h"
#if !defined(TASK_POWER_INFO)
@@ -80,7 +79,10 @@ bool IsAddressInSharedRegion(mach_vm_address_t addr, cpu_type_t type) {
} // namespace
-SystemMemoryInfoKB::SystemMemoryInfoKB() : total(0), free(0) {}
+SystemMemoryInfoKB::SystemMemoryInfoKB() {
+ total = 0;
+ free = 0;
+}
SystemMemoryInfoKB::SystemMemoryInfoKB(const SystemMemoryInfoKB& other) =
default;
@@ -92,10 +94,10 @@ SystemMemoryInfoKB::SystemMemoryInfoKB(const SystemMemoryInfoKB& other) =
// otherwise return 0.
// static
-std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics(
+ProcessMetrics* ProcessMetrics::CreateProcessMetrics(
ProcessHandle process,
PortProvider* port_provider) {
- return WrapUnique(new ProcessMetrics(process, port_provider));
+ return new ProcessMetrics(process, port_provider);
}
size_t ProcessMetrics::GetPagefileUsage() const {
@@ -110,12 +112,10 @@ size_t ProcessMetrics::GetPeakPagefileUsage() const {
}
size_t ProcessMetrics::GetWorkingSetSize() const {
- size_t private_bytes = 0;
- size_t shared_bytes = 0;
- size_t resident_bytes = 0;
- if (!GetMemoryBytes(&private_bytes, &shared_bytes, &resident_bytes))
+ task_basic_info_64 task_info_data;
+ if (!GetTaskInfo(TaskForPid(process_), &task_info_data))
return 0;
- return resident_bytes;
+ return task_info_data.resident_size;
}
size_t ProcessMetrics::GetPeakWorkingSetSize() const {
@@ -126,7 +126,7 @@ size_t ProcessMetrics::GetPeakWorkingSetSize() const {
// private_bytes is the size of private resident memory.
// shared_bytes is the size of shared resident memory.
bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
- size_t* shared_bytes) const {
+ size_t* shared_bytes) {
size_t private_pages_count = 0;
size_t shared_pages_count = 0;
@@ -145,7 +145,7 @@ bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
// The same region can be referenced multiple times. To avoid double counting
// we need to keep track of which regions we've already counted.
- hash_set<int> seen_objects;
+ base::hash_set<int> seen_objects;
// We iterate through each VM region in the task's address map. For shared
// memory we add up all the pages that are marked as shared. Like libtop we
@@ -191,7 +191,6 @@ bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
info.share_mode = SM_PRIVATE;
switch (info.share_mode) {
- case SM_LARGE_PAGE:
case SM_PRIVATE:
private_pages_count += info.private_pages_resident;
private_pages_count += info.shared_pages_resident;
@@ -200,9 +199,6 @@ bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
private_pages_count += info.private_pages_resident;
// Fall through
case SM_SHARED:
- case SM_PRIVATE_ALIASED:
- case SM_TRUESHARED:
- case SM_SHARED_ALIASED:
if (seen_objects.count(info.obj_id) == 0) {
// Only count the first reference to this region.
seen_objects.insert(info.obj_id);
@@ -252,15 +248,6 @@ bool ProcessMetrics::GetCommittedAndWorkingSetKBytes(
return true;
}
-bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
- size_t* shared_bytes,
- size_t* resident_bytes) const {
- if (!GetMemoryBytes(private_bytes, shared_bytes))
- return false;
- *resident_bytes = *private_bytes + *shared_bytes;
- return true;
-}
-
#define TIME_VALUE_TO_TIMEVAL(a, r) do { \
(r)->tv_sec = (a)->seconds; \
(r)->tv_usec = (a)->microseconds; \
@@ -339,17 +326,6 @@ int ProcessMetrics::GetIdleWakeupsPerSecond() {
// where TASK_POWER_INFO isn't supported yet.
return 0;
}
-
- // The task_power_info struct contains two wakeup counters:
- // task_interrupt_wakeups and task_platform_idle_wakeups.
- // task_interrupt_wakeups is the total number of wakeups generated by the
- // process, and is the number that Activity Monitor reports.
- // task_platform_idle_wakeups is a subset of task_interrupt_wakeups that
- // tallies the number of times the processor was taken out of its low-power
- // idle state to handle a wakeup. task_platform_idle_wakeups therefore result
- // in a greater power increase than the other interrupts which occur while the
- // CPU is already working, and reducing them has a greater overall impact on
- // power usage. See the powermetrics man page for more info.
return CalculateIdleWakeupsPerSecond(
power_info_data.task_platform_idle_wakeups);
}
diff --git a/base/process/process_metrics_posix.cc b/base/process/process_metrics_posix.cc
index 13acf2ea34..fad581eece 100644
--- a/base/process/process_metrics_posix.cc
+++ b/base/process/process_metrics_posix.cc
@@ -33,8 +33,6 @@ static const rlim_t kSystemDefaultMaxFds = 256;
static const rlim_t kSystemDefaultMaxFds = 8192;
#elif defined(OS_FREEBSD)
static const rlim_t kSystemDefaultMaxFds = 8192;
-#elif defined(OS_NETBSD)
-static const rlim_t kSystemDefaultMaxFds = 1024;
#elif defined(OS_OPENBSD)
static const rlim_t kSystemDefaultMaxFds = 256;
#elif defined(OS_ANDROID)
diff --git a/base/process/process_metrics_unittest.cc b/base/process/process_metrics_unittest.cc
index b0bd7ea80b..94a2ffe7f8 100644
--- a/base/process/process_metrics_unittest.cc
+++ b/base/process/process_metrics_unittest.cc
@@ -286,13 +286,13 @@ TEST_F(SystemMetricsTest, ParseVmstat) {
"pgrefill_high 0\n"
"pgrefill_movable 0\n";
EXPECT_TRUE(ParseProcVmstat(valid_input1, &meminfo));
- EXPECT_EQ(179LU, meminfo.pswpin);
- EXPECT_EQ(406LU, meminfo.pswpout);
- EXPECT_EQ(487192LU, meminfo.pgmajfault);
+ EXPECT_EQ(meminfo.pswpin, 179);
+ EXPECT_EQ(meminfo.pswpout, 406);
+ EXPECT_EQ(meminfo.pgmajfault, 487192);
EXPECT_TRUE(ParseProcVmstat(valid_input2, &meminfo));
- EXPECT_EQ(12LU, meminfo.pswpin);
- EXPECT_EQ(901LU, meminfo.pswpout);
- EXPECT_EQ(2023LU, meminfo.pgmajfault);
+ EXPECT_EQ(meminfo.pswpin, 12);
+ EXPECT_EQ(meminfo.pswpout, 901);
+ EXPECT_EQ(meminfo.pgmajfault, 2023);
}
#endif // defined(OS_LINUX) || defined(OS_ANDROID)
@@ -485,13 +485,10 @@ MULTIPROCESS_TEST_MAIN(ChildMain) {
} // namespace
-// Arc++ note: don't compile as SpawnMultiProcessTestChild brings in a lot of
-// extra dependency.
-#if !defined(OS_ANDROID) && !defined(__ANDROID__) && !defined(__ANDROID_HOST__)
TEST(ProcessMetricsTest, GetOpenFdCount) {
ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- const FilePath temp_path = temp_dir.GetPath();
+ const FilePath temp_path = temp_dir.path();
CommandLine child_command_line(GetMultiProcessTestChildBaseCommandLine());
child_command_line.AppendSwitchPath(kTempDirFlag, temp_path);
Process child = SpawnMultiProcessTestChild(
@@ -516,8 +513,6 @@ TEST(ProcessMetricsTest, GetOpenFdCount) {
EXPECT_EQ(0, open_fds);
ASSERT_TRUE(child.Terminate(0, true));
}
-#endif // !defined(__ANDROID__)
-
#endif // defined(OS_LINUX)
} // namespace debug
diff --git a/base/process/process_posix.cc b/base/process/process_posix.cc
index a1d84e9128..ba9b5447c0 100644
--- a/base/process/process_posix.cc
+++ b/base/process/process_posix.cc
@@ -9,7 +9,6 @@
#include <sys/resource.h>
#include <sys/wait.h>
-#include "base/debug/activity_tracker.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
@@ -267,17 +266,12 @@ Process Process::DeprecatedGetProcessFromHandle(ProcessHandle handle) {
return Process(handle);
}
-#if !defined(OS_LINUX) && !defined(OS_MACOSX)
+#if !defined(OS_LINUX)
// static
bool Process::CanBackgroundProcesses() {
return false;
}
-#endif // !defined(OS_LINUX) && !defined(OS_MACOSX)
-
-// static
-void Process::TerminateCurrentProcessImmediately(int exit_code) {
- _exit(exit_code);
-}
+#endif // !defined(OS_LINUX)
bool Process::IsValid() const {
return process_ != kNullProcessHandle;
@@ -320,12 +314,6 @@ bool Process::Terminate(int /*exit_code*/, bool wait) const {
if (result && wait) {
int tries = 60;
- if (RunningOnValgrind()) {
- // Wait for some extra time when running under Valgrind since the child
- // processes may take some time doing leak checking.
- tries *= 2;
- }
-
unsigned sleep_ms = 4;
// The process may not end immediately due to pending I/O
@@ -365,18 +353,15 @@ bool Process::Terminate(int /*exit_code*/, bool wait) const {
}
#endif // !defined(OS_NACL_NONSFI)
-bool Process::WaitForExit(int* exit_code) const {
+bool Process::WaitForExit(int* exit_code) {
return WaitForExitWithTimeout(TimeDelta::Max(), exit_code);
}
-bool Process::WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) const {
- // Record the event that this thread is blocking upon (for hang diagnosis).
- base::debug::ScopedProcessWaitActivity process_activity(this);
-
+bool Process::WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) {
return WaitForExitWithTimeoutImpl(Handle(), exit_code, timeout);
}
-#if !defined(OS_LINUX) && !defined(OS_MACOSX)
+#if !defined(OS_LINUX)
bool Process::IsProcessBackgrounded() const {
// See SetProcessBackgrounded().
DCHECK(IsValid());
@@ -384,13 +369,13 @@ bool Process::IsProcessBackgrounded() const {
}
bool Process::SetProcessBackgrounded(bool /*value*/) {
- // Not implemented for POSIX systems other than Linux and Mac. With POSIX, if
- // we were to lower the process priority we wouldn't be able to raise it back
- // to its initial priority.
+ // Not implemented for POSIX systems other than Linux. With POSIX, if we were
+ // to lower the process priority we wouldn't be able to raise it back to its
+ // initial priority.
NOTIMPLEMENTED();
return false;
}
-#endif // !defined(OS_LINUX) && !defined(OS_MACOSX)
+#endif // !defined(OS_LINUX)
int Process::GetPriority() const {
DCHECK(IsValid());
diff --git a/base/profiler/scoped_profile.h b/base/profiler/scoped_profile.h
index 4df6a1bc02..657150a0f1 100644
--- a/base/profiler/scoped_profile.h
+++ b/base/profiler/scoped_profile.h
@@ -16,38 +16,28 @@
#include "base/location.h"
#include "base/macros.h"
#include "base/profiler/tracked_time.h"
-#include "base/trace_event/heap_profiler.h"
#include "base/tracked_objects.h"
-// Two level indirection is required for correct macro substitution.
-#define PASTE_COUNTER_ON_NAME2(name, counter) name##counter
-#define PASTE_COUNTER_ON_NAME(name, counter) \
- PASTE_COUNTER_ON_NAME2(name, counter)
+#define PASTE_LINE_NUMBER_ON_NAME(name, line) name##line
-#define COUNTER_BASED_VARIABLE_NAME_FOR_PROFILING \
- PASTE_COUNTER_ON_NAME(some_profiler_variable_, __COUNTER__)
+#define LINE_BASED_VARIABLE_NAME_FOR_PROFILING \
+ PASTE_LINE_NUMBER_ON_NAME(some_profiler_variable_, __LINE__)
// Defines the containing scope as a profiled region. This allows developers to
// profile their code and see results on their about:profiler page, as well as
-// on the UMA dashboard and heap profiler.
-#define TRACK_RUN_IN_THIS_SCOPED_REGION(dispatch_function_name) \
- const ::tracked_objects::Location& location = \
- FROM_HERE_WITH_EXPLICIT_FUNCTION(#dispatch_function_name); \
- TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION \
- COUNTER_BASED_VARIABLE_NAME_FOR_PROFILING(location.file_name()); \
- ::tracked_objects::ScopedProfile COUNTER_BASED_VARIABLE_NAME_FOR_PROFILING( \
- location, ::tracked_objects::ScopedProfile::ENABLED)
+// on the UMA dashboard.
+#define TRACK_RUN_IN_THIS_SCOPED_REGION(dispatch_function_name) \
+ ::tracked_objects::ScopedProfile LINE_BASED_VARIABLE_NAME_FOR_PROFILING( \
+ FROM_HERE_WITH_EXPLICIT_FUNCTION(#dispatch_function_name), \
+ ::tracked_objects::ScopedProfile::ENABLED)
// Same as TRACK_RUN_IN_THIS_SCOPED_REGION except that there's an extra param
// which is concatenated with the function name for better filtering.
-#define TRACK_SCOPED_REGION(category_name, dispatch_function_name) \
- const ::tracked_objects::Location& location = \
- FROM_HERE_WITH_EXPLICIT_FUNCTION("[" category_name \
- "]" dispatch_function_name); \
- TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION \
- COUNTER_BASED_VARIABLE_NAME_FOR_PROFILING(location.file_name()); \
- ::tracked_objects::ScopedProfile COUNTER_BASED_VARIABLE_NAME_FOR_PROFILING( \
- location, ::tracked_objects::ScopedProfile::ENABLED)
+#define TRACK_SCOPED_REGION(category_name, dispatch_function_name) \
+ ::tracked_objects::ScopedProfile LINE_BASED_VARIABLE_NAME_FOR_PROFILING( \
+ FROM_HERE_WITH_EXPLICIT_FUNCTION( \
+ "[" category_name "]" dispatch_function_name), \
+ ::tracked_objects::ScopedProfile::ENABLED)
namespace tracked_objects {
class Births;
diff --git a/base/rand_util_posix.cc b/base/rand_util_posix.cc
index 469f7af9bf..6a6e05ada8 100644
--- a/base/rand_util_posix.cc
+++ b/base/rand_util_posix.cc
@@ -13,7 +13,6 @@
#include "base/files/file_util.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
-#include "base/posix/eintr_wrapper.h"
namespace {
@@ -23,7 +22,7 @@ namespace {
// we can use LazyInstance to handle opening it on the first access.
class URandomFd {
public:
- URandomFd() : fd_(HANDLE_EINTR(open("/dev/urandom", O_RDONLY | O_CLOEXEC))) {
+ URandomFd() : fd_(open("/dev/urandom", O_RDONLY)) {
DCHECK_GE(fd_, 0) << "Cannot open /dev/urandom: " << errno;
}
diff --git a/base/run_loop.cc b/base/run_loop.cc
index 4c19d3589f..a2322f8495 100644
--- a/base/run_loop.cc
+++ b/base/run_loop.cc
@@ -19,14 +19,12 @@ RunLoop::RunLoop()
running_(false),
quit_when_idle_received_(false),
weak_factory_(this) {
- DCHECK(loop_);
}
RunLoop::~RunLoop() {
}
void RunLoop::Run() {
- DCHECK(thread_checker_.CalledOnValidThread());
if (!BeforeRun())
return;
@@ -46,7 +44,6 @@ void RunLoop::RunUntilIdle() {
}
void RunLoop::Quit() {
- DCHECK(thread_checker_.CalledOnValidThread());
quit_called_ = true;
if (running_ && loop_->run_loop_ == this) {
// This is the inner-most RunLoop, so quit now.
@@ -55,7 +52,6 @@ void RunLoop::Quit() {
}
void RunLoop::QuitWhenIdle() {
- DCHECK(thread_checker_.CalledOnValidThread());
quit_when_idle_received_ = true;
}
diff --git a/base/run_loop.h b/base/run_loop.h
index 077d097ba9..635018f434 100644
--- a/base/run_loop.h
+++ b/base/run_loop.h
@@ -10,7 +10,6 @@
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
-#include "base/threading/thread_checker.h"
#include "build/build_config.h"
namespace base {
@@ -106,8 +105,6 @@ class BASE_EXPORT RunLoop {
// that we should quit Run once it becomes idle.
bool quit_when_idle_received_;
- base::ThreadChecker thread_checker_;
-
// WeakPtrFactory for QuitClosure safety.
base::WeakPtrFactory<RunLoop> weak_factory_;
diff --git a/base/scoped_generic.h b/base/scoped_generic.h
index c2d51cfdb4..84de6b7d50 100644
--- a/base/scoped_generic.h
+++ b/base/scoped_generic.h
@@ -14,7 +14,7 @@
namespace base {
-// This class acts like unique_ptr with a custom deleter (although is slightly
+// This class acts like ScopedPtr with a custom deleter (although is slightly
// less fancy in some of the more escoteric respects) except that it keeps a
// copy of the object rather than a pointer, and we require that the contained
// object has some kind of "invalid" value.
@@ -22,12 +22,12 @@ namespace base {
// Defining a scoper based on this class allows you to get a scoper for
// non-pointer types without having to write custom code for set, reset, and
// move, etc. and get almost identical semantics that people are used to from
-// unique_ptr.
+// scoped_ptr.
//
// It is intended that you will typedef this class with an appropriate deleter
// to implement clean up tasks for objects that act like pointers from a
// resource management standpoint but aren't, such as file descriptors and
-// various types of operating system handles. Using unique_ptr for these
+// various types of operating system handles. Using scoped_ptr for these
// things requires that you keep a pointer to the handle valid for the lifetime
// of the scoper (which is easy to mess up).
//
@@ -97,7 +97,7 @@ class ScopedGeneric {
}
// Frees the currently owned object, if any. Then takes ownership of a new
- // object, if given. Self-resets are not allowd as on unique_ptr. See
+ // object, if given. Self-resets are not allowd as on scoped_ptr. See
// http://crbug.com/162971
void reset(const element_type& value = traits_type::InvalidValue()) {
if (data_.generic != traits_type::InvalidValue() && data_.generic == value)
diff --git a/base/scoped_observer.h b/base/scoped_observer.h
index 7f1d6fba96..13d7ca8bb1 100644
--- a/base/scoped_observer.h
+++ b/base/scoped_observer.h
@@ -47,7 +47,7 @@ class ScopedObserver {
}
bool IsObserving(Source* source) const {
- return base::ContainsValue(sources_, source);
+ return ContainsValue(sources_, source);
}
bool IsObservingSources() const { return !sources_.empty(); }
diff --git a/base/security_unittest.cc b/base/security_unittest.cc
index 519c997eb0..af9d2bf19d 100644
--- a/base/security_unittest.cc
+++ b/base/security_unittest.cc
@@ -87,30 +87,31 @@ void OverflowTestsSoftExpectTrue(bool overflow_detected) {
}
}
-#if defined(OS_IOS) || defined(ADDRESS_SANITIZER) || \
- defined(THREAD_SANITIZER) || defined(MEMORY_SANITIZER)
+#if defined(OS_IOS) || defined(OS_WIN) || defined(OS_LINUX)
#define MAYBE_NewOverflow DISABLED_NewOverflow
#else
#define MAYBE_NewOverflow NewOverflow
#endif
// Test array[TooBig][X] and array[X][TooBig] allocations for int overflows.
// IOS doesn't honor nothrow, so disable the test there.
-// Disabled under XSan because asan aborts when new returns nullptr,
-// https://bugs.chromium.org/p/chromium/issues/detail?id=690271#c15
+// Crashes on Windows Dbg builds, disable there as well.
+// Disabled on Linux because failing Linux Valgrind bot, and Valgrind exclusions
+// are not currently read. See http://crbug.com/582398
TEST(SecurityTest, MAYBE_NewOverflow) {
const size_t kArraySize = 4096;
// We want something "dynamic" here, so that the compiler doesn't
// immediately reject crazy arrays.
const size_t kDynamicArraySize = HideValueFromCompiler(kArraySize);
- const size_t kMaxSizeT = std::numeric_limits<size_t>::max();
+ // numeric_limits are still not constexpr until we switch to C++11, so we
+ // use an ugly cast.
+ const size_t kMaxSizeT = ~static_cast<size_t>(0);
+ ASSERT_EQ(numeric_limits<size_t>::max(), kMaxSizeT);
const size_t kArraySize2 = kMaxSizeT / kArraySize + 10;
const size_t kDynamicArraySize2 = HideValueFromCompiler(kArraySize2);
{
std::unique_ptr<char[][kArraySize]> array_pointer(
new (nothrow) char[kDynamicArraySize2][kArraySize]);
- // Prevent clang from optimizing away the whole test.
- char* volatile p = reinterpret_cast<char*>(array_pointer.get());
- OverflowTestsSoftExpectTrue(!p);
+ OverflowTestsSoftExpectTrue(!array_pointer);
}
// On windows, the compiler prevents static array sizes of more than
// 0x7fffffff (error C2148).
@@ -120,9 +121,7 @@ TEST(SecurityTest, MAYBE_NewOverflow) {
{
std::unique_ptr<char[][kArraySize2]> array_pointer(
new (nothrow) char[kDynamicArraySize][kArraySize2]);
- // Prevent clang from optimizing away the whole test.
- char* volatile p = reinterpret_cast<char*>(array_pointer.get());
- OverflowTestsSoftExpectTrue(!p);
+ OverflowTestsSoftExpectTrue(!array_pointer);
}
#endif // !defined(OS_WIN) || !defined(ARCH_CPU_64_BITS)
}
diff --git a/base/sequence_checker.h b/base/sequence_checker.h
index 471631844b..ad0182825c 100644
--- a/base/sequence_checker.h
+++ b/base/sequence_checker.h
@@ -5,6 +5,13 @@
#ifndef BASE_SEQUENCE_CHECKER_H_
#define BASE_SEQUENCE_CHECKER_H_
+// See comments for the similar block in thread_checker.h.
+#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
+#define ENABLE_SEQUENCE_CHECKER 1
+#else
+#define ENABLE_SEQUENCE_CHECKER 0
+#endif
+
#include "base/sequence_checker_impl.h"
namespace base {
@@ -15,22 +22,23 @@ namespace base {
// the right version for your build configuration.
class SequenceCheckerDoNothing {
public:
- bool CalledOnValidSequence() const { return true; }
+ bool CalledOnValidSequencedThread() const {
+ return true;
+ }
void DetachFromSequence() {}
};
-// SequenceChecker is a helper class to verify that calls to some methods of a
-// class are sequenced. Calls are sequenced when they are issued:
-// - From tasks posted to SequencedTaskRunners or SingleThreadTaskRunners bound
-// to the same sequence, or,
-// - From a single thread outside of any task.
+// SequenceChecker is a helper class used to help verify that some
+// methods of a class are called in sequence -- that is, called from
+// the same SequencedTaskRunner. It is a generalization of
+// ThreadChecker; see comments in sequence_checker_impl.h for details.
//
// Example:
// class MyClass {
// public:
// void Foo() {
-// DCHECK(sequence_checker_.CalledOnValidSequence());
+// DCHECK(sequence_checker_.CalledOnValidSequencedThread());
// ... (do stuff) ...
// }
//
@@ -38,14 +46,16 @@ class SequenceCheckerDoNothing {
// SequenceChecker sequence_checker_;
// }
//
-// In Release mode, CalledOnValidSequence() will always return true.
-#if DCHECK_IS_ON()
+// In Release mode, CalledOnValidSequencedThread() will always return true.
+#if ENABLE_SEQUENCE_CHECKER
class SequenceChecker : public SequenceCheckerImpl {
};
#else
class SequenceChecker : public SequenceCheckerDoNothing {
};
-#endif // DCHECK_IS_ON()
+#endif // ENABLE_SEQUENCE_CHECKER
+
+#undef ENABLE_SEQUENCE_CHECKER
} // namespace base
diff --git a/base/sequence_checker_impl.cc b/base/sequence_checker_impl.cc
index df2a8cb24f..e95b8ee5f3 100644
--- a/base/sequence_checker_impl.cc
+++ b/base/sequence_checker_impl.cc
@@ -4,66 +4,43 @@
#include "base/sequence_checker_impl.h"
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/sequence_token.h"
-#include "base/threading/sequenced_worker_pool.h"
-#include "base/threading/thread_checker_impl.h"
-
namespace base {
-class SequenceCheckerImpl::Core {
- public:
- Core()
- : sequence_token_(SequenceToken::GetForCurrentThread()),
- sequenced_worker_pool_token_(
- SequencedWorkerPool::GetSequenceTokenForCurrentThread()) {
- // SequencedWorkerPool doesn't use SequenceToken and code outside of
- // SequenceWorkerPool doesn't set a SequencedWorkerPool token.
- DCHECK(!sequence_token_.IsValid() ||
- !sequenced_worker_pool_token_.IsValid());
- }
-
- ~Core() = default;
+SequenceCheckerImpl::SequenceCheckerImpl()
+ : sequence_token_assigned_(false) {
+ AutoLock auto_lock(lock_);
+ EnsureSequenceTokenAssigned();
+}
- bool CalledOnValidThread() const {
- if (sequence_token_.IsValid())
- return sequence_token_ == SequenceToken::GetForCurrentThread();
+SequenceCheckerImpl::~SequenceCheckerImpl() {}
- if (sequenced_worker_pool_token_.IsValid()) {
- return sequenced_worker_pool_token_.Equals(
- SequencedWorkerPool::GetSequenceTokenForCurrentThread());
- }
+bool SequenceCheckerImpl::CalledOnValidSequencedThread() const {
+ AutoLock auto_lock(lock_);
+ EnsureSequenceTokenAssigned();
- // SequenceChecker behaves as a ThreadChecker when it is not bound to a
- // valid sequence token.
+ // If this thread is not associated with a SequencedWorkerPool,
+ // SequenceChecker behaves as a ThreadChecker. See header for details.
+ if (!sequence_token_.IsValid())
return thread_checker_.CalledOnValidThread();
- }
-
- private:
- SequenceToken sequence_token_;
-
- // TODO(gab): Remove this when SequencedWorkerPool is deprecated in favor of
- // TaskScheduler. crbug.com/622400
- SequencedWorkerPool::SequenceToken sequenced_worker_pool_token_;
- // Used when |sequenced_worker_pool_token_| and |sequence_token_| are invalid.
- ThreadCheckerImpl thread_checker_;
-};
-
-SequenceCheckerImpl::SequenceCheckerImpl() : core_(MakeUnique<Core>()) {}
-SequenceCheckerImpl::~SequenceCheckerImpl() = default;
-
-bool SequenceCheckerImpl::CalledOnValidSequence() const {
- AutoLock auto_lock(lock_);
- if (!core_)
- core_ = MakeUnique<Core>();
- return core_->CalledOnValidThread();
+ return sequence_token_.Equals(
+ SequencedWorkerPool::GetSequenceTokenForCurrentThread());
}
void SequenceCheckerImpl::DetachFromSequence() {
AutoLock auto_lock(lock_);
- core_.reset();
+ thread_checker_.DetachFromThread();
+ sequence_token_assigned_ = false;
+ sequence_token_ = SequencedWorkerPool::SequenceToken();
+}
+
+void SequenceCheckerImpl::EnsureSequenceTokenAssigned() const {
+ lock_.AssertAcquired();
+ if (sequence_token_assigned_)
+ return;
+
+ sequence_token_assigned_ = true;
+ sequence_token_ = SequencedWorkerPool::GetSequenceTokenForCurrentThread();
}
} // namespace base
diff --git a/base/sequence_checker_impl.h b/base/sequence_checker_impl.h
index a54c388451..e3c5fed508 100644
--- a/base/sequence_checker_impl.h
+++ b/base/sequence_checker_impl.h
@@ -5,40 +5,44 @@
#ifndef BASE_SEQUENCE_CHECKER_IMPL_H_
#define BASE_SEQUENCE_CHECKER_IMPL_H_
-#include <memory>
-
#include "base/base_export.h"
-#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/synchronization/lock.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "base/threading/thread_checker_impl.h"
namespace base {
-// Real implementation of SequenceChecker for use in debug mode or for temporary
-// use in release mode (e.g. to CHECK on a threading issue seen only in the
-// wild).
-//
-// Note: You should almost always use the SequenceChecker class to get the right
-// version for your build configuration.
+// SequenceCheckerImpl is used to help verify that some methods of a
+// class are called in sequence -- that is, called from the same
+// SequencedTaskRunner. It is a generalization of ThreadChecker; in
+// particular, it behaves exactly like ThreadChecker if constructed
+// on a thread that is not part of a SequencedWorkerPool.
class BASE_EXPORT SequenceCheckerImpl {
public:
SequenceCheckerImpl();
~SequenceCheckerImpl();
- // Returns true if called in sequence with previous calls to this method and
- // the constructor.
- bool CalledOnValidSequence() const WARN_UNUSED_RESULT;
+ // Returns whether the we are being called on the same sequence token
+ // as previous calls. If there is no associated sequence, then returns
+ // whether we are being called on the underlying ThreadChecker's thread.
+ bool CalledOnValidSequencedThread() const;
- // Unbinds the checker from the currently associated sequence. The checker
- // will be re-bound on the next call to CalledOnValidSequence().
+ // Unbinds the checker from the currently associated sequence. The
+ // checker will be re-bound on the next call to CalledOnValidSequence().
void DetachFromSequence();
private:
- class Core;
+ void EnsureSequenceTokenAssigned() const;
// Guards all variables below.
mutable Lock lock_;
- mutable std::unique_ptr<Core> core_;
+
+ // Used if |sequence_token_| is not valid.
+ ThreadCheckerImpl thread_checker_;
+ mutable bool sequence_token_assigned_;
+
+ mutable SequencedWorkerPool::SequenceToken sequence_token_;
DISALLOW_COPY_AND_ASSIGN(SequenceCheckerImpl);
};
diff --git a/base/sequence_checker_unittest.cc b/base/sequence_checker_unittest.cc
index 86e9298d97..196bb1cc79 100644
--- a/base/sequence_checker_unittest.cc
+++ b/base/sequence_checker_unittest.cc
@@ -2,257 +2,334 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/sequence_checker.h"
+
#include <stddef.h>
#include <memory>
-#include <string>
+#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
-#include "base/callback_forward.h"
+#include "base/location.h"
+#include "base/logging.h"
#include "base/macros.h"
-#include "base/message_loop/message_loop.h"
-#include "base/sequence_checker_impl.h"
-#include "base/sequence_token.h"
+#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
#include "base/test/sequenced_worker_pool_owner.h"
-#include "base/threading/simple_thread.h"
+#include "base/threading/thread.h"
#include "testing/gtest/include/gtest/gtest.h"
+// Duplicated from base/sequence_checker.h so that we can be good citizens
+// there and undef the macro.
+#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
+#define ENABLE_SEQUENCE_CHECKER 1
+#else
+#define ENABLE_SEQUENCE_CHECKER 0
+#endif
+
namespace base {
namespace {
-constexpr size_t kNumWorkerThreads = 3;
+const size_t kNumWorkerThreads = 3;
-// Runs a callback on another thread.
-class RunCallbackThread : public SimpleThread {
+// Simple class to exercise the basics of SequenceChecker.
+// DoStuff should verify that it's called on a valid sequenced thread.
+// SequenceCheckedObject can be destroyed on any thread (like WeakPtr).
+class SequenceCheckedObject {
public:
- explicit RunCallbackThread(const Closure& callback)
- : SimpleThread("RunCallbackThread"), callback_(callback) {
- Start();
- Join();
+ SequenceCheckedObject() {}
+ ~SequenceCheckedObject() {}
+
+ // Verifies that it was called on the same thread as the constructor.
+ void DoStuff() {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
}
- private:
- // SimpleThread:
- void Run() override { callback_.Run(); }
+ void DetachFromSequence() {
+ sequence_checker_.DetachFromSequence();
+ }
- const Closure callback_;
+ private:
+ SequenceChecker sequence_checker_;
- DISALLOW_COPY_AND_ASSIGN(RunCallbackThread);
+ DISALLOW_COPY_AND_ASSIGN(SequenceCheckedObject);
};
class SequenceCheckerTest : public testing::Test {
+ public:
+ SequenceCheckerTest() : other_thread_("sequence_checker_test_other_thread") {}
+
+ void SetUp() override {
+ other_thread_.Start();
+ ResetPool();
+ }
+
+ void TearDown() override {
+ other_thread_.Stop();
+ }
+
protected:
- SequenceCheckerTest() : pool_owner_(kNumWorkerThreads, "test") {}
+ base::Thread* other_thread() { return &other_thread_; }
- void PostToSequencedWorkerPool(const Closure& callback,
- const std::string& token_name) {
- pool_owner_.pool()->PostNamedSequencedWorkerTask(token_name, FROM_HERE,
- callback);
+ const scoped_refptr<SequencedWorkerPool>& pool() {
+ return pool_owner_->pool();
}
- void FlushSequencedWorkerPoolForTesting() {
- pool_owner_.pool()->FlushForTesting();
+ void PostDoStuffToWorkerPool(SequenceCheckedObject* sequence_checked_object,
+ const std::string& token_name) {
+ pool()->PostNamedSequencedWorkerTask(
+ token_name,
+ FROM_HERE,
+ base::Bind(&SequenceCheckedObject::DoStuff,
+ base::Unretained(sequence_checked_object)));
}
+ void PostDoStuffToOtherThread(
+ SequenceCheckedObject* sequence_checked_object) {
+ other_thread()->task_runner()->PostTask(
+ FROM_HERE, base::Bind(&SequenceCheckedObject::DoStuff,
+ base::Unretained(sequence_checked_object)));
+ }
+
+ void PostDeleteToOtherThread(
+ std::unique_ptr<SequenceCheckedObject> sequence_checked_object) {
+ other_thread()->message_loop()->task_runner()->DeleteSoon(
+ FROM_HERE, sequence_checked_object.release());
+ }
+
+ // Destroys the SequencedWorkerPool instance, blocking until it is fully shut
+ // down, and creates a new instance.
+ void ResetPool() {
+ pool_owner_.reset(new SequencedWorkerPoolOwner(kNumWorkerThreads, "test"));
+ }
+
+ void MethodOnDifferentThreadDeathTest();
+ void DetachThenCallFromDifferentThreadDeathTest();
+ void DifferentSequenceTokensDeathTest();
+ void WorkerPoolAndSimpleThreadDeathTest();
+ void TwoDifferentWorkerPoolsDeathTest();
+
private:
MessageLoop message_loop_; // Needed by SequencedWorkerPool to function.
- SequencedWorkerPoolOwner pool_owner_;
-
- DISALLOW_COPY_AND_ASSIGN(SequenceCheckerTest);
+ base::Thread other_thread_;
+ std::unique_ptr<SequencedWorkerPoolOwner> pool_owner_;
};
-void ExpectCalledOnValidSequence(SequenceCheckerImpl* sequence_checker) {
- ASSERT_TRUE(sequence_checker);
+TEST_F(SequenceCheckerTest, CallsAllowedOnSameThread) {
+ std::unique_ptr<SequenceCheckedObject> sequence_checked_object(
+ new SequenceCheckedObject);
- // This should bind |sequence_checker| to the current sequence if it wasn't
- // already bound to a sequence.
- EXPECT_TRUE(sequence_checker->CalledOnValidSequence());
+ // Verify that DoStuff doesn't assert.
+ sequence_checked_object->DoStuff();
- // Since |sequence_checker| is now bound to the current sequence, another call
- // to CalledOnValidSequence() should return true.
- EXPECT_TRUE(sequence_checker->CalledOnValidSequence());
+ // Verify that the destructor doesn't assert.
+ sequence_checked_object.reset();
}
-void ExpectCalledOnValidSequenceWithSequenceToken(
- SequenceCheckerImpl* sequence_checker,
- SequenceToken sequence_token) {
- ScopedSetSequenceTokenForCurrentThread
- scoped_set_sequence_token_for_current_thread(sequence_token);
- ExpectCalledOnValidSequence(sequence_checker);
-}
+TEST_F(SequenceCheckerTest, DestructorAllowedOnDifferentThread) {
+ std::unique_ptr<SequenceCheckedObject> sequence_checked_object(
+ new SequenceCheckedObject);
-void ExpectNotCalledOnValidSequence(SequenceCheckerImpl* sequence_checker) {
- ASSERT_TRUE(sequence_checker);
- EXPECT_FALSE(sequence_checker->CalledOnValidSequence());
+ // Verify the destructor doesn't assert when called on a different thread.
+ PostDeleteToOtherThread(std::move(sequence_checked_object));
+ other_thread()->Stop();
}
-} // namespace
-
-TEST_F(SequenceCheckerTest, CallsAllowedOnSameThreadNoSequenceToken) {
- SequenceCheckerImpl sequence_checker;
- EXPECT_TRUE(sequence_checker.CalledOnValidSequence());
-}
+TEST_F(SequenceCheckerTest, DetachFromSequence) {
+ std::unique_ptr<SequenceCheckedObject> sequence_checked_object(
+ new SequenceCheckedObject);
-TEST_F(SequenceCheckerTest, CallsAllowedOnSameThreadSameSequenceToken) {
- ScopedSetSequenceTokenForCurrentThread
- scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
- SequenceCheckerImpl sequence_checker;
- EXPECT_TRUE(sequence_checker.CalledOnValidSequence());
-}
+ // Verify that DoStuff doesn't assert when called on a different thread after
+ // a call to DetachFromSequence.
+ sequence_checked_object->DetachFromSequence();
-TEST_F(SequenceCheckerTest, CallsDisallowedOnDifferentThreadsNoSequenceToken) {
- SequenceCheckerImpl sequence_checker;
- RunCallbackThread thread(
- Bind(&ExpectNotCalledOnValidSequence, Unretained(&sequence_checker)));
+ PostDoStuffToOtherThread(sequence_checked_object.get());
+ other_thread()->Stop();
}
-TEST_F(SequenceCheckerTest, CallsAllowedOnDifferentThreadsSameSequenceToken) {
- const SequenceToken sequence_token(SequenceToken::Create());
+TEST_F(SequenceCheckerTest, SameSequenceTokenValid) {
+ std::unique_ptr<SequenceCheckedObject> sequence_checked_object(
+ new SequenceCheckedObject);
- ScopedSetSequenceTokenForCurrentThread
- scoped_set_sequence_token_for_current_thread(sequence_token);
- SequenceCheckerImpl sequence_checker;
- EXPECT_TRUE(sequence_checker.CalledOnValidSequence());
+ sequence_checked_object->DetachFromSequence();
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
+ pool()->FlushForTesting();
- RunCallbackThread thread(Bind(&ExpectCalledOnValidSequenceWithSequenceToken,
- Unretained(&sequence_checker), sequence_token));
+ PostDeleteToOtherThread(std::move(sequence_checked_object));
+ other_thread()->Stop();
}
-TEST_F(SequenceCheckerTest, CallsDisallowedOnSameThreadDifferentSequenceToken) {
- std::unique_ptr<SequenceCheckerImpl> sequence_checker;
+TEST_F(SequenceCheckerTest, DetachSequenceTokenValid) {
+ std::unique_ptr<SequenceCheckedObject> sequence_checked_object(
+ new SequenceCheckedObject);
- {
- ScopedSetSequenceTokenForCurrentThread
- scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
- sequence_checker.reset(new SequenceCheckerImpl);
- }
+ sequence_checked_object->DetachFromSequence();
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
+ pool()->FlushForTesting();
- {
- // Different SequenceToken.
- ScopedSetSequenceTokenForCurrentThread
- scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
- EXPECT_FALSE(sequence_checker->CalledOnValidSequence());
- }
+ sequence_checked_object->DetachFromSequence();
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "B");
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "B");
+ pool()->FlushForTesting();
- // No SequenceToken.
- EXPECT_FALSE(sequence_checker->CalledOnValidSequence());
+ PostDeleteToOtherThread(std::move(sequence_checked_object));
+ other_thread()->Stop();
}
-TEST_F(SequenceCheckerTest, DetachFromSequence) {
- std::unique_ptr<SequenceCheckerImpl> sequence_checker;
+#if GTEST_HAS_DEATH_TEST || !ENABLE_SEQUENCE_CHECKER
- {
- ScopedSetSequenceTokenForCurrentThread
- scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
- sequence_checker.reset(new SequenceCheckerImpl);
- }
+void SequenceCheckerTest::MethodOnDifferentThreadDeathTest() {
+ std::unique_ptr<SequenceCheckedObject> sequence_checked_object(
+ new SequenceCheckedObject);
- sequence_checker->DetachFromSequence();
+ // DoStuff should assert in debug builds only when called on a
+ // different thread.
+ PostDoStuffToOtherThread(sequence_checked_object.get());
+ other_thread()->Stop();
+}
- {
- // Verify that CalledOnValidSequence() returns true when called with
- // a different sequence token after a call to DetachFromSequence().
- ScopedSetSequenceTokenForCurrentThread
- scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
- EXPECT_TRUE(sequence_checker->CalledOnValidSequence());
- }
+#if ENABLE_SEQUENCE_CHECKER
+TEST_F(SequenceCheckerTest, MethodNotAllowedOnDifferentThreadDeathTestInDebug) {
+ // The default style "fast" does not support multi-threaded tests.
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+ ASSERT_DEATH({
+ MethodOnDifferentThreadDeathTest();
+ }, "");
+}
+#else
+TEST_F(SequenceCheckerTest, MethodAllowedOnDifferentThreadDeathTestInRelease) {
+ MethodOnDifferentThreadDeathTest();
}
+#endif // ENABLE_SEQUENCE_CHECKER
-TEST_F(SequenceCheckerTest, DetachFromSequenceNoSequenceToken) {
- SequenceCheckerImpl sequence_checker;
- sequence_checker.DetachFromSequence();
+void SequenceCheckerTest::DetachThenCallFromDifferentThreadDeathTest() {
+ std::unique_ptr<SequenceCheckedObject> sequence_checked_object(
+ new SequenceCheckedObject);
- // Verify that CalledOnValidSequence() returns true when called on a
- // different thread after a call to DetachFromSequence().
- RunCallbackThread thread(
- Bind(&ExpectCalledOnValidSequence, Unretained(&sequence_checker)));
+ // DoStuff doesn't assert when called on a different thread
+ // after a call to DetachFromSequence.
+ sequence_checked_object->DetachFromSequence();
+ PostDoStuffToOtherThread(sequence_checked_object.get());
+ other_thread()->Stop();
- EXPECT_FALSE(sequence_checker.CalledOnValidSequence());
+ // DoStuff should assert in debug builds only after moving to
+ // another thread.
+ sequence_checked_object->DoStuff();
}
-TEST_F(SequenceCheckerTest, SequencedWorkerPool_SameSequenceTokenValid) {
- SequenceCheckerImpl sequence_checker;
- sequence_checker.DetachFromSequence();
-
- PostToSequencedWorkerPool(
- Bind(&ExpectCalledOnValidSequence, Unretained(&sequence_checker)), "A");
- PostToSequencedWorkerPool(
- Bind(&ExpectCalledOnValidSequence, Unretained(&sequence_checker)), "A");
- FlushSequencedWorkerPoolForTesting();
+#if ENABLE_SEQUENCE_CHECKER
+TEST_F(SequenceCheckerTest, DetachFromSequenceDeathTestInDebug) {
+ // The default style "fast" does not support multi-threaded tests.
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+ ASSERT_DEATH({
+ DetachThenCallFromDifferentThreadDeathTest();
+ }, "");
}
+#else
+TEST_F(SequenceCheckerTest, DetachFromThreadDeathTestInRelease) {
+ DetachThenCallFromDifferentThreadDeathTest();
+}
+#endif // ENABLE_SEQUENCE_CHECKER
-TEST_F(SequenceCheckerTest, SequencedWorkerPool_DetachSequenceTokenValid) {
- SequenceCheckerImpl sequence_checker;
- sequence_checker.DetachFromSequence();
-
- PostToSequencedWorkerPool(
- Bind(&ExpectCalledOnValidSequence, Unretained(&sequence_checker)), "A");
- PostToSequencedWorkerPool(
- Bind(&ExpectCalledOnValidSequence, Unretained(&sequence_checker)), "A");
- FlushSequencedWorkerPoolForTesting();
+void SequenceCheckerTest::DifferentSequenceTokensDeathTest() {
+ std::unique_ptr<SequenceCheckedObject> sequence_checked_object(
+ new SequenceCheckedObject);
- sequence_checker.DetachFromSequence();
+ sequence_checked_object->DetachFromSequence();
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "B");
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "B");
+ pool()->FlushForTesting();
- PostToSequencedWorkerPool(
- Bind(&ExpectCalledOnValidSequence, Unretained(&sequence_checker)), "B");
- PostToSequencedWorkerPool(
- Bind(&ExpectCalledOnValidSequence, Unretained(&sequence_checker)), "B");
- FlushSequencedWorkerPoolForTesting();
+ PostDeleteToOtherThread(std::move(sequence_checked_object));
+ other_thread()->Stop();
}
-TEST_F(SequenceCheckerTest,
- SequencedWorkerPool_DifferentSequenceTokensInvalid) {
- SequenceCheckerImpl sequence_checker;
- sequence_checker.DetachFromSequence();
-
- PostToSequencedWorkerPool(
- Bind(&ExpectCalledOnValidSequence, Unretained(&sequence_checker)), "A");
- PostToSequencedWorkerPool(
- Bind(&ExpectCalledOnValidSequence, Unretained(&sequence_checker)), "A");
- FlushSequencedWorkerPoolForTesting();
-
- PostToSequencedWorkerPool(
- Bind(&ExpectNotCalledOnValidSequence, Unretained(&sequence_checker)),
- "B");
- PostToSequencedWorkerPool(
- Bind(&ExpectNotCalledOnValidSequence, Unretained(&sequence_checker)),
- "B");
- FlushSequencedWorkerPoolForTesting();
+#if ENABLE_SEQUENCE_CHECKER
+TEST_F(SequenceCheckerTest, DifferentSequenceTokensDeathTestInDebug) {
+ // The default style "fast" does not support multi-threaded tests.
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+ ASSERT_DEATH({
+ DifferentSequenceTokensDeathTest();
+ }, "");
}
+#else
+TEST_F(SequenceCheckerTest, DifferentSequenceTokensDeathTestInRelease) {
+ DifferentSequenceTokensDeathTest();
+}
+#endif // ENABLE_SEQUENCE_CHECKER
+
+void SequenceCheckerTest::WorkerPoolAndSimpleThreadDeathTest() {
+ std::unique_ptr<SequenceCheckedObject> sequence_checked_object(
+ new SequenceCheckedObject);
-TEST_F(SequenceCheckerTest,
- SequencedWorkerPool_WorkerPoolAndSimpleThreadInvalid) {
- SequenceCheckerImpl sequence_checker;
- sequence_checker.DetachFromSequence();
+ sequence_checked_object->DetachFromSequence();
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
+ pool()->FlushForTesting();
- PostToSequencedWorkerPool(
- Bind(&ExpectCalledOnValidSequence, Unretained(&sequence_checker)), "A");
- PostToSequencedWorkerPool(
- Bind(&ExpectCalledOnValidSequence, Unretained(&sequence_checker)), "A");
- FlushSequencedWorkerPoolForTesting();
+ PostDoStuffToOtherThread(sequence_checked_object.get());
+ other_thread()->Stop();
+}
- EXPECT_FALSE(sequence_checker.CalledOnValidSequence());
+#if ENABLE_SEQUENCE_CHECKER
+TEST_F(SequenceCheckerTest, WorkerPoolAndSimpleThreadDeathTestInDebug) {
+ // The default style "fast" does not support multi-threaded tests.
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+ ASSERT_DEATH({
+ WorkerPoolAndSimpleThreadDeathTest();
+ }, "");
}
+#else
+TEST_F(SequenceCheckerTest, WorkerPoolAndSimpleThreadDeathTestInRelease) {
+ WorkerPoolAndSimpleThreadDeathTest();
+}
+#endif // ENABLE_SEQUENCE_CHECKER
-TEST_F(SequenceCheckerTest,
- SequencedWorkerPool_TwoDifferentWorkerPoolsInvalid) {
- SequenceCheckerImpl sequence_checker;
- sequence_checker.DetachFromSequence();
+void SequenceCheckerTest::TwoDifferentWorkerPoolsDeathTest() {
+ std::unique_ptr<SequenceCheckedObject> sequence_checked_object(
+ new SequenceCheckedObject);
- PostToSequencedWorkerPool(
- Bind(&ExpectCalledOnValidSequence, Unretained(&sequence_checker)), "A");
- PostToSequencedWorkerPool(
- Bind(&ExpectCalledOnValidSequence, Unretained(&sequence_checker)), "A");
- FlushSequencedWorkerPoolForTesting();
+ sequence_checked_object->DetachFromSequence();
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
+ pool()->FlushForTesting();
SequencedWorkerPoolOwner second_pool_owner(kNumWorkerThreads, "test2");
second_pool_owner.pool()->PostNamedSequencedWorkerTask(
- "A", FROM_HERE, base::Bind(&ExpectNotCalledOnValidSequence,
- base::Unretained(&sequence_checker)));
+ "A",
+ FROM_HERE,
+ base::Bind(&SequenceCheckedObject::DoStuff,
+ base::Unretained(sequence_checked_object.get())));
second_pool_owner.pool()->FlushForTesting();
}
+#if ENABLE_SEQUENCE_CHECKER
+TEST_F(SequenceCheckerTest, TwoDifferentWorkerPoolsDeathTestInDebug) {
+ // The default style "fast" does not support multi-threaded tests.
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+ ASSERT_DEATH({
+ TwoDifferentWorkerPoolsDeathTest();
+ }, "");
+}
+#else
+TEST_F(SequenceCheckerTest, TwoDifferentWorkerPoolsDeathTestInRelease) {
+ TwoDifferentWorkerPoolsDeathTest();
+}
+#endif // ENABLE_SEQUENCE_CHECKER
+
+#endif // GTEST_HAS_DEATH_TEST || !ENABLE_SEQUENCE_CHECKER
+
+} // namespace
+
} // namespace base
+
+// Just in case we ever get lumped together with other compilation units.
+#undef ENABLE_SEQUENCE_CHECKER
diff --git a/base/sequence_token.cc b/base/sequence_token.cc
deleted file mode 100644
index 264e3b65e3..0000000000
--- a/base/sequence_token.cc
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2016 The Chromium 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/sequence_token.h"
-
-#include "base/atomic_sequence_num.h"
-#include "base/lazy_instance.h"
-#include "base/logging.h"
-#include "base/threading/thread_local.h"
-
-namespace base {
-
-namespace {
-
-base::StaticAtomicSequenceNumber g_sequence_token_generator;
-
-base::StaticAtomicSequenceNumber g_task_token_generator;
-
-LazyInstance<ThreadLocalPointer<const SequenceToken>>::Leaky
- tls_current_sequence_token = LAZY_INSTANCE_INITIALIZER;
-
-LazyInstance<ThreadLocalPointer<const TaskToken>>::Leaky
- tls_current_task_token = LAZY_INSTANCE_INITIALIZER;
-
-} // namespace
-
-bool SequenceToken::operator==(const SequenceToken& other) const {
- return token_ == other.token_ && IsValid();
-}
-
-bool SequenceToken::operator!=(const SequenceToken& other) const {
- return !(*this == other);
-}
-
-bool SequenceToken::IsValid() const {
- return token_ != kInvalidSequenceToken;
-}
-
-int SequenceToken::ToInternalValue() const {
- return token_;
-}
-
-SequenceToken SequenceToken::Create() {
- return SequenceToken(g_sequence_token_generator.GetNext());
-}
-
-SequenceToken SequenceToken::GetForCurrentThread() {
- const SequenceToken* current_sequence_token =
- tls_current_sequence_token.Get().Get();
- return current_sequence_token ? *current_sequence_token : SequenceToken();
-}
-
-bool TaskToken::operator==(const TaskToken& other) const {
- return token_ == other.token_ && IsValid();
-}
-
-bool TaskToken::operator!=(const TaskToken& other) const {
- return !(*this == other);
-}
-
-bool TaskToken::IsValid() const {
- return token_ != kInvalidTaskToken;
-}
-
-TaskToken TaskToken::Create() {
- return TaskToken(g_task_token_generator.GetNext());
-}
-
-TaskToken TaskToken::GetForCurrentThread() {
- const TaskToken* current_task_token = tls_current_task_token.Get().Get();
- return current_task_token ? *current_task_token : TaskToken();
-}
-
-ScopedSetSequenceTokenForCurrentThread::ScopedSetSequenceTokenForCurrentThread(
- const SequenceToken& sequence_token)
- : sequence_token_(sequence_token), task_token_(TaskToken::Create()) {
- DCHECK(!tls_current_sequence_token.Get().Get());
- DCHECK(!tls_current_task_token.Get().Get());
- tls_current_sequence_token.Get().Set(&sequence_token_);
- tls_current_task_token.Get().Set(&task_token_);
-}
-
-ScopedSetSequenceTokenForCurrentThread::
- ~ScopedSetSequenceTokenForCurrentThread() {
- DCHECK_EQ(tls_current_sequence_token.Get().Get(), &sequence_token_);
- DCHECK_EQ(tls_current_task_token.Get().Get(), &task_token_);
- tls_current_sequence_token.Get().Set(nullptr);
- tls_current_task_token.Get().Set(nullptr);
-}
-
-} // namespace base
diff --git a/base/sequence_token.h b/base/sequence_token.h
deleted file mode 100644
index 6e7d191ae8..0000000000
--- a/base/sequence_token.h
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright 2016 The Chromium 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_SEQUENCE_TOKEN_H_
-#define BASE_SEQUENCE_TOKEN_H_
-
-#include "base/base_export.h"
-#include "base/macros.h"
-
-namespace base {
-
-// A token that identifies a series of sequenced tasks (i.e. tasks that run one
-// at a time in posting order).
-class BASE_EXPORT SequenceToken {
- public:
- // Instantiates an invalid SequenceToken.
- SequenceToken() = default;
-
- // Explicitly allow copy.
- SequenceToken(const SequenceToken& other) = default;
- SequenceToken& operator=(const SequenceToken& other) = default;
-
- // An invalid SequenceToken is not equal to any other SequenceToken, including
- // other invalid SequenceTokens.
- bool operator==(const SequenceToken& other) const;
- bool operator!=(const SequenceToken& other) const;
-
- // Returns true if this is a valid SequenceToken.
- bool IsValid() const;
-
- // Returns the integer uniquely representing this SequenceToken. This method
- // should only be used for tracing and debugging.
- int ToInternalValue() const;
-
- // Returns a valid SequenceToken which isn't equal to any previously returned
- // SequenceToken.
- static SequenceToken Create();
-
- // Returns the SequenceToken associated with the task running on the current
- // thread, as determined by the active ScopedSetSequenceTokenForCurrentThread
- // if any.
- static SequenceToken GetForCurrentThread();
-
- private:
- explicit SequenceToken(int token) : token_(token) {}
-
- static constexpr int kInvalidSequenceToken = -1;
- int token_ = kInvalidSequenceToken;
-};
-
-// A token that identifies a task.
-//
-// This is used by ThreadCheckerImpl to determine whether calls to
-// CalledOnValidThread() come from the same task and hence are deterministically
-// single-threaded (vs. calls coming from different sequenced or parallel tasks,
-// which may or may not run on the same thread).
-class BASE_EXPORT TaskToken {
- public:
- // Instantiates an invalid TaskToken.
- TaskToken() = default;
-
- // Explicitly allow copy.
- TaskToken(const TaskToken& other) = default;
- TaskToken& operator=(const TaskToken& other) = default;
-
- // An invalid TaskToken is not equal to any other TaskToken, including
- // other invalid TaskTokens.
- bool operator==(const TaskToken& other) const;
- bool operator!=(const TaskToken& other) const;
-
- // Returns true if this is a valid TaskToken.
- bool IsValid() const;
-
- // In the scope of a ScopedSetSequenceTokenForCurrentThread, returns a valid
- // TaskToken which isn't equal to any TaskToken returned in the scope of a
- // different ScopedSetSequenceTokenForCurrentThread. Otherwise, returns an
- // invalid TaskToken.
- static TaskToken GetForCurrentThread();
-
- private:
- friend class ScopedSetSequenceTokenForCurrentThread;
-
- explicit TaskToken(int token) : token_(token) {}
-
- // Returns a valid TaskToken which isn't equal to any previously returned
- // TaskToken. This is private as it only meant to be instantiated by
- // ScopedSetSequenceTokenForCurrentThread.
- static TaskToken Create();
-
- static constexpr int kInvalidTaskToken = -1;
- int token_ = kInvalidTaskToken;
-};
-
-// Instantiate this in the scope where a single task runs.
-class BASE_EXPORT ScopedSetSequenceTokenForCurrentThread {
- public:
- // Throughout the lifetime of the constructed object,
- // SequenceToken::GetForCurrentThread() will return |sequence_token| and
- // TaskToken::GetForCurrentThread() will return a TaskToken which is not equal
- // to any TaskToken returned in the scope of another
- // ScopedSetSequenceTokenForCurrentThread.
- ScopedSetSequenceTokenForCurrentThread(const SequenceToken& sequence_token);
- ~ScopedSetSequenceTokenForCurrentThread();
-
- private:
- const SequenceToken sequence_token_;
- const TaskToken task_token_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedSetSequenceTokenForCurrentThread);
-};
-
-} // namespace base
-
-#endif // BASE_SEQUENCE_TOKEN_H_
diff --git a/base/sequence_token_unittest.cc b/base/sequence_token_unittest.cc
deleted file mode 100644
index b0e69de42b..0000000000
--- a/base/sequence_token_unittest.cc
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright 2016 The Chromium 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/sequence_token.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace base {
-
-TEST(SequenceTokenTest, IsValid) {
- EXPECT_FALSE(SequenceToken().IsValid());
- EXPECT_TRUE(SequenceToken::Create().IsValid());
-}
-
-TEST(SequenceTokenTest, OperatorEquals) {
- SequenceToken invalid_a;
- SequenceToken invalid_b;
- const SequenceToken valid_a = SequenceToken::Create();
- const SequenceToken valid_b = SequenceToken::Create();
-
- EXPECT_FALSE(invalid_a == invalid_a);
- EXPECT_FALSE(invalid_a == invalid_b);
- EXPECT_FALSE(invalid_a == valid_a);
- EXPECT_FALSE(invalid_a == valid_b);
-
- EXPECT_FALSE(valid_a == invalid_a);
- EXPECT_FALSE(valid_a == invalid_b);
- EXPECT_EQ(valid_a, valid_a);
- EXPECT_FALSE(valid_a == valid_b);
-}
-
-TEST(SequenceTokenTest, OperatorNotEquals) {
- SequenceToken invalid_a;
- SequenceToken invalid_b;
- const SequenceToken valid_a = SequenceToken::Create();
- const SequenceToken valid_b = SequenceToken::Create();
-
- EXPECT_NE(invalid_a, invalid_a);
- EXPECT_NE(invalid_a, invalid_b);
- EXPECT_NE(invalid_a, valid_a);
- EXPECT_NE(invalid_a, valid_b);
-
- EXPECT_NE(valid_a, invalid_a);
- EXPECT_NE(valid_a, invalid_b);
- EXPECT_FALSE(valid_a != valid_a);
- EXPECT_NE(valid_a, valid_b);
-}
-
-TEST(SequenceTokenTest, GetForCurrentThread) {
- const SequenceToken token = SequenceToken::Create();
-
- EXPECT_FALSE(SequenceToken::GetForCurrentThread().IsValid());
-
- {
- ScopedSetSequenceTokenForCurrentThread
- scoped_set_sequence_token_for_current_thread(token);
- EXPECT_TRUE(SequenceToken::GetForCurrentThread().IsValid());
- EXPECT_EQ(token, SequenceToken::GetForCurrentThread());
- }
-
- EXPECT_FALSE(SequenceToken::GetForCurrentThread().IsValid());
-}
-
-TEST(SequenceTokenTest, ToInternalValue) {
- const SequenceToken token1 = SequenceToken::Create();
- const SequenceToken token2 = SequenceToken::Create();
-
- // Confirm that internal values are unique.
- EXPECT_NE(token1.ToInternalValue(), token2.ToInternalValue());
-}
-
-// Expect a default-constructed TaskToken to be invalid and not equal to
-// another invalid TaskToken.
-TEST(TaskTokenTest, InvalidDefaultConstructed) {
- EXPECT_FALSE(TaskToken().IsValid());
- EXPECT_NE(TaskToken(), TaskToken());
-}
-
-// Expect a TaskToken returned by TaskToken::GetForCurrentThread() outside the
-// scope of a ScopedSetSequenceTokenForCurrentThread to be invalid.
-TEST(TaskTokenTest, InvalidOutsideScope) {
- EXPECT_FALSE(TaskToken::GetForCurrentThread().IsValid());
-}
-
-// Expect an invalid TaskToken not to be equal with a valid TaskToken.
-TEST(TaskTokenTest, ValidNotEqualsInvalid) {
- ScopedSetSequenceTokenForCurrentThread
- scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
- TaskToken valid = TaskToken::GetForCurrentThread();
- TaskToken invalid;
- EXPECT_NE(valid, invalid);
-}
-
-// Expect TaskTokens returned by TaskToken::GetForCurrentThread() in the scope
-// of the same ScopedSetSequenceTokenForCurrentThread instance to be
-// valid and equal with each other.
-TEST(TaskTokenTest, EqualInSameScope) {
- ScopedSetSequenceTokenForCurrentThread
- scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
-
- const TaskToken token_a = TaskToken::GetForCurrentThread();
- const TaskToken token_b = TaskToken::GetForCurrentThread();
-
- EXPECT_TRUE(token_a.IsValid());
- EXPECT_TRUE(token_b.IsValid());
- EXPECT_EQ(token_a, token_b);
-}
-
-// Expect TaskTokens returned by TaskToken::GetForCurrentThread() in the scope
-// of different ScopedSetSequenceTokenForCurrentThread instances to be
-// valid but not equal to each other.
-TEST(TaskTokenTest, NotEqualInDifferentScopes) {
- TaskToken token_a;
- TaskToken token_b;
-
- {
- ScopedSetSequenceTokenForCurrentThread
- scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
- token_a = TaskToken::GetForCurrentThread();
- }
- {
- ScopedSetSequenceTokenForCurrentThread
- scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
- token_b = TaskToken::GetForCurrentThread();
- }
-
- EXPECT_TRUE(token_a.IsValid());
- EXPECT_TRUE(token_b.IsValid());
- EXPECT_NE(token_a, token_b);
-}
-
-} // namespace base
diff --git a/base/sequenced_task_runner.cc b/base/sequenced_task_runner.cc
index dc11ebc3f1..00d4048815 100644
--- a/base/sequenced_task_runner.cc
+++ b/base/sequenced_task_runner.cc
@@ -14,24 +14,18 @@ bool SequencedTaskRunner::PostNonNestableTask(
return PostNonNestableDelayedTask(from_here, task, base::TimeDelta());
}
-bool SequencedTaskRunner::DeleteOrReleaseSoonInternal(
+bool SequencedTaskRunner::DeleteSoonInternal(
const tracked_objects::Location& from_here,
- void (*deleter)(const void*),
+ void(*deleter)(const void*),
const void* object) {
return PostNonNestableTask(from_here, Bind(deleter, object));
}
-OnTaskRunnerDeleter::OnTaskRunnerDeleter(
- scoped_refptr<SequencedTaskRunner> task_runner)
- : task_runner_(std::move(task_runner)) {
-}
-
-OnTaskRunnerDeleter::~OnTaskRunnerDeleter() {
+bool SequencedTaskRunner::ReleaseSoonInternal(
+ const tracked_objects::Location& from_here,
+ void(*releaser)(const void*),
+ const void* object) {
+ return PostNonNestableTask(from_here, Bind(releaser, object));
}
-OnTaskRunnerDeleter::OnTaskRunnerDeleter(OnTaskRunnerDeleter&&) = default;
-
-OnTaskRunnerDeleter& OnTaskRunnerDeleter::operator=(
- OnTaskRunnerDeleter&&) = default;
-
} // namespace base
diff --git a/base/sequenced_task_runner.h b/base/sequenced_task_runner.h
index 6b2726ed4f..6bb3f2b871 100644
--- a/base/sequenced_task_runner.h
+++ b/base/sequenced_task_runner.h
@@ -122,8 +122,9 @@ class BASE_EXPORT SequencedTaskRunner : public TaskRunner {
template <class T>
bool DeleteSoon(const tracked_objects::Location& from_here,
const T* object) {
- return DeleteOrReleaseSoonInternal(from_here, &DeleteHelper<T>::DoDelete,
- object);
+ return
+ subtle::DeleteHelperInternal<T, bool>::DeleteViaSequencedTaskRunner(
+ this, from_here, object);
}
// Submits a non-nestable task to release the given object. Returns
@@ -131,34 +132,26 @@ class BASE_EXPORT SequencedTaskRunner : public TaskRunner {
// and false if the object definitely will not be released.
template <class T>
bool ReleaseSoon(const tracked_objects::Location& from_here,
- const T* object) {
- return DeleteOrReleaseSoonInternal(from_here, &ReleaseHelper<T>::DoRelease,
- object);
+ T* object) {
+ return
+ subtle::ReleaseHelperInternal<T, bool>::ReleaseViaSequencedTaskRunner(
+ this, from_here, object);
}
protected:
~SequencedTaskRunner() override {}
private:
- bool DeleteOrReleaseSoonInternal(const tracked_objects::Location& from_here,
- void (*deleter)(const void*),
- const void* object);
-};
-
-struct BASE_EXPORT OnTaskRunnerDeleter {
- explicit OnTaskRunnerDeleter(scoped_refptr<SequencedTaskRunner> task_runner);
- ~OnTaskRunnerDeleter();
+ template <class T, class R> friend class subtle::DeleteHelperInternal;
+ template <class T, class R> friend class subtle::ReleaseHelperInternal;
- OnTaskRunnerDeleter(OnTaskRunnerDeleter&&);
- OnTaskRunnerDeleter& operator=(OnTaskRunnerDeleter&&);
-
- template <typename T>
- void operator()(const T* ptr) {
- if (ptr)
- task_runner_->DeleteSoon(FROM_HERE, ptr);
- }
+ bool DeleteSoonInternal(const tracked_objects::Location& from_here,
+ void(*deleter)(const void*),
+ const void* object);
- scoped_refptr<SequencedTaskRunner> task_runner_;
+ bool ReleaseSoonInternal(const tracked_objects::Location& from_here,
+ void(*releaser)(const void*),
+ const void* object);
};
} // namespace base
diff --git a/base/sequenced_task_runner_helpers.h b/base/sequenced_task_runner_helpers.h
index 18ec0e26f5..7980b46b6c 100644
--- a/base/sequenced_task_runner_helpers.h
+++ b/base/sequenced_task_runner_helpers.h
@@ -5,9 +5,23 @@
#ifndef BASE_SEQUENCED_TASK_RUNNER_HELPERS_H_
#define BASE_SEQUENCED_TASK_RUNNER_HELPERS_H_
+#include "base/debug/alias.h"
+#include "base/macros.h"
+
+// TODO(akalin): Investigate whether it's possible to just have
+// SequencedTaskRunner use these helpers (instead of MessageLoop).
+// Then we can just move these to sequenced_task_runner.h.
+
+namespace tracked_objects {
+class Location;
+}
+
namespace base {
-class SequencedTaskRunner;
+namespace subtle {
+template <class T, class R> class DeleteHelperInternal;
+template <class T, class R> class ReleaseHelperInternal;
+}
// Template helpers which use function indirection to erase T from the
// function signature while still remembering it so we can call the
@@ -20,23 +34,80 @@ class SequencedTaskRunner;
template <class T>
class DeleteHelper {
private:
+ template <class T2, class R> friend class subtle::DeleteHelperInternal;
+
static void DoDelete(const void* object) {
- delete static_cast<const T*>(object);
+ delete reinterpret_cast<const T*>(object);
}
- friend class SequencedTaskRunner;
+ DISALLOW_COPY_AND_ASSIGN(DeleteHelper);
};
template <class T>
class ReleaseHelper {
private:
+ template <class T2, class R> friend class subtle::ReleaseHelperInternal;
+
static void DoRelease(const void* object) {
- static_cast<const T*>(object)->Release();
+ reinterpret_cast<const T*>(object)->Release();
}
- friend class SequencedTaskRunner;
+ DISALLOW_COPY_AND_ASSIGN(ReleaseHelper);
};
+namespace subtle {
+
+// An internal SequencedTaskRunner-like class helper for DeleteHelper
+// and ReleaseHelper. We don't want to expose the Do*() functions
+// directly directly since the void* argument makes it possible to
+// pass/ an object of the wrong type to delete. Instead, we force
+// callers to go through these internal helpers for type
+// safety. SequencedTaskRunner-like classes which expose DeleteSoon or
+// ReleaseSoon methods should friend the appropriate helper and
+// implement a corresponding *Internal method with the following
+// signature:
+//
+// bool(const tracked_objects::Location&,
+// void(*function)(const void*),
+// void* object)
+//
+// An implementation of this function should simply create a
+// base::Closure from (function, object) and return the result of
+// posting the task.
+template <class T, class ReturnType>
+class DeleteHelperInternal {
+ public:
+ template <class SequencedTaskRunnerType>
+ static ReturnType DeleteViaSequencedTaskRunner(
+ SequencedTaskRunnerType* sequenced_task_runner,
+ const tracked_objects::Location& from_here,
+ const T* object) {
+ return sequenced_task_runner->DeleteSoonInternal(
+ from_here, &DeleteHelper<T>::DoDelete, object);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DeleteHelperInternal);
+};
+
+template <class T, class ReturnType>
+class ReleaseHelperInternal {
+ public:
+ template <class SequencedTaskRunnerType>
+ static ReturnType ReleaseViaSequencedTaskRunner(
+ SequencedTaskRunnerType* sequenced_task_runner,
+ const tracked_objects::Location& from_here,
+ const T* object) {
+ return sequenced_task_runner->ReleaseSoonInternal(
+ from_here, &ReleaseHelper<T>::DoRelease, object);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ReleaseHelperInternal);
+};
+
+} // namespace subtle
+
} // namespace base
#endif // BASE_SEQUENCED_TASK_RUNNER_HELPERS_H_
diff --git a/base/sha1.cc b/base/sha1_portable.cc
index a710001ab7..dd2ab6fe17 100644
--- a/base/sha1.cc
+++ b/base/sha1_portable.cc
@@ -8,7 +8,6 @@
#include <stdint.h>
#include <string.h>
-#include "base/sys_byteorder.h"
namespace base {
@@ -93,6 +92,10 @@ static inline uint32_t K(uint32_t t) {
}
}
+static inline void swapends(uint32_t* t) {
+ *t = (*t >> 24) | ((*t >> 8) & 0xff00) | ((*t & 0xff00) << 8) | (*t << 24);
+}
+
const int SecureHashAlgorithm::kDigestSizeBytes = 20;
void SecureHashAlgorithm::Init() {
@@ -115,7 +118,7 @@ void SecureHashAlgorithm::Final() {
Process();
for (int t = 0; t < 5; ++t)
- H[t] = ByteSwap(H[t]);
+ swapends(&H[t]);
}
void SecureHashAlgorithm::Update(const void* data, size_t nbytes) {
@@ -162,7 +165,7 @@ void SecureHashAlgorithm::Process() {
// W and M are in a union, so no need to memcpy.
// memcpy(W, M, sizeof(M));
for (t = 0; t < 16; ++t)
- W[t] = ByteSwap(W[t]);
+ swapends(&W[t]);
// b.
for (t = 16; t < 80; ++t)
diff --git a/base/stl_util.h b/base/stl_util.h
index b0670b295e..12e226a9db 100644
--- a/base/stl_util.h
+++ b/base/stl_util.h
@@ -8,37 +8,13 @@
#define BASE_STL_UTIL_H_
#include <algorithm>
-#include <deque>
-#include <forward_list>
#include <functional>
#include <iterator>
-#include <list>
-#include <map>
-#include <set>
#include <string>
-#include <unordered_map>
-#include <unordered_set>
#include <vector>
#include "base/logging.h"
-namespace base {
-
-namespace internal {
-
-// Calls erase on iterators of matching elements.
-template <typename Container, typename Predicate>
-void IterateAndEraseIf(Container& container, Predicate pred) {
- for (auto it = container.begin(); it != container.end();) {
- if (pred(*it))
- it = container.erase(it);
- else
- ++it;
- }
-}
-
-} // namespace internal
-
// Clears internal memory of an STL object.
// STL clear()/reserve(0) does not always free internal memory allocated
// This function uses swap/destructor to ensure the internal memory is freed.
@@ -51,6 +27,69 @@ void STLClearObject(T* obj) {
obj->reserve(0);
}
+// For a range within a container of pointers, calls delete (non-array version)
+// on these pointers.
+// NOTE: for these three functions, we could just implement a DeleteObject
+// functor and then call for_each() on the range and functor, but this
+// requires us to pull in all of algorithm.h, which seems expensive.
+// For hash_[multi]set, it is important that this deletes behind the iterator
+// because the hash_set may call the hash function on the iterator when it is
+// advanced, which could result in the hash function trying to deference a
+// stale pointer.
+template <class ForwardIterator>
+void STLDeleteContainerPointers(ForwardIterator begin, ForwardIterator end) {
+ while (begin != end) {
+ ForwardIterator temp = begin;
+ ++begin;
+ delete *temp;
+ }
+}
+
+// For a range within a container of pairs, calls delete (non-array version) on
+// BOTH items in the pairs.
+// NOTE: Like STLDeleteContainerPointers, it is important that this deletes
+// behind the iterator because if both the key and value are deleted, the
+// container may call the hash function on the iterator when it is advanced,
+// which could result in the hash function trying to dereference a stale
+// pointer.
+template <class ForwardIterator>
+void STLDeleteContainerPairPointers(ForwardIterator begin,
+ ForwardIterator end) {
+ while (begin != end) {
+ ForwardIterator temp = begin;
+ ++begin;
+ delete temp->first;
+ delete temp->second;
+ }
+}
+
+// For a range within a container of pairs, calls delete (non-array version) on
+// the FIRST item in the pairs.
+// NOTE: Like STLDeleteContainerPointers, deleting behind the iterator.
+template <class ForwardIterator>
+void STLDeleteContainerPairFirstPointers(ForwardIterator begin,
+ ForwardIterator end) {
+ while (begin != end) {
+ ForwardIterator temp = begin;
+ ++begin;
+ delete temp->first;
+ }
+}
+
+// For a range within a container of pairs, calls delete.
+// NOTE: Like STLDeleteContainerPointers, deleting behind the iterator.
+// Deleting the value does not always invalidate the iterator, but it may
+// do so if the key is a pointer into the value object.
+template <class ForwardIterator>
+void STLDeleteContainerPairSecondPointers(ForwardIterator begin,
+ ForwardIterator end) {
+ while (begin != end) {
+ ForwardIterator temp = begin;
+ ++begin;
+ delete temp->second;
+ }
+}
+
// Counts the number of instances of val in a container.
template <typename Container, typename T>
typename std::iterator_traits<
@@ -76,6 +115,74 @@ inline char* string_as_array(std::string* str) {
return str->empty() ? NULL : &*str->begin();
}
+// The following functions are useful for cleaning up STL containers whose
+// elements point to allocated memory.
+
+// STLDeleteElements() deletes all the elements in an STL container and clears
+// the container. This function is suitable for use with a vector, set,
+// hash_set, or any other STL container which defines sensible begin(), end(),
+// and clear() methods.
+//
+// If container is NULL, this function is a no-op.
+//
+// As an alternative to calling STLDeleteElements() directly, consider
+// STLElementDeleter (defined below), which ensures that your container's
+// elements are deleted when the STLElementDeleter goes out of scope.
+template <class T>
+void STLDeleteElements(T* container) {
+ if (!container)
+ return;
+ STLDeleteContainerPointers(container->begin(), container->end());
+ container->clear();
+}
+
+// Given an STL container consisting of (key, value) pairs, STLDeleteValues
+// deletes all the "value" components and clears the container. Does nothing
+// in the case it's given a NULL pointer.
+template <class T>
+void STLDeleteValues(T* container) {
+ if (!container)
+ return;
+ STLDeleteContainerPairSecondPointers(container->begin(), container->end());
+ container->clear();
+}
+
+
+// The following classes provide a convenient way to delete all elements or
+// values from STL containers when they goes out of scope. This greatly
+// simplifies code that creates temporary objects and has multiple return
+// statements. Example:
+//
+// vector<MyProto *> tmp_proto;
+// STLElementDeleter<vector<MyProto *> > d(&tmp_proto);
+// if (...) return false;
+// ...
+// return success;
+
+// Given a pointer to an STL container this class will delete all the element
+// pointers when it goes out of scope.
+template<class T>
+class STLElementDeleter {
+ public:
+ STLElementDeleter<T>(T* container) : container_(container) {}
+ ~STLElementDeleter<T>() { STLDeleteElements(container_); }
+
+ private:
+ T* container_;
+};
+
+// Given a pointer to an STL container this class will delete all the value
+// pointers when it goes out of scope.
+template<class T>
+class STLValueDeleter {
+ public:
+ STLValueDeleter<T>(T* container) : container_(container) {}
+ ~STLValueDeleter<T>() { STLDeleteValues(container_); }
+
+ private:
+ T* container_;
+};
+
// Test to see if a set, map, hash_set or hash_map contains a particular key.
// Returns true if the key is in the collection.
template <typename Collection, typename Key>
@@ -91,6 +198,8 @@ bool ContainsValue(const Collection& collection, const Value& value) {
collection.end();
}
+namespace base {
+
// Returns true if the container is sorted.
template <typename Container>
bool STLIsSorted(const Container& cont) {
@@ -148,145 +257,6 @@ bool STLIncludes(const Arg1& a1, const Arg2& a2) {
a2.begin(), a2.end());
}
-// Erase/EraseIf are based on library fundamentals ts v2 erase/erase_if
-// http://en.cppreference.com/w/cpp/experimental/lib_extensions_2
-// They provide a generic way to erase elements from a container.
-// The functions here implement these for the standard containers until those
-// functions are available in the C++ standard.
-// For Chromium containers overloads should be defined in their own headers
-// (like standard containers).
-// Note: there is no std::erase for standard associative containers so we don't
-// have it either.
-
-template <typename CharT, typename Traits, typename Allocator, typename Value>
-void Erase(std::basic_string<CharT, Traits, Allocator>& container,
- const Value& value) {
- container.erase(std::remove(container.begin(), container.end(), value),
- container.end());
-}
-
-template <typename CharT, typename Traits, typename Allocator, class Predicate>
-void EraseIf(std::basic_string<CharT, Traits, Allocator>& container,
- Predicate pred) {
- container.erase(std::remove_if(container.begin(), container.end(), pred),
- container.end());
-}
-
-template <class T, class Allocator, class Value>
-void Erase(std::deque<T, Allocator>& container, const Value& value) {
- container.erase(std::remove(container.begin(), container.end(), value),
- container.end());
-}
-
-template <class T, class Allocator, class Predicate>
-void EraseIf(std::deque<T, Allocator>& container, Predicate pred) {
- container.erase(std::remove_if(container.begin(), container.end(), pred),
- container.end());
-}
-
-template <class T, class Allocator, class Value>
-void Erase(std::vector<T, Allocator>& container, const Value& value) {
- container.erase(std::remove(container.begin(), container.end(), value),
- container.end());
-}
-
-template <class T, class Allocator, class Predicate>
-void EraseIf(std::vector<T, Allocator>& container, Predicate pred) {
- container.erase(std::remove_if(container.begin(), container.end(), pred),
- container.end());
-}
-
-template <class T, class Allocator, class Value>
-void Erase(std::forward_list<T, Allocator>& container, const Value& value) {
- // Unlike std::forward_list::remove, this function template accepts
- // heterogeneous types and does not force a conversion to the container's
- // value type before invoking the == operator.
- container.remove_if([&](const T& cur) { return cur == value; });
-}
-
-template <class T, class Allocator, class Predicate>
-void EraseIf(std::forward_list<T, Allocator>& container, Predicate pred) {
- container.remove_if(pred);
-}
-
-template <class T, class Allocator, class Value>
-void Erase(std::list<T, Allocator>& container, const Value& value) {
- // Unlike std::list::remove, this function template accepts heterogeneous
- // types and does not force a conversion to the container's value type before
- // invoking the == operator.
- container.remove_if([&](const T& cur) { return cur == value; });
-}
-
-template <class T, class Allocator, class Predicate>
-void EraseIf(std::list<T, Allocator>& container, Predicate pred) {
- container.remove_if(pred);
-}
-
-template <class Key, class T, class Compare, class Allocator, class Predicate>
-void EraseIf(std::map<Key, T, Compare, Allocator>& container, Predicate pred) {
- internal::IterateAndEraseIf(container, pred);
-}
-
-template <class Key, class T, class Compare, class Allocator, class Predicate>
-void EraseIf(std::multimap<Key, T, Compare, Allocator>& container,
- Predicate pred) {
- internal::IterateAndEraseIf(container, pred);
-}
-
-template <class Key, class Compare, class Allocator, class Predicate>
-void EraseIf(std::set<Key, Compare, Allocator>& container, Predicate pred) {
- internal::IterateAndEraseIf(container, pred);
-}
-
-template <class Key, class Compare, class Allocator, class Predicate>
-void EraseIf(std::multiset<Key, Compare, Allocator>& container,
- Predicate pred) {
- internal::IterateAndEraseIf(container, pred);
-}
-
-template <class Key,
- class T,
- class Hash,
- class KeyEqual,
- class Allocator,
- class Predicate>
-void EraseIf(std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& container,
- Predicate pred) {
- internal::IterateAndEraseIf(container, pred);
-}
-
-template <class Key,
- class T,
- class Hash,
- class KeyEqual,
- class Allocator,
- class Predicate>
-void EraseIf(
- std::unordered_multimap<Key, T, Hash, KeyEqual, Allocator>& container,
- Predicate pred) {
- internal::IterateAndEraseIf(container, pred);
-}
-
-template <class Key,
- class Hash,
- class KeyEqual,
- class Allocator,
- class Predicate>
-void EraseIf(std::unordered_set<Key, Hash, KeyEqual, Allocator>& container,
- Predicate pred) {
- internal::IterateAndEraseIf(container, pred);
-}
-
-template <class Key,
- class Hash,
- class KeyEqual,
- class Allocator,
- class Predicate>
-void EraseIf(std::unordered_multiset<Key, Hash, KeyEqual, Allocator>& container,
- Predicate pred) {
- internal::IterateAndEraseIf(container, pred);
-}
-
} // namespace base
#endif // BASE_STL_UTIL_H_
diff --git a/base/stl_util_unittest.cc b/base/stl_util_unittest.cc
index 48d0f660b5..42004eb869 100644
--- a/base/stl_util_unittest.cc
+++ b/base/stl_util_unittest.cc
@@ -4,20 +4,8 @@
#include "base/stl_util.h"
-#include <deque>
-#include <forward_list>
-#include <functional>
-#include <iterator>
-#include <list>
-#include <map>
#include <set>
-#include <string>
-#include <unordered_map>
-#include <unordered_set>
-#include <vector>
-#include "base/strings/string16.h"
-#include "base/strings/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
@@ -40,55 +28,6 @@ class ComparableValue {
int value_;
};
-template <typename Container>
-void RunEraseTest() {
- const std::pair<Container, Container> test_data[] = {
- {Container(), Container()}, {{1, 2, 3}, {1, 3}}, {{1, 2, 3, 2}, {1, 3}}};
-
- for (auto test_case : test_data) {
- base::Erase(test_case.first, 2);
- EXPECT_EQ(test_case.second, test_case.first);
- }
-}
-
-// This test is written for containers of std::pair<int, int> to support maps.
-template <typename Container>
-void RunEraseIfTest() {
- struct {
- Container input;
- Container erase_even;
- Container erase_odd;
- } test_data[] = {
- {Container(), Container(), Container()},
- {{{1, 1}, {2, 2}, {3, 3}}, {{1, 1}, {3, 3}}, {{2, 2}}},
- {{{1, 1}, {2, 2}, {3, 3}, {4, 4}}, {{1, 1}, {3, 3}}, {{2, 2}, {4, 4}}},
- };
-
- for (auto test_case : test_data) {
- base::EraseIf(test_case.input, [](const std::pair<int, int>& elem) {
- return !(elem.first & 1);
- });
- EXPECT_EQ(test_case.erase_even, test_case.input);
- }
-
- for (auto test_case : test_data) {
- base::EraseIf(test_case.input, [](const std::pair<int, int>& elem) {
- return elem.first & 1;
- });
- EXPECT_EQ(test_case.erase_odd, test_case.input);
- }
-}
-
-struct CustomIntHash {
- size_t operator()(int elem) const { return std::hash<int>()(elem) + 1; }
-};
-
-struct HashByFirst {
- size_t operator()(const std::pair<int, int>& elem) const {
- return std::hash<int>()(elem.first);
- }
-};
-
} // namespace
namespace base {
@@ -324,100 +263,5 @@ TEST(StringAsArrayTest, WriteCopy) {
EXPECT_EQ("abc", s2);
}
-TEST(Erase, String) {
- const std::pair<std::string, std::string> test_data[] = {
- {"", ""}, {"abc", "bc"}, {"abca", "bc"},
- };
-
- for (auto test_case : test_data) {
- Erase(test_case.first, 'a');
- EXPECT_EQ(test_case.second, test_case.first);
- }
-
- for (auto test_case : test_data) {
- EraseIf(test_case.first, [](char elem) { return elem < 'b'; });
- EXPECT_EQ(test_case.second, test_case.first);
- }
-}
-
-TEST(Erase, String16) {
- std::pair<base::string16, base::string16> test_data[] = {
- {base::string16(), base::string16()},
- {UTF8ToUTF16("abc"), UTF8ToUTF16("bc")},
- {UTF8ToUTF16("abca"), UTF8ToUTF16("bc")},
- };
-
- const base::string16 letters = UTF8ToUTF16("ab");
- for (auto test_case : test_data) {
- Erase(test_case.first, letters[0]);
- EXPECT_EQ(test_case.second, test_case.first);
- }
-
- for (auto test_case : test_data) {
- EraseIf(test_case.first, [&](short elem) { return elem < letters[1]; });
- EXPECT_EQ(test_case.second, test_case.first);
- }
-}
-
-TEST(Erase, Deque) {
- RunEraseTest<std::deque<int>>();
- RunEraseIfTest<std::deque<std::pair<int, int>>>();
-}
-
-TEST(Erase, Vector) {
- RunEraseTest<std::vector<int>>();
- RunEraseIfTest<std::vector<std::pair<int, int>>>();
-}
-
-TEST(Erase, ForwardList) {
- RunEraseTest<std::forward_list<int>>();
- RunEraseIfTest<std::forward_list<std::pair<int, int>>>();
-}
-
-TEST(Erase, List) {
- RunEraseTest<std::list<int>>();
- RunEraseIfTest<std::list<std::pair<int, int>>>();
-}
-
-TEST(Erase, Map) {
- RunEraseIfTest<std::map<int, int>>();
- RunEraseIfTest<std::map<int, int, std::greater<int>>>();
-}
-
-TEST(Erase, Multimap) {
- RunEraseIfTest<std::multimap<int, int>>();
- RunEraseIfTest<std::multimap<int, int, std::greater<int>>>();
-}
-
-TEST(Erase, Set) {
- RunEraseIfTest<std::set<std::pair<int, int>>>();
- RunEraseIfTest<
- std::set<std::pair<int, int>, std::greater<std::pair<int, int>>>>();
-}
-
-TEST(Erase, Multiset) {
- RunEraseIfTest<std::multiset<std::pair<int, int>>>();
- RunEraseIfTest<
- std::multiset<std::pair<int, int>, std::greater<std::pair<int, int>>>>();
-}
-
-TEST(Erase, UnorderedMap) {
- RunEraseIfTest<std::unordered_map<int, int>>();
- RunEraseIfTest<std::unordered_map<int, int, CustomIntHash>>();
-}
-
-TEST(Erase, UnorderedMultimap) {
- RunEraseIfTest<std::unordered_multimap<int, int>>();
- RunEraseIfTest<std::unordered_multimap<int, int, CustomIntHash>>();
-}
-
-TEST(Erase, UnorderedSet) {
- RunEraseIfTest<std::unordered_set<std::pair<int, int>, HashByFirst>>();
-}
-
-TEST(Erase, UnorderedMultiset) {
- RunEraseIfTest<std::unordered_multiset<std::pair<int, int>, HashByFirst>>();
-}
-
} // namespace
} // namespace base
diff --git a/base/strings/string_number_conversions.cc b/base/strings/string_number_conversions.cc
index adb4bdb8d2..09aeb444d6 100644
--- a/base/strings/string_number_conversions.cc
+++ b/base/strings/string_number_conversions.cc
@@ -10,11 +10,11 @@
#include <wctype.h>
#include <limits>
-#include <type_traits>
#include "base/logging.h"
#include "base/numerics/safe_math.h"
#include "base/scoped_clear_errno.h"
+#include "base/scoped_clear_errno.h"
namespace base {
@@ -35,8 +35,7 @@ struct IntToStringT {
// The ValueOrDie call below can never fail, because UnsignedAbs is valid
// for all valid inputs.
- typename std::make_unsigned<INT>::type res =
- CheckedNumeric<INT>(value).UnsignedAbs().ValueOrDie();
+ auto res = CheckedNumeric<INT>(value).UnsignedAbs().ValueOrDie();
CHR* end = outbuf + kOutputBufSize;
CHR* i = end;
diff --git a/base/strings/string_number_conversions_unittest.cc b/base/strings/string_number_conversions_unittest.cc
index b4c3068f36..91191e07e1 100644
--- a/base/strings/string_number_conversions_unittest.cc
+++ b/base/strings/string_number_conversions_unittest.cc
@@ -719,41 +719,19 @@ TEST(StringNumberConversionsTest, StringToDouble) {
double output;
bool success;
} cases[] = {
- // Test different forms of zero.
{"0", 0.0, true},
- {"+0", 0.0, true},
- {"-0", 0.0, true},
- {"0.0", 0.0, true},
- {"000000000000000000000000000000.0", 0.0, true},
- {"0.000000000000000000000000000", 0.0, true},
-
- // Test the answer.
{"42", 42.0, true},
{"-42", -42.0, true},
-
- // Test variances of an ordinary number.
{"123.45", 123.45, true},
{"-123.45", -123.45, true},
{"+123.45", 123.45, true},
-
- // Test different forms of representation.
{"2.99792458e8", 299792458.0, true},
{"149597870.691E+3", 149597870691.0, true},
{"6.", 6.0, true},
-
- // Test around the largest/smallest value that a double can represent.
- {"9e307", 9e307, true},
- {"1.7976e308", 1.7976e308, true},
- {"1.7977e308", HUGE_VAL, false},
- {"1.797693134862315807e+308", HUGE_VAL, true},
- {"1.797693134862315808e+308", HUGE_VAL, false},
- {"9e308", HUGE_VAL, false},
- {"9e309", HUGE_VAL, false},
- {"9e999", HUGE_VAL, false},
- {"9e1999", HUGE_VAL, false},
- {"9e19999", HUGE_VAL, false},
- {"9e99999999999999999999", std::numeric_limits<double>::infinity(), false},
- {"-9e99999999999999999999", -std::numeric_limits<double>::infinity(), false},
+ {"9e99999999999999999999", std::numeric_limits<double>::infinity(),
+ false},
+ {"-9e99999999999999999999", -std::numeric_limits<double>::infinity(),
+ false},
{"1e-2", 0.01, true},
{"42 ", 42.0, false},
{" 1e-2", 0.01, false},
@@ -761,9 +739,6 @@ TEST(StringNumberConversionsTest, StringToDouble) {
{"-1E-7", -0.0000001, true},
{"01e02", 100, true},
{"2.3e15", 2.3e15, true},
- {"100e-309", 100e-309, true},
-
- // Test some invalid cases.
{"\t\n\v\f\r -123.45e2", -12345.0, false},
{"+123 e4", 123.0, false},
{"123e ", 123.0, false},
@@ -774,10 +749,6 @@ TEST(StringNumberConversionsTest, StringToDouble) {
{"-", 0.0, false},
{"+", 0.0, false},
{"", 0.0, false},
-
- // crbug.org/588726
- {"-0.0010000000000000000000000000000000000000001e-256",
- -1.0000000000000001e-259, true},
};
for (size_t i = 0; i < arraysize(cases); ++i) {
@@ -838,54 +809,4 @@ TEST(StringNumberConversionsTest, HexEncode) {
EXPECT_EQ(hex.compare("01FF02FE038081"), 0);
}
-// Test cases of known-bad strtod conversions that motivated the use of dmg_fp.
-// See https://bugs.chromium.org/p/chromium/issues/detail?id=593512.
-TEST(StringNumberConversionsTest, StrtodFailures) {
- static const struct {
- const char* input;
- uint64_t expected;
- } cases[] = {
- // http://www.exploringbinary.com/incorrectly-rounded-conversions-in-visual-c-plus-plus/
- {"9214843084008499", 0x43405e6cec57761aULL},
- {"0.500000000000000166533453693773481063544750213623046875",
- 0x3fe0000000000002ULL},
- {"30078505129381147446200", 0x44997a3c7271b021ULL},
- {"1777820000000000000001", 0x4458180d5bad2e3eULL},
- {"0.500000000000000166547006220929549868969843373633921146392822265625",
- 0x3fe0000000000002ULL},
- {"0.50000000000000016656055874808561867439493653364479541778564453125",
- 0x3fe0000000000002ULL},
- {"0.3932922657273", 0x3fd92bb352c4623aULL},
-
- // http://www.exploringbinary.com/incorrectly-rounded-conversions-in-gcc-and-glibc/
- {"0.500000000000000166533453693773481063544750213623046875",
- 0x3fe0000000000002ULL},
- {"3.518437208883201171875e13", 0x42c0000000000002ULL},
- {"62.5364939768271845828", 0x404f44abd5aa7ca4ULL},
- {"8.10109172351e-10", 0x3e0bd5cbaef0fd0cULL},
- {"1.50000000000000011102230246251565404236316680908203125",
- 0x3ff8000000000000ULL},
- {"9007199254740991.4999999999999999999999999999999995",
- 0x433fffffffffffffULL},
-
- // http://www.exploringbinary.com/incorrect-decimal-to-floating-point-conversion-in-sqlite/
- {"1e-23", 0x3b282db34012b251ULL},
- {"8.533e+68", 0x4e3fa69165a8eea2ULL},
- {"4.1006e-184", 0x19dbe0d1c7ea60c9ULL},
- {"9.998e+307", 0x7fe1cc0a350ca87bULL},
- {"9.9538452227e-280", 0x0602117ae45cde43ULL},
- {"6.47660115e-260", 0x0a1fdd9e333badadULL},
- {"7.4e+47", 0x49e033d7eca0adefULL},
- {"5.92e+48", 0x4a1033d7eca0adefULL},
- {"7.35e+66", 0x4dd172b70eababa9ULL},
- {"8.32116e+55", 0x4b8b2628393e02cdULL},
- };
-
- for (const auto& test : cases) {
- double output;
- EXPECT_TRUE(StringToDouble(test.input, &output));
- EXPECT_EQ(bit_cast<uint64_t>(output), test.expected);
- }
-}
-
} // namespace base
diff --git a/base/strings/string_split.cc b/base/strings/string_split.cc
index a8180b24d3..6c949b989a 100644
--- a/base/strings/string_split.cc
+++ b/base/strings/string_split.cc
@@ -227,22 +227,18 @@ bool SplitStringIntoKeyValuePairs(StringPiece input,
return success;
}
-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;
+void SplitStringUsingSubstr(StringPiece16 input,
+ StringPiece16 delimiter,
+ std::vector<string16>* result) {
+ SplitStringUsingSubstrT(input, delimiter, TRIM_WHITESPACE, SPLIT_WANT_ALL,
+ result);
}
-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;
+void SplitStringUsingSubstr(StringPiece input,
+ StringPiece delimiter,
+ std::vector<std::string>* result) {
+ SplitStringUsingSubstrT(input, delimiter, TRIM_WHITESPACE, SPLIT_WANT_ALL,
+ result);
}
std::vector<StringPiece16> SplitStringPieceUsingSubstr(
diff --git a/base/strings/string_split.h b/base/strings/string_split.h
index 24b9dfa1e9..ec9f24604a 100644
--- a/base/strings/string_split.h
+++ b/base/strings/string_split.h
@@ -90,16 +90,16 @@ BASE_EXPORT bool SplitStringIntoKeyValuePairs(StringPiece input,
// Similar to SplitString, but use a substring delimiter instead of a list of
// characters that are all possible delimiters.
-BASE_EXPORT std::vector<string16> SplitStringUsingSubstr(
- StringPiece16 input,
- StringPiece16 delimiter,
- WhitespaceHandling whitespace,
- SplitResult result_type);
-BASE_EXPORT std::vector<std::string> SplitStringUsingSubstr(
- StringPiece input,
- StringPiece delimiter,
- WhitespaceHandling whitespace,
- SplitResult result_type);
+//
+// TODO(brettw) this should probably be changed and expanded to provide a
+// mirror of the SplitString[Piece] API above, just with the different
+// delimiter handling.
+BASE_EXPORT void SplitStringUsingSubstr(StringPiece16 input,
+ StringPiece16 delimiter,
+ std::vector<string16>* result);
+BASE_EXPORT void SplitStringUsingSubstr(StringPiece input,
+ StringPiece delimiter,
+ std::vector<std::string>* result);
// Like SplitStringUsingSubstr above except it returns a vector of StringPieces
// which reference the original buffer without copying. Although you have to be
diff --git a/base/strings/string_split_unittest.cc b/base/strings/string_split_unittest.cc
index bf09aa5497..657a2db7b5 100644
--- a/base/strings/string_split_unittest.cc
+++ b/base/strings/string_split_unittest.cc
@@ -150,8 +150,8 @@ TEST_F(SplitStringIntoKeyValuePairsTest, DelimiterInValue) {
}
TEST(SplitStringUsingSubstrTest, EmptyString) {
- std::vector<std::string> results = SplitStringUsingSubstr(
- std::string(), "DELIMITER", TRIM_WHITESPACE, SPLIT_WANT_ALL);
+ std::vector<std::string> results;
+ SplitStringUsingSubstr(std::string(), "DELIMITER", &results);
ASSERT_EQ(1u, results.size());
EXPECT_THAT(results, ElementsAre(""));
}
@@ -231,33 +231,38 @@ TEST(StringUtilTest, SplitString_WhitespaceAndResultType) {
}
TEST(SplitStringUsingSubstrTest, StringWithNoDelimiter) {
- std::vector<std::string> results = SplitStringUsingSubstr(
- "alongwordwithnodelimiter", "DELIMITER", TRIM_WHITESPACE,
- SPLIT_WANT_ALL);
+ std::vector<std::string> results;
+ SplitStringUsingSubstr("alongwordwithnodelimiter", "DELIMITER", &results);
ASSERT_EQ(1u, results.size());
EXPECT_THAT(results, ElementsAre("alongwordwithnodelimiter"));
}
TEST(SplitStringUsingSubstrTest, LeadingDelimitersSkipped) {
- std::vector<std::string> results = SplitStringUsingSubstr(
+ std::vector<std::string> results;
+ SplitStringUsingSubstr(
"DELIMITERDELIMITERDELIMITERoneDELIMITERtwoDELIMITERthree",
- "DELIMITER", TRIM_WHITESPACE, SPLIT_WANT_ALL);
+ "DELIMITER",
+ &results);
ASSERT_EQ(6u, results.size());
EXPECT_THAT(results, ElementsAre("", "", "", "one", "two", "three"));
}
TEST(SplitStringUsingSubstrTest, ConsecutiveDelimitersSkipped) {
- std::vector<std::string> results = SplitStringUsingSubstr(
+ std::vector<std::string> results;
+ SplitStringUsingSubstr(
"unoDELIMITERDELIMITERDELIMITERdosDELIMITERtresDELIMITERDELIMITERcuatro",
- "DELIMITER", TRIM_WHITESPACE, SPLIT_WANT_ALL);
+ "DELIMITER",
+ &results);
ASSERT_EQ(7u, results.size());
EXPECT_THAT(results, ElementsAre("uno", "", "", "dos", "tres", "", "cuatro"));
}
TEST(SplitStringUsingSubstrTest, TrailingDelimitersSkipped) {
- std::vector<std::string> results = SplitStringUsingSubstr(
+ std::vector<std::string> results;
+ SplitStringUsingSubstr(
"unDELIMITERdeuxDELIMITERtroisDELIMITERquatreDELIMITERDELIMITERDELIMITER",
- "DELIMITER", TRIM_WHITESPACE, SPLIT_WANT_ALL);
+ "DELIMITER",
+ &results);
ASSERT_EQ(7u, results.size());
EXPECT_THAT(
results, ElementsAre("un", "deux", "trois", "quatre", "", "", ""));
diff --git a/base/strings/string_util.cc b/base/strings/string_util.cc
index 71ae894dd6..cb668ed7ff 100644
--- a/base/strings/string_util.cc
+++ b/base/strings/string_util.cc
@@ -64,21 +64,6 @@ static bool CompareParameter(const ReplacementOffset& elem1,
return elem1.parameter < elem2.parameter;
}
-// Overloaded function to append one string onto the end of another. Having a
-// separate overload for |source| as both string and StringPiece allows for more
-// efficient usage from functions templated to work with either type (avoiding a
-// redundant call to the BasicStringPiece constructor in both cases).
-template <typename string_type>
-inline void AppendToString(string_type* target, const string_type& source) {
- target->append(source);
-}
-
-template <typename string_type>
-inline void AppendToString(string_type* target,
- const BasicStringPiece<string_type>& source) {
- source.AppendToString(target);
-}
-
// Assuming that a pointer is the size of a "machine word", then
// uintptr_t is an integer type that is also a machine word.
typedef uintptr_t MachineWord;
@@ -868,40 +853,21 @@ char16* WriteInto(string16* str, size_t length_with_null) {
return WriteIntoT(str, length_with_null);
}
-// Generic version for all JoinString overloads. |list_type| must be a sequence
-// (std::vector or std::initializer_list) of strings/StringPieces (std::string,
-// string16, StringPiece or StringPiece16). |string_type| is either std::string
-// or string16.
-template <typename list_type, typename string_type>
-static string_type JoinStringT(const list_type& parts,
- BasicStringPiece<string_type> sep) {
- if (parts.size() == 0)
- return string_type();
-
- // Pre-allocate the eventual size of the string. Start with the size of all of
- // the separators (note that this *assumes* parts.size() > 0).
- size_t total_size = (parts.size() - 1) * sep.size();
- for (const auto& part : parts)
- total_size += part.size();
- string_type result;
- result.reserve(total_size);
+template<typename STR>
+static STR JoinStringT(const std::vector<STR>& parts,
+ BasicStringPiece<STR> sep) {
+ if (parts.empty())
+ return STR();
+ STR result(parts[0]);
auto iter = parts.begin();
- DCHECK(iter != parts.end());
- AppendToString(&result, *iter);
++iter;
for (; iter != parts.end(); ++iter) {
sep.AppendToString(&result);
- // Using the overloaded AppendToString allows this template function to work
- // on both strings and StringPieces without creating an intermediate
- // StringPiece object.
- AppendToString(&result, *iter);
+ result += *iter;
}
- // Sanity-check that we pre-allocated correctly.
- DCHECK_EQ(total_size, result.size());
-
return result;
}
@@ -915,26 +881,6 @@ string16 JoinString(const std::vector<string16>& parts,
return JoinStringT(parts, separator);
}
-std::string JoinString(const std::vector<StringPiece>& parts,
- StringPiece separator) {
- return JoinStringT(parts, separator);
-}
-
-string16 JoinString(const std::vector<StringPiece16>& parts,
- StringPiece16 separator) {
- return JoinStringT(parts, separator);
-}
-
-std::string JoinString(std::initializer_list<StringPiece> parts,
- StringPiece separator) {
- return JoinStringT(parts, separator);
-}
-
-string16 JoinString(std::initializer_list<StringPiece16> parts,
- StringPiece16 separator) {
- return JoinStringT(parts, separator);
-}
-
template<class FormatStringType, class OutStringType>
OutStringType DoReplaceStringPlaceholders(
const FormatStringType& format_string,
diff --git a/base/strings/string_util.h b/base/strings/string_util.h
index 29076ed913..0ee077c62b 100644
--- a/base/strings/string_util.h
+++ b/base/strings/string_util.h
@@ -12,7 +12,6 @@
#include <stddef.h>
#include <stdint.h>
-#include <initializer_list>
#include <string>
#include <vector>
@@ -291,6 +290,8 @@ BASE_EXPORT bool ContainsOnlyChars(const StringPiece16& input,
BASE_EXPORT bool IsStringUTF8(const StringPiece& str);
BASE_EXPORT bool IsStringASCII(const StringPiece& str);
BASE_EXPORT bool IsStringASCII(const StringPiece16& str);
+// A convenience adaptor for WebStrings, as they don't convert into
+// StringPieces directly.
BASE_EXPORT bool IsStringASCII(const string16& str);
#if defined(WCHAR_T_IS_UTF32)
BASE_EXPORT bool IsStringASCII(const std::wstring& str);
@@ -436,30 +437,11 @@ BASE_EXPORT char16* WriteInto(string16* str, size_t length_with_null);
BASE_EXPORT wchar_t* WriteInto(std::wstring* str, size_t length_with_null);
#endif
-// Does the opposite of SplitString()/SplitStringPiece(). Joins a vector or list
-// of strings into a single string, inserting |separator| (which may be empty)
-// in between all elements.
-//
-// If possible, callers should build a vector of StringPieces and use the
-// StringPiece variant, so that they do not create unnecessary copies of
-// strings. For example, instead of using SplitString, modifying the vector,
-// then using JoinString, use SplitStringPiece followed by JoinString so that no
-// copies of those strings are created until the final join operation.
+// Does the opposite of SplitString().
BASE_EXPORT std::string JoinString(const std::vector<std::string>& parts,
StringPiece separator);
BASE_EXPORT string16 JoinString(const std::vector<string16>& parts,
StringPiece16 separator);
-BASE_EXPORT std::string JoinString(const std::vector<StringPiece>& parts,
- StringPiece separator);
-BASE_EXPORT string16 JoinString(const std::vector<StringPiece16>& parts,
- StringPiece16 separator);
-// Explicit initializer_list overloads are required to break ambiguity when used
-// with a literal initializer list (otherwise the compiler would not be able to
-// decide between the string and StringPiece overloads).
-BASE_EXPORT std::string JoinString(std::initializer_list<StringPiece> parts,
- StringPiece separator);
-BASE_EXPORT string16 JoinString(std::initializer_list<StringPiece16> parts,
- StringPiece16 separator);
// Replace $1-$2-$3..$9 in the format string with values from |subst|.
// Additionally, any number of consecutive '$' characters is replaced by that
diff --git a/base/strings/string_util_unittest.cc b/base/strings/string_util_unittest.cc
index 6ac307ec2b..df2226e48b 100644
--- a/base/strings/string_util_unittest.cc
+++ b/base/strings/string_util_unittest.cc
@@ -676,10 +676,6 @@ TEST(StringUtilTest, JoinString) {
std::vector<std::string> parts;
EXPECT_EQ(std::string(), JoinString(parts, separator));
- parts.push_back(std::string());
- EXPECT_EQ(std::string(), JoinString(parts, separator));
- parts.clear();
-
parts.push_back("a");
EXPECT_EQ("a", JoinString(parts, separator));
@@ -698,10 +694,6 @@ TEST(StringUtilTest, JoinString16) {
std::vector<string16> parts;
EXPECT_EQ(string16(), JoinString(parts, separator));
- parts.push_back(string16());
- EXPECT_EQ(string16(), JoinString(parts, separator));
- parts.clear();
-
parts.push_back(ASCIIToUTF16("a"));
EXPECT_EQ(ASCIIToUTF16("a"), JoinString(parts, separator));
@@ -715,108 +707,6 @@ TEST(StringUtilTest, JoinString16) {
EXPECT_EQ(ASCIIToUTF16("a|b|c|| "), JoinString(parts, ASCIIToUTF16("|")));
}
-TEST(StringUtilTest, JoinStringPiece) {
- std::string separator(", ");
- std::vector<StringPiece> parts;
- EXPECT_EQ(std::string(), JoinString(parts, separator));
-
- // Test empty first part (https://crbug.com/698073).
- parts.push_back(StringPiece());
- EXPECT_EQ(std::string(), JoinString(parts, separator));
- parts.clear();
-
- parts.push_back("a");
- EXPECT_EQ("a", JoinString(parts, separator));
-
- parts.push_back("b");
- parts.push_back("c");
- EXPECT_EQ("a, b, c", JoinString(parts, separator));
-
- parts.push_back(StringPiece());
- EXPECT_EQ("a, b, c, ", JoinString(parts, separator));
- parts.push_back(" ");
- EXPECT_EQ("a|b|c|| ", JoinString(parts, "|"));
-}
-
-TEST(StringUtilTest, JoinStringPiece16) {
- string16 separator = ASCIIToUTF16(", ");
- std::vector<StringPiece16> parts;
- EXPECT_EQ(string16(), JoinString(parts, separator));
-
- // Test empty first part (https://crbug.com/698073).
- parts.push_back(StringPiece16());
- EXPECT_EQ(string16(), JoinString(parts, separator));
- parts.clear();
-
- const string16 kA = ASCIIToUTF16("a");
- parts.push_back(kA);
- EXPECT_EQ(ASCIIToUTF16("a"), JoinString(parts, separator));
-
- const string16 kB = ASCIIToUTF16("b");
- parts.push_back(kB);
- const string16 kC = ASCIIToUTF16("c");
- parts.push_back(kC);
- EXPECT_EQ(ASCIIToUTF16("a, b, c"), JoinString(parts, separator));
-
- parts.push_back(StringPiece16());
- EXPECT_EQ(ASCIIToUTF16("a, b, c, "), JoinString(parts, separator));
- const string16 kSpace = ASCIIToUTF16(" ");
- parts.push_back(kSpace);
- EXPECT_EQ(ASCIIToUTF16("a|b|c|| "), JoinString(parts, ASCIIToUTF16("|")));
-}
-
-TEST(StringUtilTest, JoinStringInitializerList) {
- std::string separator(", ");
- EXPECT_EQ(std::string(), JoinString({}, separator));
-
- // Test empty first part (https://crbug.com/698073).
- EXPECT_EQ(std::string(), JoinString({StringPiece()}, separator));
-
- // With const char*s.
- EXPECT_EQ("a", JoinString({"a"}, separator));
- EXPECT_EQ("a, b, c", JoinString({"a", "b", "c"}, separator));
- EXPECT_EQ("a, b, c, ", JoinString({"a", "b", "c", StringPiece()}, separator));
- EXPECT_EQ("a|b|c|| ", JoinString({"a", "b", "c", StringPiece(), " "}, "|"));
-
- // With std::strings.
- const std::string kA = "a";
- const std::string kB = "b";
- EXPECT_EQ("a, b", JoinString({kA, kB}, separator));
-
- // With StringPieces.
- const StringPiece kPieceA = kA;
- const StringPiece kPieceB = kB;
- EXPECT_EQ("a, b", JoinString({kPieceA, kPieceB}, separator));
-}
-
-TEST(StringUtilTest, JoinStringInitializerList16) {
- string16 separator = ASCIIToUTF16(", ");
- EXPECT_EQ(string16(), JoinString({}, separator));
-
- // Test empty first part (https://crbug.com/698073).
- EXPECT_EQ(string16(), JoinString({StringPiece16()}, separator));
-
- // With string16s.
- const string16 kA = ASCIIToUTF16("a");
- EXPECT_EQ(ASCIIToUTF16("a"), JoinString({kA}, separator));
-
- const string16 kB = ASCIIToUTF16("b");
- const string16 kC = ASCIIToUTF16("c");
- EXPECT_EQ(ASCIIToUTF16("a, b, c"), JoinString({kA, kB, kC}, separator));
-
- EXPECT_EQ(ASCIIToUTF16("a, b, c, "),
- JoinString({kA, kB, kC, StringPiece16()}, separator));
- const string16 kSpace = ASCIIToUTF16(" ");
- EXPECT_EQ(
- ASCIIToUTF16("a|b|c|| "),
- JoinString({kA, kB, kC, StringPiece16(), kSpace}, ASCIIToUTF16("|")));
-
- // With StringPiece16s.
- const StringPiece16 kPieceA = kA;
- const StringPiece16 kPieceB = kB;
- EXPECT_EQ(ASCIIToUTF16("a, b"), JoinString({kPieceA, kPieceB}, separator));
-}
-
TEST(StringUtilTest, StartsWith) {
EXPECT_TRUE(StartsWith("javascript:url", "javascript",
base::CompareCase::SENSITIVE));
diff --git a/base/strings/utf_string_conversion_utils.h b/base/strings/utf_string_conversion_utils.h
index 2d95870c58..c716404539 100644
--- a/base/strings/utf_string_conversion_utils.h
+++ b/base/strings/utf_string_conversion_utils.h
@@ -5,8 +5,7 @@
#ifndef BASE_STRINGS_UTF_STRING_CONVERSION_UTILS_H_
#define BASE_STRINGS_UTF_STRING_CONVERSION_UTILS_H_
-// Low-level UTF handling functions. Most code will want to use the functions
-// in utf_string_conversions.h
+// This should only be used by the various UTF string conversion files.
#include <stddef.h>
#include <stdint.h>
diff --git a/base/strings/utf_string_conversions.cc b/base/strings/utf_string_conversions.cc
index 85450c6566..6b17eacd6c 100644
--- a/base/strings/utf_string_conversions.cc
+++ b/base/strings/utf_string_conversions.cc
@@ -180,6 +180,10 @@ bool UTF16ToUTF8(const char16* src, size_t src_len, std::string* output) {
}
std::string UTF16ToUTF8(StringPiece16 utf16) {
+ if (IsStringASCII(utf16)) {
+ return std::string(utf16.begin(), utf16.end());
+ }
+
std::string ret;
// Ignore the success flag of this call, it will do the best it can for
// invalid input, which is what we want here.
diff --git a/base/synchronization/atomic_flag.cc b/base/synchronization/atomic_flag.cc
deleted file mode 100644
index 8c2018d369..0000000000
--- a/base/synchronization/atomic_flag.cc
+++ /dev/null
@@ -1,32 +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/synchronization/atomic_flag.h"
-
-#include "base/logging.h"
-
-namespace base {
-
-AtomicFlag::AtomicFlag() {
- // It doesn't matter where the AtomicFlag is built so long as it's always
- // Set() from the same sequence after. Note: the sequencing requirements are
- // necessary for IsSet()'s callers to know which sequence's memory operations
- // they are synchronized with.
- set_sequence_checker_.DetachFromSequence();
-}
-
-void AtomicFlag::Set() {
- DCHECK(set_sequence_checker_.CalledOnValidSequence());
- base::subtle::Release_Store(&flag_, 1);
-}
-
-bool AtomicFlag::IsSet() const {
- return base::subtle::Acquire_Load(&flag_) != 0;
-}
-
-void AtomicFlag::UnsafeResetForTesting() {
- base::subtle::Release_Store(&flag_, 0);
-}
-
-} // namespace base
diff --git a/base/synchronization/atomic_flag.h b/base/synchronization/atomic_flag.h
deleted file mode 100644
index ff175e190c..0000000000
--- a/base/synchronization/atomic_flag.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_SYNCHRONIZATION_ATOMIC_FLAG_H_
-#define BASE_SYNCHRONIZATION_ATOMIC_FLAG_H_
-
-#include "base/atomicops.h"
-#include "base/base_export.h"
-#include "base/macros.h"
-#include "base/sequence_checker.h"
-
-namespace base {
-
-// A flag that can safely be set from one thread and read from other threads.
-//
-// This class IS NOT intended for synchronization between threads.
-class BASE_EXPORT AtomicFlag {
- public:
- AtomicFlag();
- ~AtomicFlag() = default;
-
- // Set the flag. Must always be called from the same sequence.
- void Set();
-
- // Returns true iff the flag was set. If this returns true, the current thread
- // is guaranteed to be synchronized with all memory operations on the sequence
- // which invoked Set() up until at least the first call to Set() on it.
- bool IsSet() const;
-
- // Resets the flag. Be careful when using this: callers might not expect
- // IsSet() to return false after returning true once.
- void UnsafeResetForTesting();
-
- private:
- base::subtle::Atomic32 flag_ = 0;
- SequenceChecker set_sequence_checker_;
-
- DISALLOW_COPY_AND_ASSIGN(AtomicFlag);
-};
-
-} // namespace base
-
-#endif // BASE_SYNCHRONIZATION_ATOMIC_FLAG_H_
diff --git a/base/synchronization/atomic_flag_unittest.cc b/base/synchronization/atomic_flag_unittest.cc
deleted file mode 100644
index a3aa3341a0..0000000000
--- a/base/synchronization/atomic_flag_unittest.cc
+++ /dev/null
@@ -1,131 +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/synchronization/atomic_flag.h"
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/single_thread_task_runner.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/test/gtest_util.h"
-#include "base/threading/platform_thread.h"
-#include "base/threading/thread.h"
-#include "build/build_config.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace base {
-
-namespace {
-
-void ExpectSetFlagDeath(AtomicFlag* flag) {
- ASSERT_TRUE(flag);
- EXPECT_DCHECK_DEATH(flag->Set());
-}
-
-// Busy waits (to explicitly avoid using synchronization constructs that would
-// defeat the purpose of testing atomics) until |tested_flag| is set and then
-// verifies that non-atomic |*expected_after_flag| is true and sets |*done_flag|
-// before returning if it's non-null.
-void BusyWaitUntilFlagIsSet(AtomicFlag* tested_flag, bool* expected_after_flag,
- AtomicFlag* done_flag) {
- while (!tested_flag->IsSet())
- PlatformThread::YieldCurrentThread();
-
- EXPECT_TRUE(*expected_after_flag);
- if (done_flag)
- done_flag->Set();
-}
-
-} // namespace
-
-TEST(AtomicFlagTest, SimpleSingleThreadedTest) {
- AtomicFlag flag;
- ASSERT_FALSE(flag.IsSet());
- flag.Set();
- ASSERT_TRUE(flag.IsSet());
-}
-
-TEST(AtomicFlagTest, DoubleSetTest) {
- AtomicFlag flag;
- ASSERT_FALSE(flag.IsSet());
- flag.Set();
- ASSERT_TRUE(flag.IsSet());
- flag.Set();
- ASSERT_TRUE(flag.IsSet());
-}
-
-TEST(AtomicFlagTest, ReadFromDifferentThread) {
- // |tested_flag| is the one being tested below.
- AtomicFlag tested_flag;
- // |expected_after_flag| is used to confirm that sequential consistency is
- // obtained around |tested_flag|.
- bool expected_after_flag = false;
- // |reset_flag| is used to confirm the test flows as intended without using
- // synchronization constructs which would defeat the purpose of exercising
- // atomics.
- AtomicFlag reset_flag;
-
- Thread thread("AtomicFlagTest.ReadFromDifferentThread");
- ASSERT_TRUE(thread.Start());
- thread.task_runner()->PostTask(
- FROM_HERE,
- Bind(&BusyWaitUntilFlagIsSet, &tested_flag, &expected_after_flag,
- &reset_flag));
-
- // To verify that IsSet() fetches the flag's value from memory every time it
- // is called (not just the first time that it is called on a thread), sleep
- // before setting the flag.
- PlatformThread::Sleep(TimeDelta::FromMilliseconds(20));
-
- // |expected_after_flag| is used to verify that all memory operations
- // performed before |tested_flag| is Set() are visible to threads that can see
- // IsSet().
- expected_after_flag = true;
- tested_flag.Set();
-
- // Sleep again to give the busy loop time to observe the flag and verify
- // expectations.
- PlatformThread::Sleep(TimeDelta::FromMilliseconds(20));
-
- // Use |reset_flag| to confirm that the above completed (which the rest of
- // this test assumes).
- ASSERT_TRUE(reset_flag.IsSet());
-
- tested_flag.UnsafeResetForTesting();
- EXPECT_FALSE(tested_flag.IsSet());
- expected_after_flag = false;
-
- // Perform the same test again after the controlled UnsafeResetForTesting(),
- // |thread| is guaranteed to be synchronized past the
- // |UnsafeResetForTesting()| call when the task runs per the implicit
- // synchronization in the post task mechanism.
- thread.task_runner()->PostTask(
- FROM_HERE,
- Bind(&BusyWaitUntilFlagIsSet, &tested_flag, &expected_after_flag,
- nullptr));
-
- PlatformThread::Sleep(TimeDelta::FromMilliseconds(20));
-
- expected_after_flag = true;
- tested_flag.Set();
-
- // The |thread|'s destructor will block until the posted task completes, so
- // the test will time out if it fails to see the flag be set.
-}
-
-TEST(AtomicFlagTest, SetOnDifferentSequenceDeathTest) {
- // Checks that Set() can't be called from another sequence after being called
- // on this one. AtomicFlag should die on a DCHECK if Set() is called again
- // from another sequence.
- ::testing::FLAGS_gtest_death_test_style = "threadsafe";
- Thread t("AtomicFlagTest.SetOnDifferentThreadDeathTest");
- ASSERT_TRUE(t.Start());
- EXPECT_TRUE(t.WaitUntilThreadStarted());
-
- AtomicFlag flag;
- flag.Set();
- t.task_runner()->PostTask(FROM_HERE, Bind(&ExpectSetFlagDeath, &flag));
-}
-
-} // namespace base
diff --git a/base/synchronization/cancellation_flag.cc b/base/synchronization/cancellation_flag.cc
new file mode 100644
index 0000000000..ca5c0a8283
--- /dev/null
+++ b/base/synchronization/cancellation_flag.cc
@@ -0,0 +1,26 @@
+// 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/synchronization/cancellation_flag.h"
+
+#include "base/logging.h"
+
+namespace base {
+
+void CancellationFlag::Set() {
+#if !defined(NDEBUG)
+ DCHECK_EQ(set_on_, PlatformThread::CurrentId());
+#endif
+ base::subtle::Release_Store(&flag_, 1);
+}
+
+bool CancellationFlag::IsSet() const {
+ return base::subtle::Acquire_Load(&flag_) != 0;
+}
+
+void CancellationFlag::UnsafeResetForTesting() {
+ base::subtle::Release_Store(&flag_, 0);
+}
+
+} // namespace base
diff --git a/base/synchronization/cancellation_flag.h b/base/synchronization/cancellation_flag.h
index 39094e2dc0..f2f83f47da 100644
--- a/base/synchronization/cancellation_flag.h
+++ b/base/synchronization/cancellation_flag.h
@@ -5,15 +5,44 @@
#ifndef BASE_SYNCHRONIZATION_CANCELLATION_FLAG_H_
#define BASE_SYNCHRONIZATION_CANCELLATION_FLAG_H_
-#include "base/synchronization/atomic_flag.h"
+#include "base/atomicops.h"
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "base/threading/platform_thread.h"
namespace base {
-// Use inheritance instead of "using" to allow forward declaration of "class
-// CancellationFlag".
-// TODO(fdoray): Replace CancellationFlag with AtomicFlag throughout the
-// codebase and delete this file. crbug.com/630251
-class CancellationFlag : public AtomicFlag {};
+// CancellationFlag allows one thread to cancel jobs executed on some worker
+// thread. Calling Set() from one thread and IsSet() from a number of threads
+// is thread-safe.
+//
+// This class IS NOT intended for synchronization between threads.
+class BASE_EXPORT CancellationFlag {
+ public:
+ CancellationFlag() : flag_(false) {
+#if !defined(NDEBUG)
+ set_on_ = PlatformThread::CurrentId();
+#endif
+ }
+ ~CancellationFlag() {}
+
+ // Set the flag. May only be called on the thread which owns the object.
+ void Set();
+ bool IsSet() const; // Returns true iff the flag was set.
+
+ // For subtle reasons that may be different on different architectures,
+ // a different thread testing IsSet() may erroneously read 'true' after
+ // this method has been called.
+ void UnsafeResetForTesting();
+
+ private:
+ base::subtle::Atomic32 flag_;
+#if !defined(NDEBUG)
+ PlatformThreadId set_on_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(CancellationFlag);
+};
} // namespace base
diff --git a/base/synchronization/cancellation_flag_unittest.cc b/base/synchronization/cancellation_flag_unittest.cc
new file mode 100644
index 0000000000..13c74bcbd4
--- /dev/null
+++ b/base/synchronization/cancellation_flag_unittest.cc
@@ -0,0 +1,65 @@
+// 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.
+
+// Tests of CancellationFlag class.
+
+#include "base/synchronization/cancellation_flag.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/spin_wait.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+namespace base {
+
+namespace {
+
+//------------------------------------------------------------------------------
+// Define our test class.
+//------------------------------------------------------------------------------
+
+void CancelHelper(CancellationFlag* flag) {
+#if GTEST_HAS_DEATH_TEST
+ ASSERT_DEBUG_DEATH(flag->Set(), "");
+#endif
+}
+
+TEST(CancellationFlagTest, SimpleSingleThreadedTest) {
+ CancellationFlag flag;
+ ASSERT_FALSE(flag.IsSet());
+ flag.Set();
+ ASSERT_TRUE(flag.IsSet());
+}
+
+TEST(CancellationFlagTest, DoubleSetTest) {
+ CancellationFlag flag;
+ ASSERT_FALSE(flag.IsSet());
+ flag.Set();
+ ASSERT_TRUE(flag.IsSet());
+ flag.Set();
+ ASSERT_TRUE(flag.IsSet());
+}
+
+TEST(CancellationFlagTest, SetOnDifferentThreadDeathTest) {
+ // Checks that Set() can't be called from any other thread.
+ // CancellationFlag should die on a DCHECK if Set() is called from
+ // other thread.
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+ Thread t("CancellationFlagTest.SetOnDifferentThreadDeathTest");
+ ASSERT_TRUE(t.Start());
+ ASSERT_TRUE(t.message_loop());
+ ASSERT_TRUE(t.IsRunning());
+
+ CancellationFlag flag;
+ t.task_runner()->PostTask(FROM_HERE, base::Bind(&CancelHelper, &flag));
+}
+
+} // namespace
+
+} // namespace base
diff --git a/base/synchronization/condition_variable.h b/base/synchronization/condition_variable.h
index b567751172..ebf90d249a 100644
--- a/base/synchronization/condition_variable.h
+++ b/base/synchronization/condition_variable.h
@@ -91,13 +91,11 @@ class BASE_EXPORT ConditionVariable {
~ConditionVariable();
// Wait() releases the caller's critical section atomically as it starts to
- // sleep, and the reacquires it when it is signaled. The wait functions are
- // susceptible to spurious wakeups. (See usage note 1 for more details.)
+ // sleep, and the reacquires it when it is signaled.
void Wait();
void TimedWait(const TimeDelta& max_time);
- // Broadcast() revives all waiting threads. (See usage note 2 for more
- // details.)
+ // Broadcast() revives all waiting threads.
void Broadcast();
// Signal() revives one waiting thread.
void Signal();
diff --git a/base/synchronization/condition_variable_posix.cc b/base/synchronization/condition_variable_posix.cc
index d07c671810..d86fd180ec 100644
--- a/base/synchronization/condition_variable_posix.cc
+++ b/base/synchronization/condition_variable_posix.cc
@@ -118,8 +118,6 @@ void ConditionVariable::TimedWait(const TimeDelta& max_time) {
#endif // OS_ANDROID && HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
#endif // OS_MACOSX
- // On failure, we only expect the CV to timeout. Any other error value means
- // that we've unexpectedly woken up.
DCHECK(rv == 0 || rv == ETIMEDOUT);
#if DCHECK_IS_ON()
user_lock_->CheckUnheldAndMark();
diff --git a/base/synchronization/lock.h b/base/synchronization/lock.h
index 599984e8b6..fbf6cef769 100644
--- a/base/synchronization/lock.h
+++ b/base/synchronization/lock.h
@@ -61,23 +61,6 @@ class BASE_EXPORT Lock {
void AssertAcquired() const;
#endif // DCHECK_IS_ON()
- // Whether Lock mitigates priority inversion when used from different thread
- // priorities.
- static bool HandlesMultipleThreadPriorities() {
-#if defined(OS_POSIX)
- // POSIX mitigates priority inversion by setting the priority of a thread
- // holding a Lock to the maximum priority of any other thread waiting on it.
- return internal::LockImpl::PriorityInheritanceAvailable();
-#elif defined(OS_WIN)
- // Windows mitigates priority inversion by randomly boosting the priority of
- // ready threads.
- // https://msdn.microsoft.com/library/windows/desktop/ms684831.aspx
- return true;
-#else
-#error Unsupported platform
-#endif
- }
-
#if defined(OS_POSIX) || defined(OS_WIN)
// Both Windows and POSIX implementations of ConditionVariable need to be
// able to see our lock and tweak our debugging counters, as they release and
diff --git a/base/synchronization/lock_impl.h b/base/synchronization/lock_impl.h
index 603585a050..cbaabc784b 100644
--- a/base/synchronization/lock_impl.h
+++ b/base/synchronization/lock_impl.h
@@ -48,11 +48,6 @@ class BASE_EXPORT LockImpl {
// unnecessary.
NativeHandle* native_handle() { return &native_handle_; }
-#if defined(OS_POSIX)
- // Whether this lock will attempt to use priority inheritance.
- static bool PriorityInheritanceAvailable();
-#endif
-
private:
NativeHandle native_handle_;
diff --git a/base/synchronization/lock_impl_posix.cc b/base/synchronization/lock_impl_posix.cc
index ff997ea65f..5619adaf5d 100644
--- a/base/synchronization/lock_impl_posix.cc
+++ b/base/synchronization/lock_impl_posix.cc
@@ -7,45 +7,27 @@
#include <errno.h>
#include <string.h>
-#include "base/debug/activity_tracker.h"
#include "base/logging.h"
-#include "base/synchronization/lock.h"
namespace base {
namespace internal {
-// Determines which platforms can consider using priority inheritance locks. Use
-// this define for platform code that may not compile if priority inheritance
-// locks aren't available. For this platform code,
-// PRIORITY_INHERITANCE_LOCKS_POSSIBLE() is a necessary but insufficient check.
-// Lock::PriorityInheritanceAvailable still must be checked as the code may
-// compile but the underlying platform still may not correctly support priority
-// inheritance locks.
-#if defined(OS_NACL) || defined(OS_ANDROID) || defined(__ANDROID__)
-#define PRIORITY_INHERITANCE_LOCKS_POSSIBLE() 0
-#else
-#define PRIORITY_INHERITANCE_LOCKS_POSSIBLE() 1
-#endif
-
LockImpl::LockImpl() {
+#ifndef NDEBUG
+ // In debug, setup attributes for lock error checking.
pthread_mutexattr_t mta;
int rv = pthread_mutexattr_init(&mta);
DCHECK_EQ(rv, 0) << ". " << strerror(rv);
-#if PRIORITY_INHERITANCE_LOCKS_POSSIBLE()
- if (PriorityInheritanceAvailable()) {
- rv = pthread_mutexattr_setprotocol(&mta, PTHREAD_PRIO_INHERIT);
- DCHECK_EQ(rv, 0) << ". " << strerror(rv);
- }
-#endif
-#ifndef NDEBUG
- // In debug, setup attributes for lock error checking.
rv = pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_ERRORCHECK);
DCHECK_EQ(rv, 0) << ". " << strerror(rv);
-#endif
rv = pthread_mutex_init(&native_handle_, &mta);
DCHECK_EQ(rv, 0) << ". " << strerror(rv);
rv = pthread_mutexattr_destroy(&mta);
DCHECK_EQ(rv, 0) << ". " << strerror(rv);
+#else
+ // In release, go with the default lock attributes.
+ pthread_mutex_init(&native_handle_, NULL);
+#endif
}
LockImpl::~LockImpl() {
@@ -60,7 +42,6 @@ bool LockImpl::Try() {
}
void LockImpl::Lock() {
- base::debug::ScopedLockAcquireActivity lock_activity(this);
int rv = pthread_mutex_lock(&native_handle_);
DCHECK_EQ(rv, 0) << ". " << strerror(rv);
}
@@ -70,29 +51,5 @@ void LockImpl::Unlock() {
DCHECK_EQ(rv, 0) << ". " << strerror(rv);
}
-// static
-bool LockImpl::PriorityInheritanceAvailable() {
-#if PRIORITY_INHERITANCE_LOCKS_POSSIBLE() && defined(OS_MACOSX)
- return true;
-#else
- // Security concerns prevent the use of priority inheritance mutexes on Linux.
- // * CVE-2010-0622 - wake_futex_pi unlocks incorrect, possible DoS.
- // https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-0622
- // * CVE-2012-6647 - Linux < 3.5.1, futex_wait_requeue_pi possible DoS.
- // https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-6647
- // * CVE-2014-3153 - Linux <= 3.14.5, futex_requeue, privilege escalation.
- // https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-3153
- //
- // If the above were all addressed, we still need a runtime check to deal with
- // the bug below.
- // * glibc Bug 14652: https://sourceware.org/bugzilla/show_bug.cgi?id=14652
- // Fixed in glibc 2.17.
- // Priority inheritance mutexes may deadlock with condition variables
- // during recacquisition of the mutex after the condition variable is
- // signalled.
- return false;
-#endif
-}
-
} // namespace internal
} // namespace base
diff --git a/base/synchronization/waitable_event.h b/base/synchronization/waitable_event.h
index 761965f03a..3863e98455 100644
--- a/base/synchronization/waitable_event.h
+++ b/base/synchronization/waitable_event.h
@@ -25,7 +25,6 @@
namespace base {
class TimeDelta;
-class TimeTicks;
// A WaitableEvent can be a useful thread synchronization tool when you want to
// allow one thread to wait for another thread to finish some work. For
@@ -87,17 +86,12 @@ class BASE_EXPORT WaitableEvent {
// delete e;
void Wait();
- // Wait up until wait_delta has passed for the event to be signaled. Returns
- // true if the event was signaled.
+ // Wait up until max_time has passed for the event to be signaled. Returns
+ // true if the event was signaled. If this method returns false, then it
+ // does not necessarily mean that max_time was exceeded.
//
// TimedWait can synchronise its own destruction like |Wait|.
- bool TimedWait(const TimeDelta& wait_delta);
-
- // Wait up until end_time deadline has passed for the event to be signaled.
- // Return true if the event was signaled.
- //
- // TimedWaitUntil can synchronise its own destruction like |Wait|.
- bool TimedWaitUntil(const TimeTicks& end_time);
+ bool TimedWait(const TimeDelta& max_time);
#if defined(OS_WIN)
HANDLE handle() const { return handle_.Get(); }
diff --git a/base/synchronization/waitable_event_posix.cc b/base/synchronization/waitable_event_posix.cc
index 5dfff468ad..b32c882711 100644
--- a/base/synchronization/waitable_event_posix.cc
+++ b/base/synchronization/waitable_event_posix.cc
@@ -7,7 +7,6 @@
#include <algorithm>
#include <vector>
-#include "base/debug/activity_tracker.h"
#include "base/logging.h"
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
@@ -153,22 +152,14 @@ class SyncWaiter : public WaitableEvent::Waiter {
};
void WaitableEvent::Wait() {
- bool result = TimedWaitUntil(TimeTicks::Max());
+ bool result = TimedWait(TimeDelta::FromSeconds(-1));
DCHECK(result) << "TimedWait() should never fail with infinite timeout";
}
-bool WaitableEvent::TimedWait(const TimeDelta& wait_delta) {
- // TimeTicks takes care of overflow including the cases when wait_delta
- // is a maximum value.
- return TimedWaitUntil(TimeTicks::Now() + wait_delta);
-}
-
-bool WaitableEvent::TimedWaitUntil(const TimeTicks& end_time) {
+bool WaitableEvent::TimedWait(const TimeDelta& max_time) {
base::ThreadRestrictions::AssertWaitAllowed();
- // Record the event that this thread is blocking upon (for hang diagnosis).
- base::debug::ScopedEventWaitActivity event_activity(this);
-
- const bool finite_time = !end_time.is_max();
+ const TimeTicks end_time(TimeTicks::Now() + max_time);
+ const bool finite_time = max_time.ToInternalValue() >= 0;
kernel_->lock_.Acquire();
if (kernel_->signaled_) {
@@ -241,9 +232,6 @@ size_t WaitableEvent::WaitMany(WaitableEvent** raw_waitables,
base::ThreadRestrictions::AssertWaitAllowed();
DCHECK(count) << "Cannot wait on no events";
- // Record an event (the first) that this thread is blocking upon.
- base::debug::ScopedEventWaitActivity event_activity(raw_waitables[0]);
-
// We need to acquire the locks in a globally consistent order. Thus we sort
// the array of waitables by address. We actually sort a pairs so that we can
// map back to the original index values later.
diff --git a/base/synchronization/waitable_event_unittest.cc b/base/synchronization/waitable_event_unittest.cc
index c0e280aa97..ac5c9f1255 100644
--- a/base/synchronization/waitable_event_unittest.cc
+++ b/base/synchronization/waitable_event_unittest.cc
@@ -136,7 +136,13 @@ TEST(WaitableEventTest, WaitMany) {
// Tests that using TimeDelta::Max() on TimedWait() is not the same as passing
// a timeout of 0. (crbug.com/465948)
-TEST(WaitableEventTest, TimedWait) {
+#if defined(OS_POSIX)
+// crbug.com/465948 not fixed yet.
+#define MAYBE_TimedWait DISABLED_TimedWait
+#else
+#define MAYBE_TimedWait TimedWait
+#endif
+TEST(WaitableEventTest, MAYBE_TimedWait) {
WaitableEvent* ev =
new WaitableEvent(WaitableEvent::ResetPolicy::AUTOMATIC,
WaitableEvent::InitialState::NOT_SIGNALED);
@@ -147,58 +153,11 @@ TEST(WaitableEventTest, TimedWait) {
TimeTicks start = TimeTicks::Now();
PlatformThread::Create(0, &signaler, &thread);
- EXPECT_TRUE(ev->TimedWait(TimeDelta::Max()));
+ ev->TimedWait(TimeDelta::Max());
EXPECT_GE(TimeTicks::Now() - start, thread_delay);
delete ev;
PlatformThread::Join(thread);
}
-// Tests that a sub-ms TimedWait doesn't time out promptly.
-TEST(WaitableEventTest, SubMsTimedWait) {
- WaitableEvent ev(WaitableEvent::ResetPolicy::AUTOMATIC,
- WaitableEvent::InitialState::NOT_SIGNALED);
-
- TimeDelta delay = TimeDelta::FromMicroseconds(900);
- TimeTicks start_time = TimeTicks::Now();
- ev.TimedWait(delay);
- EXPECT_GE(TimeTicks::Now() - start_time, delay);
-}
-
-// Tests that TimedWaitUntil can be safely used with various end_time deadline
-// values.
-TEST(WaitableEventTest, TimedWaitUntil) {
- WaitableEvent ev(WaitableEvent::ResetPolicy::AUTOMATIC,
- WaitableEvent::InitialState::NOT_SIGNALED);
-
- TimeTicks start_time(TimeTicks::Now());
- TimeDelta delay = TimeDelta::FromMilliseconds(10);
-
- // Should be OK to wait for the current time or time in the past.
- // That should end promptly and be equivalent to IsSignalled.
- EXPECT_FALSE(ev.TimedWaitUntil(start_time));
- EXPECT_FALSE(ev.TimedWaitUntil(start_time - delay));
-
- // Should be OK to wait for zero TimeTicks().
- EXPECT_FALSE(ev.TimedWaitUntil(TimeTicks()));
-
- // Waiting for a time in the future shouldn't end before the deadline
- // if the event isn't signalled.
- EXPECT_FALSE(ev.TimedWaitUntil(start_time + delay));
- EXPECT_GE(TimeTicks::Now() - start_time, delay);
-
- // Test that passing TimeTicks::Max to TimedWaitUntil is valid and isn't
- // the same as passing TimeTicks(). Also verifies that signaling event
- // ends the wait promptly.
- WaitableEventSignaler signaler(delay, &ev);
- PlatformThreadHandle thread;
- start_time = TimeTicks::Now();
- PlatformThread::Create(0, &signaler, &thread);
-
- EXPECT_TRUE(ev.TimedWaitUntil(TimeTicks::Max()));
- EXPECT_GE(TimeTicks::Now() - start_time, delay);
-
- PlatformThread::Join(thread);
-}
-
} // namespace base
diff --git a/base/synchronization/waitable_event_watcher.h b/base/synchronization/waitable_event_watcher.h
index 44ef5047ed..eb51effa49 100644
--- a/base/synchronization/waitable_event_watcher.h
+++ b/base/synchronization/waitable_event_watcher.h
@@ -6,14 +6,13 @@
#define BASE_SYNCHRONIZATION_WAITABLE_EVENT_WATCHER_H_
#include "base/base_export.h"
-#include "base/macros.h"
-#include "base/sequence_checker.h"
#include "build/build_config.h"
#if defined(OS_WIN)
#include "base/win/object_watcher.h"
#else
#include "base/callback.h"
+#include "base/message_loop/message_loop.h"
#include "base/synchronization/waitable_event.h"
#endif
@@ -21,13 +20,14 @@ namespace base {
class Flag;
class AsyncWaiter;
+class AsyncCallbackTask;
class WaitableEvent;
// This class provides a way to wait on a WaitableEvent asynchronously.
//
// Each instance of this object can be waiting on a single WaitableEvent. When
-// the waitable event is signaled, a callback is invoked on the sequence that
-// called StartWatching(). This callback can be deleted by deleting the waiter.
+// the waitable event is signaled, a callback is made in the thread of a given
+// MessageLoop. This callback can be deleted by deleting the waiter.
//
// Typical usage:
//
@@ -60,56 +60,53 @@ class WaitableEvent;
class BASE_EXPORT WaitableEventWatcher
#if defined(OS_WIN)
- : public win::ObjectWatcher::Delegate
+ : public win::ObjectWatcher::Delegate {
+#else
+ : public MessageLoop::DestructionObserver {
#endif
-{
public:
typedef Callback<void(WaitableEvent*)> EventCallback;
WaitableEventWatcher();
-
-#if defined(OS_WIN)
~WaitableEventWatcher() override;
-#else
- ~WaitableEventWatcher();
-#endif
- // When |event| is signaled, |callback| is called on the sequence that called
- // StartWatching().
+ // When @event is signaled, the given callback is called on the thread of the
+ // current message loop when StartWatching is called.
bool StartWatching(WaitableEvent* event, const EventCallback& callback);
- // Cancel the current watch. Must be called from the same sequence which
+ // Cancel the current watch. Must be called from the same thread which
// started the watch.
//
// Does nothing if no event is being watched, nor if the watch has completed.
// The callback will *not* be called for the current watch after this
- // function returns. Since the callback runs on the same sequence as this
+ // function returns. Since the callback runs on the same thread as this
// function, it cannot be called during this function either.
void StopWatching();
+ // Return the currently watched event, or NULL if no object is currently being
+ // watched.
+ WaitableEvent* GetWatchedEvent();
+
+ // Return the callback that will be invoked when the event is
+ // signaled.
+ const EventCallback& callback() const { return callback_; }
+
private:
#if defined(OS_WIN)
void OnObjectSignaled(HANDLE h) override;
-
win::ObjectWatcher watcher_;
- EventCallback callback_;
- WaitableEvent* event_ = nullptr;
#else
- // Instantiated in StartWatching(). Set before the callback runs. Reset in
- // StopWatching() or StartWatching().
- scoped_refptr<Flag> cancel_flag_;
-
- // Enqueued in the wait list of the watched WaitableEvent.
- AsyncWaiter* waiter_ = nullptr;
+ // Implementation of MessageLoop::DestructionObserver
+ void WillDestroyCurrentMessageLoop() override;
- // Kernel of the watched WaitableEvent.
+ MessageLoop* message_loop_;
+ scoped_refptr<Flag> cancel_flag_;
+ AsyncWaiter* waiter_;
+ base::Closure internal_callback_;
scoped_refptr<WaitableEvent::WaitableEventKernel> kernel_;
-
- // Ensures that StartWatching() and StopWatching() are called on the same
- // sequence.
- SequenceChecker sequence_checker_;
#endif
- DISALLOW_COPY_AND_ASSIGN(WaitableEventWatcher);
+ WaitableEvent* event_;
+ EventCallback callback_;
};
} // namespace base
diff --git a/base/synchronization/waitable_event_watcher_posix.cc b/base/synchronization/waitable_event_watcher_posix.cc
index 3adbc5f977..7cf8688d4c 100644
--- a/base/synchronization/waitable_event_watcher_posix.cc
+++ b/base/synchronization/waitable_event_watcher_posix.cc
@@ -4,12 +4,12 @@
#include "base/synchronization/waitable_event_watcher.h"
-#include <utility>
-
#include "base/bind.h"
-#include "base/logging.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/single_thread_task_runner.h"
#include "base/synchronization/lock.h"
-#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/synchronization/waitable_event.h"
namespace base {
@@ -17,15 +17,14 @@ namespace base {
// WaitableEventWatcher (async waits).
//
// The basic design is that we add an AsyncWaiter to the wait-list of the event.
-// That AsyncWaiter has a pointer to SequencedTaskRunner, and a Task to be
-// posted to it. The task ends up calling the callback when it runs on the
-// sequence.
+// That AsyncWaiter has a pointer to MessageLoop, and a Task to be posted to it.
+// The MessageLoop ends up running the task, which calls the delegate.
//
// Since the wait can be canceled, we have a thread-safe Flag object which is
// set when the wait has been canceled. At each stage in the above, we check the
// flag before going onto the next stage. Since the wait may only be canceled in
-// the sequence which runs the Task, we are assured that the callback cannot be
-// called after canceling...
+// the MessageLoop which runs the Task, we are assured that the delegate cannot
+// be called after canceling...
// -----------------------------------------------------------------------------
// A thread-safe, reference-counted, write-once flag.
@@ -55,22 +54,23 @@ class Flag : public RefCountedThreadSafe<Flag> {
};
// -----------------------------------------------------------------------------
-// This is an asynchronous waiter which posts a task to a SequencedTaskRunner
-// when fired. An AsyncWaiter may only be in a single wait-list.
+// This is an asynchronous waiter which posts a task to a MessageLoop when
+// fired. An AsyncWaiter may only be in a single wait-list.
// -----------------------------------------------------------------------------
class AsyncWaiter : public WaitableEvent::Waiter {
public:
- AsyncWaiter(scoped_refptr<SequencedTaskRunner> task_runner,
+ AsyncWaiter(MessageLoop* message_loop,
const base::Closure& callback,
Flag* flag)
- : task_runner_(std::move(task_runner)),
+ : message_loop_(message_loop),
callback_(callback),
- flag_(flag) {}
+ flag_(flag) { }
bool Fire(WaitableEvent* event) override {
// Post the callback if we haven't been cancelled.
- if (!flag_->value())
- task_runner_->PostTask(FROM_HERE, callback_);
+ if (!flag_->value()) {
+ message_loop_->task_runner()->PostTask(FROM_HERE, callback_);
+ }
// We are removed from the wait-list by the WaitableEvent itself. It only
// remains to delete ourselves.
@@ -85,37 +85,37 @@ class AsyncWaiter : public WaitableEvent::Waiter {
bool Compare(void* tag) override { return tag == flag_.get(); }
private:
- const scoped_refptr<SequencedTaskRunner> task_runner_;
- const base::Closure callback_;
- const scoped_refptr<Flag> flag_;
+ MessageLoop *const message_loop_;
+ base::Closure callback_;
+ scoped_refptr<Flag> flag_;
};
// -----------------------------------------------------------------------------
-// For async waits we need to run a callback on a sequence. We do this by
-// posting an AsyncCallbackHelper task, which calls the callback and keeps track
-// of when the event is canceled.
+// For async waits we need to make a callback in a MessageLoop thread. We do
+// this by posting a callback, which calls the delegate and keeps track of when
+// the event is canceled.
// -----------------------------------------------------------------------------
void AsyncCallbackHelper(Flag* flag,
const WaitableEventWatcher::EventCallback& callback,
WaitableEvent* event) {
- // Runs on the sequence that called StartWatching().
+ // Runs in MessageLoop thread.
if (!flag->value()) {
- // This is to let the WaitableEventWatcher know that the event has occured.
+ // This is to let the WaitableEventWatcher know that the event has occured
+ // because it needs to be able to return NULL from GetWatchedObject
flag->Set();
callback.Run(event);
}
}
-WaitableEventWatcher::WaitableEventWatcher() {
- sequence_checker_.DetachFromSequence();
+WaitableEventWatcher::WaitableEventWatcher()
+ : message_loop_(NULL),
+ cancel_flag_(NULL),
+ waiter_(NULL),
+ event_(NULL) {
}
WaitableEventWatcher::~WaitableEventWatcher() {
- // The destructor may be called from a different sequence than StartWatching()
- // when there is no active watch. To avoid triggering a DCHECK in
- // StopWatching(), do not call it when there is no active watch.
- if (cancel_flag_ && !cancel_flag_->value())
- StopWatching();
+ StopWatching();
}
// -----------------------------------------------------------------------------
@@ -125,44 +125,61 @@ WaitableEventWatcher::~WaitableEventWatcher() {
bool WaitableEventWatcher::StartWatching(
WaitableEvent* event,
const EventCallback& callback) {
- DCHECK(sequence_checker_.CalledOnValidSequence());
- DCHECK(SequencedTaskRunnerHandle::Get());
+ MessageLoop *const current_ml = MessageLoop::current();
+ DCHECK(current_ml) << "Cannot create WaitableEventWatcher without a "
+ "current MessageLoop";
// A user may call StartWatching from within the callback function. In this
// case, we won't know that we have finished watching, expect that the Flag
// will have been set in AsyncCallbackHelper().
- if (cancel_flag_.get() && cancel_flag_->value())
- cancel_flag_ = nullptr;
+ if (cancel_flag_.get() && cancel_flag_->value()) {
+ if (message_loop_) {
+ message_loop_->RemoveDestructionObserver(this);
+ message_loop_ = NULL;
+ }
+
+ cancel_flag_ = NULL;
+ }
- DCHECK(!cancel_flag_) << "StartWatching called while still watching";
+ DCHECK(!cancel_flag_.get()) << "StartWatching called while still watching";
cancel_flag_ = new Flag;
- const Closure internal_callback = base::Bind(
- &AsyncCallbackHelper, base::RetainedRef(cancel_flag_), callback, event);
+ callback_ = callback;
+ internal_callback_ = base::Bind(
+ &AsyncCallbackHelper, base::RetainedRef(cancel_flag_), callback_, event);
WaitableEvent::WaitableEventKernel* kernel = event->kernel_.get();
AutoLock locked(kernel->lock_);
+ event_ = event;
+
if (kernel->signaled_) {
if (!kernel->manual_reset_)
kernel->signaled_ = false;
// No hairpinning - we can't call the delegate directly here. We have to
- // post a task to the SequencedTaskRunnerHandle as usual.
- SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE, internal_callback);
+ // enqueue a task on the MessageLoop as normal.
+ current_ml->task_runner()->PostTask(FROM_HERE, internal_callback_);
return true;
}
+ message_loop_ = current_ml;
+ current_ml->AddDestructionObserver(this);
+
kernel_ = kernel;
- waiter_ = new AsyncWaiter(SequencedTaskRunnerHandle::Get(), internal_callback,
- cancel_flag_.get());
+ waiter_ = new AsyncWaiter(current_ml, internal_callback_, cancel_flag_.get());
event->Enqueue(waiter_);
return true;
}
void WaitableEventWatcher::StopWatching() {
- DCHECK(sequence_checker_.CalledOnValidSequence());
+ callback_.Reset();
+
+ if (message_loop_) {
+ message_loop_->RemoveDestructionObserver(this);
+ message_loop_ = NULL;
+ }
if (!cancel_flag_.get()) // if not currently watching...
return;
@@ -210,24 +227,44 @@ void WaitableEventWatcher::StopWatching() {
// have been enqueued with the MessageLoop because the waiter was never
// signaled)
delete waiter_;
+ internal_callback_.Reset();
cancel_flag_ = NULL;
return;
}
- // Case 3: the waiter isn't on the wait-list, thus it was signaled. It may not
- // have run yet, so we set the flag to tell it not to bother enqueuing the
- // task on the SequencedTaskRunner, but to delete it instead. The Waiter
- // deletes itself once run.
+ // Case 3: the waiter isn't on the wait-list, thus it was signaled. It may
+ // not have run yet, so we set the flag to tell it not to bother enqueuing the
+ // task on the MessageLoop, but to delete it instead. The Waiter deletes
+ // itself once run.
cancel_flag_->Set();
cancel_flag_ = NULL;
// If the waiter has already run then the task has been enqueued. If the Task
// hasn't yet run, the flag will stop the delegate from getting called. (This
- // is thread safe because one may only delete a Handle from the sequence that
- // called StartWatching()).
+ // is thread safe because one may only delete a Handle from the MessageLoop
+ // thread.)
//
// If the delegate has already been called then we have nothing to do. The
// task has been deleted by the MessageLoop.
}
+WaitableEvent* WaitableEventWatcher::GetWatchedEvent() {
+ if (!cancel_flag_.get())
+ return NULL;
+
+ if (cancel_flag_->value())
+ return NULL;
+
+ return event_;
+}
+
+// -----------------------------------------------------------------------------
+// This is called when the MessageLoop which the callback will be run it is
+// deleted. We need to cancel the callback as if we had been deleted, but we
+// will still be deleted at some point in the future.
+// -----------------------------------------------------------------------------
+void WaitableEventWatcher::WillDestroyCurrentMessageLoop() {
+ StopWatching();
+}
+
} // namespace base
diff --git a/base/sys_byteorder.h b/base/sys_byteorder.h
index 9ee1827e1e..8d9066c702 100644
--- a/base/sys_byteorder.h
+++ b/base/sys_byteorder.h
@@ -13,7 +13,6 @@
#include <stdint.h>
-#include "base/logging.h"
#include "build/build_config.h"
#if defined(COMPILER_MSVC)
@@ -47,21 +46,6 @@ inline uint64_t ByteSwap(uint64_t x) {
#endif
}
-inline uintptr_t ByteSwapUintPtrT(uintptr_t x) {
- // We do it this way because some build configurations are ILP32 even when
- // defined(ARCH_CPU_64_BITS). Unfortunately, we can't use sizeof in #ifs. But,
- // because these conditionals are constexprs, the irrelevant branches will
- // likely be optimized away, so this construction should not result in code
- // bloat.
- if (sizeof(uintptr_t) == 4) {
- return ByteSwap(static_cast<uint32_t>(x));
- } else if (sizeof(uintptr_t) == 8) {
- return ByteSwap(static_cast<uint64_t>(x));
- } else {
- NOTREACHED();
- }
-}
-
// Converts the bytes in |x| from host order (endianness) to little endian, and
// returns the result.
inline uint16_t ByteSwapToLE16(uint16_t x) {
diff --git a/base/sys_info.h b/base/sys_info.h
index e35feff735..b10747703d 100644
--- a/base/sys_info.h
+++ b/base/sys_info.h
@@ -107,19 +107,9 @@ class BASE_EXPORT SysInfo {
static bool GetLsbReleaseValue(const std::string& key, std::string* value);
// Convenience function for GetLsbReleaseValue("CHROMEOS_RELEASE_BOARD",...).
- // Returns "unknown" if CHROMEOS_RELEASE_BOARD is not set. Otherwise returns
- // the full name of the board. WARNING: the returned value often differs in
- // developer built system compared to devices that use the official version.
- // E.g. for developer built version, the function could return 'glimmer' while
- // for officially used versions it would be like 'glimmer-signed-mp-v4keys'.
- // Use GetStrippedReleaseBoard() function if you need only the short name of
- // the board (would be 'glimmer' in the case described above).
+ // Returns "unknown" if CHROMEOS_RELEASE_BOARD is not set.
static std::string GetLsbReleaseBoard();
- // Convenience function for GetLsbReleaseBoard() removing trailing "-signed-*"
- // if present. Returns "unknown" if CHROMEOS_RELEASE_BOARD is not set.
- static std::string GetStrippedReleaseBoard();
-
// Returns the creation time of /etc/lsb-release. (Used to get the date and
// time of the Chrome OS build).
static Time GetLsbReleaseTime();
diff --git a/base/sys_info_chromeos.cc b/base/sys_info_chromeos.cc
index 29f83845dc..3794ed96c6 100644
--- a/base/sys_info_chromeos.cc
+++ b/base/sys_info_chromeos.cc
@@ -163,7 +163,7 @@ class ChromeOSVersionInfo {
bool is_running_on_chromeos_;
};
-static LazyInstance<ChromeOSVersionInfo>::Leaky
+static LazyInstance<ChromeOSVersionInfo>
g_chrome_os_version_info = LAZY_INSTANCE_INITIALIZER;
ChromeOSVersionInfo& GetChromeOSVersionInfo() {
@@ -200,16 +200,6 @@ std::string SysInfo::GetLsbReleaseBoard() {
}
// static
-std::string SysInfo::GetStrippedReleaseBoard() {
- std::string board = GetLsbReleaseBoard();
- const size_t index = board.find("-signed-");
- if (index != std::string::npos)
- board.resize(index);
-
- return base::ToLowerASCII(board);
-}
-
-// static
Time SysInfo::GetLsbReleaseTime() {
return GetChromeOSVersionInfo().lsb_release_time();
}
diff --git a/base/sys_info_mac.mm b/base/sys_info_mac.mm
index f8d668c8ff..102d99f3d0 100644
--- a/base/sys_info_mac.mm
+++ b/base/sys_info_mac.mm
@@ -47,12 +47,18 @@ void SysInfo::OperatingSystemVersionNumbers(int32_t* major_version,
*minor_version = version.minorVersion;
*bugfix_version = version.patchVersion;
} else {
+#else
+ // Android buildbots are too old and have trouble using the forward
+ // declarations for some reason. Conditionally-compile the above block
+ // only when building on a more modern version of OS X.
+ if (true) {
+#endif
// -[NSProcessInfo operatingSystemVersion] is documented available in 10.10.
// It's also available via a private API since 10.9.2. For the remaining
// cases in 10.9, rely on ::Gestalt(..). Since this code is only needed for
// 10.9.0 and 10.9.1 and uses the recommended replacement thereafter,
// suppress the warning for this fallback case.
- DCHECK(base::mac::IsOS10_9());
+ DCHECK(base::mac::IsOSMavericks());
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
Gestalt(gestaltSystemVersionMajor,
diff --git a/base/sys_info_posix.cc b/base/sys_info_posix.cc
index cbdfa3f7a9..5d1c450139 100644
--- a/base/sys_info_posix.cc
+++ b/base/sys_info_posix.cc
@@ -28,11 +28,6 @@
#include <sys/statvfs.h>
#endif
-#if defined(OS_LINUX)
-#include <linux/magic.h>
-#include <sys/vfs.h>
-#endif
-
namespace {
#if !defined(OS_OPENBSD)
@@ -78,23 +73,6 @@ base::LazyInstance<
base::internal::LazySysInfoValue<int64_t, AmountOfVirtualMemory>>::Leaky
g_lazy_virtual_memory = LAZY_INSTANCE_INITIALIZER;
-#if defined(OS_LINUX)
-bool IsStatsZeroIfUnlimited(const base::FilePath& path) {
- struct statfs stats;
-
- if (HANDLE_EINTR(statfs(path.value().c_str(), &stats)) != 0)
- return false;
-
- switch (static_cast<uint32_t>(stats.f_type)) {
- case TMPFS_MAGIC:
- case HUGETLBFS_MAGIC:
- case RAMFS_MAGIC:
- return true;
- }
- return false;
-}
-#endif
-
bool GetDiskSpaceInfo(const base::FilePath& path,
int64_t* available_bytes,
int64_t* total_bytes) {
@@ -102,25 +80,10 @@ bool GetDiskSpaceInfo(const base::FilePath& path,
if (HANDLE_EINTR(statvfs(path.value().c_str(), &stats)) != 0)
return false;
-#if defined(OS_LINUX)
- const bool zero_size_means_unlimited =
- stats.f_blocks == 0 && IsStatsZeroIfUnlimited(path);
-#else
- const bool zero_size_means_unlimited = false;
-#endif
-
- if (available_bytes) {
- *available_bytes =
- zero_size_means_unlimited
- ? std::numeric_limits<int64_t>::max()
- : static_cast<int64_t>(stats.f_bavail) * stats.f_frsize;
- }
-
- if (total_bytes) {
- *total_bytes = zero_size_means_unlimited
- ? std::numeric_limits<int64_t>::max()
- : static_cast<int64_t>(stats.f_blocks) * stats.f_frsize;
- }
+ if (available_bytes)
+ *available_bytes = static_cast<int64_t>(stats.f_bavail) * stats.f_frsize;
+ if (total_bytes)
+ *total_bytes = static_cast<int64_t>(stats.f_blocks) * stats.f_frsize;
return true;
}
diff --git a/base/sys_info_unittest.cc b/base/sys_info_unittest.cc
index c3b8507707..0231df6379 100644
--- a/base/sys_info_unittest.cc
+++ b/base/sys_info_unittest.cc
@@ -156,14 +156,4 @@ TEST_F(SysInfoTest, IsRunningOnChromeOS) {
EXPECT_TRUE(base::SysInfo::IsRunningOnChromeOS());
}
-TEST_F(SysInfoTest, GetStrippedReleaseBoard) {
- const char* kLsbRelease1 = "CHROMEOS_RELEASE_BOARD=Glimmer\n";
- base::SysInfo::SetChromeOSVersionInfoForTest(kLsbRelease1, base::Time());
- EXPECT_EQ("glimmer", base::SysInfo::GetStrippedReleaseBoard());
-
- const char* kLsbRelease2 = "CHROMEOS_RELEASE_BOARD=glimmer-signed-mp-v4keys";
- base::SysInfo::SetChromeOSVersionInfoForTest(kLsbRelease2, base::Time());
- EXPECT_EQ("glimmer", base::SysInfo::GetStrippedReleaseBoard());
-}
-
#endif // OS_CHROMEOS
diff --git a/base/task/cancelable_task_tracker.cc b/base/task/cancelable_task_tracker.cc
index 9999c18303..6f394100d5 100644
--- a/base/task/cancelable_task_tracker.cc
+++ b/base/task/cancelable_task_tracker.cc
@@ -8,14 +8,21 @@
#include <utility>
+#include "base/bind.h"
#include "base/callback_helpers.h"
+#include "base/compiler_specific.h"
#include "base/location.h"
#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
#include "base/synchronization/cancellation_flag.h"
#include "base/task_runner.h"
-#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/threading/thread_task_runner_handle.h"
-namespace base {
+using base::Bind;
+using base::CancellationFlag;
+using base::Closure;
+using base::hash_map;
+using base::TaskRunner;
namespace {
@@ -50,6 +57,8 @@ void RunOrPostToTaskRunner(TaskRunner* task_runner, const Closure& closure) {
} // namespace
+namespace base {
+
// static
const CancelableTaskTracker::TaskId CancelableTaskTracker::kBadTaskId = 0;
@@ -57,7 +66,7 @@ CancelableTaskTracker::CancelableTaskTracker()
: next_id_(1),weak_factory_(this) {}
CancelableTaskTracker::~CancelableTaskTracker() {
- DCHECK(sequence_checker_.CalledOnValidSequence());
+ DCHECK(thread_checker_.CalledOnValidThread());
TryCancelAll();
}
@@ -66,7 +75,7 @@ CancelableTaskTracker::TaskId CancelableTaskTracker::PostTask(
TaskRunner* task_runner,
const tracked_objects::Location& from_here,
const Closure& task) {
- DCHECK(sequence_checker_.CalledOnValidSequence());
+ DCHECK(thread_checker_.CalledOnValidThread());
return PostTaskAndReply(task_runner, from_here, task, Bind(&base::DoNothing));
}
@@ -74,12 +83,12 @@ CancelableTaskTracker::TaskId CancelableTaskTracker::PostTask(
CancelableTaskTracker::TaskId CancelableTaskTracker::PostTaskAndReply(
TaskRunner* task_runner,
const tracked_objects::Location& from_here,
- Closure task,
- Closure reply) {
- DCHECK(sequence_checker_.CalledOnValidSequence());
+ const Closure& task,
+ const Closure& reply) {
+ DCHECK(thread_checker_.CalledOnValidThread());
- // We need a SequencedTaskRunnerHandle to run |reply|.
- DCHECK(base::SequencedTaskRunnerHandle::IsSet());
+ // We need a MessageLoop to run reply.
+ DCHECK(base::ThreadTaskRunnerHandle::IsSet());
// Owned by reply callback below.
CancellationFlag* flag = new CancellationFlag();
@@ -87,12 +96,15 @@ CancelableTaskTracker::TaskId CancelableTaskTracker::PostTaskAndReply(
TaskId id = next_id_;
next_id_++; // int64_t is big enough that we ignore the potential overflow.
- Closure untrack_closure =
+ const Closure& untrack_closure =
Bind(&CancelableTaskTracker::Untrack, weak_factory_.GetWeakPtr(), id);
- bool success = task_runner->PostTaskAndReply(
- from_here, Bind(&RunIfNotCanceled, flag, std::move(task)),
- Bind(&RunIfNotCanceledThenUntrack, base::Owned(flag), std::move(reply),
- std::move(untrack_closure)));
+ bool success =
+ task_runner->PostTaskAndReply(from_here,
+ Bind(&RunIfNotCanceled, flag, task),
+ Bind(&RunIfNotCanceledThenUntrack,
+ base::Owned(flag),
+ reply,
+ untrack_closure));
if (!success)
return kBadTaskId;
@@ -103,8 +115,8 @@ CancelableTaskTracker::TaskId CancelableTaskTracker::PostTaskAndReply(
CancelableTaskTracker::TaskId CancelableTaskTracker::NewTrackedTaskId(
IsCanceledCallback* is_canceled_cb) {
- DCHECK(sequence_checker_.CalledOnValidSequence());
- DCHECK(base::SequencedTaskRunnerHandle::IsSet());
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(base::ThreadTaskRunnerHandle::IsSet());
TaskId id = next_id_;
next_id_++; // int64_t is big enough that we ignore the potential overflow.
@@ -117,11 +129,11 @@ CancelableTaskTracker::TaskId CancelableTaskTracker::NewTrackedTaskId(
Bind(&CancelableTaskTracker::Untrack, weak_factory_.GetWeakPtr(), id),
flag);
- // Will always run |untrack_and_delete_flag| on current sequence.
+ // Will always run |untrack_and_delete_flag| on current MessageLoop.
base::ScopedClosureRunner* untrack_and_delete_flag_runner =
new base::ScopedClosureRunner(
Bind(&RunOrPostToTaskRunner,
- RetainedRef(base::SequencedTaskRunnerHandle::Get()),
+ RetainedRef(base::ThreadTaskRunnerHandle::Get()),
untrack_and_delete_flag));
*is_canceled_cb =
@@ -132,7 +144,7 @@ CancelableTaskTracker::TaskId CancelableTaskTracker::NewTrackedTaskId(
}
void CancelableTaskTracker::TryCancel(TaskId id) {
- DCHECK(sequence_checker_.CalledOnValidSequence());
+ DCHECK(thread_checker_.CalledOnValidThread());
hash_map<TaskId, CancellationFlag*>::const_iterator it = task_flags_.find(id);
if (it == task_flags_.end()) {
@@ -148,7 +160,7 @@ void CancelableTaskTracker::TryCancel(TaskId id) {
}
void CancelableTaskTracker::TryCancelAll() {
- DCHECK(sequence_checker_.CalledOnValidSequence());
+ DCHECK(thread_checker_.CalledOnValidThread());
for (hash_map<TaskId, CancellationFlag*>::const_iterator it =
task_flags_.begin();
@@ -159,19 +171,19 @@ void CancelableTaskTracker::TryCancelAll() {
}
bool CancelableTaskTracker::HasTrackedTasks() const {
- DCHECK(sequence_checker_.CalledOnValidSequence());
+ DCHECK(thread_checker_.CalledOnValidThread());
return !task_flags_.empty();
}
void CancelableTaskTracker::Track(TaskId id, CancellationFlag* flag) {
- DCHECK(sequence_checker_.CalledOnValidSequence());
+ DCHECK(thread_checker_.CalledOnValidThread());
bool success = task_flags_.insert(std::make_pair(id, flag)).second;
DCHECK(success);
}
void CancelableTaskTracker::Untrack(TaskId id) {
- DCHECK(sequence_checker_.CalledOnValidSequence());
+ DCHECK(thread_checker_.CalledOnValidThread());
size_t num = task_flags_.erase(id);
DCHECK_EQ(1u, num);
}
diff --git a/base/task/cancelable_task_tracker.h b/base/task/cancelable_task_tracker.h
index 4f64a24060..86b5a45845 100644
--- a/base/task/cancelable_task_tracker.h
+++ b/base/task/cancelable_task_tracker.h
@@ -15,38 +15,36 @@
//
// CancelableCallback (base/cancelable_callback.h) and WeakPtr binding are
// preferred solutions for canceling a task. However, they don't support
-// cancelation from another sequence. This is sometimes a performance critical
+// cancelation from another thread. This is sometimes a performance critical
// requirement. E.g. We need to cancel database lookup task on DB thread when
// user changes inputed text. If it is performance critical to do a best effort
-// cancelation of a task, then CancelableTaskTracker is appropriate, otherwise
-// use one of the other mechanisms.
+// cancelation of a task, then CancelableTaskTracker is appropriate,
+// otherwise use one of the other mechanisms.
//
// THREAD-SAFETY:
//
-// 1. A CancelableTaskTracker object must be created, used, and destroyed on a
-// single sequence.
+// 1. CancelableTaskTracker objects are not thread safe. They must
+// be created, used, and destroyed on the originating thread that posts the
+// task. It's safe to destroy a CancelableTaskTracker while there
+// are outstanding tasks. This is commonly used to cancel all outstanding
+// tasks.
//
-// 2. It's safe to destroy a CancelableTaskTracker while there are outstanding
-// tasks. This is commonly used to cancel all outstanding tasks.
+// 2. Both task and reply are deleted on the originating thread.
//
-// 3. Both task and reply are deleted on the originating sequence.
-//
-// 4. IsCanceledCallback can be run or deleted on any sequence.
+// 3. IsCanceledCallback is thread safe and can be run or deleted on any
+// thread.
#ifndef BASE_TASK_CANCELABLE_TASK_TRACKER_H_
#define BASE_TASK_CANCELABLE_TASK_TRACKER_H_
#include <stdint.h>
-#include <utility>
-
#include "base/base_export.h"
-#include "base/bind.h"
#include "base/callback.h"
#include "base/containers/hash_tables.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
-#include "base/post_task_and_reply_with_result_internal.h"
-#include "base/sequence_checker.h"
+#include "base/task_runner_util.h"
+#include "base/threading/thread_checker.h"
namespace tracked_objects {
class Location;
@@ -76,21 +74,25 @@ class BASE_EXPORT CancelableTaskTracker {
TaskId PostTaskAndReply(base::TaskRunner* task_runner,
const tracked_objects::Location& from_here,
- base::Closure task,
- base::Closure reply);
+ const base::Closure& task,
+ const base::Closure& reply);
template <typename TaskReturnType, typename ReplyArgType>
- TaskId PostTaskAndReplyWithResult(base::TaskRunner* task_runner,
- const tracked_objects::Location& from_here,
- base::Callback<TaskReturnType()> task,
- base::Callback<void(ReplyArgType)> reply) {
+ TaskId PostTaskAndReplyWithResult(
+ base::TaskRunner* task_runner,
+ const tracked_objects::Location& from_here,
+ const base::Callback<TaskReturnType(void)>& task,
+ const base::Callback<void(ReplyArgType)>& reply) {
TaskReturnType* result = new TaskReturnType();
return PostTaskAndReply(
- task_runner, from_here,
+ task_runner,
+ from_here,
base::Bind(&base::internal::ReturnAsParamAdapter<TaskReturnType>,
- std::move(task), base::Unretained(result)),
+ task,
+ base::Unretained(result)),
base::Bind(&base::internal::ReplyAdapter<TaskReturnType, ReplyArgType>,
- std::move(reply), base::Owned(result)));
+ reply,
+ base::Owned(result)));
}
// Creates a tracked TaskId and an associated IsCanceledCallback. Client can
@@ -128,7 +130,7 @@ class BASE_EXPORT CancelableTaskTracker {
base::hash_map<TaskId, base::CancellationFlag*> task_flags_;
TaskId next_id_;
- SequenceChecker sequence_checker_;
+ base::ThreadChecker thread_checker_;
base::WeakPtrFactory<CancelableTaskTracker> weak_factory_;
diff --git a/base/task/cancelable_task_tracker_unittest.cc b/base/task/cancelable_task_tracker_unittest.cc
index fd480f3687..ff9e40b855 100644
--- a/base/task/cancelable_task_tracker_unittest.cc
+++ b/base/task/cancelable_task_tracker_unittest.cc
@@ -15,7 +15,6 @@
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
-#include "base/test/gtest_util.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -113,7 +112,7 @@ TEST_F(CancelableTaskTrackerTest, CancelPostedTask) {
test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE));
EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
- EXPECT_EQ(1U, test_task_runner->NumPendingTasks());
+ EXPECT_EQ(1U, test_task_runner->GetPendingTasks().size());
task_tracker_.TryCancel(task_id);
@@ -345,11 +344,23 @@ class CancelableTaskTrackerDeathTest : public CancelableTaskTrackerTest {
}
};
+// Duplicated from base/threading/thread_checker.h so that we can be
+// good citizens there and undef the macro.
+#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
+#define ENABLE_THREAD_CHECKER 1
+#else
+#define ENABLE_THREAD_CHECKER 0
+#endif
+
// Runs |fn| with |task_tracker|, expecting it to crash in debug mode.
void MaybeRunDeadlyTaskTrackerMemberFunction(
CancelableTaskTracker* task_tracker,
const Callback<void(CancelableTaskTracker*)>& fn) {
- EXPECT_DCHECK_DEATH(fn.Run(task_tracker));
+// CancelableTask uses DCHECKs with its ThreadChecker (itself only
+// enabled in debug mode).
+#if ENABLE_THREAD_CHECKER
+ EXPECT_DEATH_IF_SUPPORTED(fn.Run(task_tracker), "");
+#endif
}
void PostDoNothingTask(CancelableTaskTracker* task_tracker) {
diff --git a/base/task_runner.cc b/base/task_runner.cc
index 35c0a23274..262e1f8b09 100644
--- a/base/task_runner.cc
+++ b/base/task_runner.cc
@@ -4,8 +4,6 @@
#include "base/task_runner.h"
-#include <utility>
-
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/threading/post_task_and_reply_impl.h"
@@ -47,11 +45,12 @@ bool TaskRunner::PostTask(const tracked_objects::Location& from_here,
return PostDelayedTask(from_here, task, base::TimeDelta());
}
-bool TaskRunner::PostTaskAndReply(const tracked_objects::Location& from_here,
- Closure task,
- Closure reply) {
+bool TaskRunner::PostTaskAndReply(
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ const Closure& reply) {
return PostTaskAndReplyTaskRunner(this).PostTaskAndReply(
- from_here, std::move(task), std::move(reply));
+ from_here, task, reply);
}
TaskRunner::TaskRunner() {}
diff --git a/base/task_runner.h b/base/task_runner.h
index be3039d372..9593835eeb 100644
--- a/base/task_runner.h
+++ b/base/task_runner.h
@@ -8,7 +8,7 @@
#include <stddef.h>
#include "base/base_export.h"
-#include "base/callback.h"
+#include "base/callback_forward.h"
#include "base/location.h"
#include "base/memory/ref_counted.h"
#include "base/time/time.h"
@@ -123,8 +123,8 @@ class BASE_EXPORT TaskRunner
// and the reply will cancel itself safely because it is bound to a
// WeakPtr<>.
bool PostTaskAndReply(const tracked_objects::Location& from_here,
- Closure task,
- Closure reply);
+ const Closure& task,
+ const Closure& reply);
protected:
friend struct TaskRunnerTraits;
diff --git a/base/task_runner_util.h b/base/task_runner_util.h
index 7fda07624d..ba8e120c6f 100644
--- a/base/task_runner_util.h
+++ b/base/task_runner_util.h
@@ -5,17 +5,37 @@
#ifndef BASE_TASK_RUNNER_UTIL_H_
#define BASE_TASK_RUNNER_UTIL_H_
-#include <utility>
-
#include "base/bind.h"
#include "base/bind_helpers.h"
-#include "base/callback.h"
#include "base/logging.h"
-#include "base/post_task_and_reply_with_result_internal.h"
#include "base/task_runner.h"
namespace base {
+namespace internal {
+
+// Adapts a function that produces a result via a return value to
+// one that returns via an output parameter.
+template <typename ReturnType>
+void ReturnAsParamAdapter(const Callback<ReturnType(void)>& func,
+ ReturnType* result) {
+ *result = func.Run();
+}
+
+// Adapts a T* result to a callblack that expects a T.
+template <typename TaskReturnType, typename ReplyArgType>
+void ReplyAdapter(const Callback<void(ReplyArgType)>& callback,
+ TaskReturnType* result) {
+ // TODO(ajwong): Remove this conditional and add a DCHECK to enforce that
+ // |reply| must be non-null in PostTaskAndReplyWithResult() below after
+ // current code that relies on this API softness has been removed.
+ // http://crbug.com/162712
+ if (!callback.is_null())
+ callback.Run(std::move(*result));
+}
+
+} // namespace internal
+
// When you have these methods
//
// R DoWorkAndReturn();
@@ -31,18 +51,18 @@ namespace base {
// Bind(&DoWorkAndReturn),
// Bind(&Callback));
template <typename TaskReturnType, typename ReplyArgType>
-bool PostTaskAndReplyWithResult(TaskRunner* task_runner,
- const tracked_objects::Location& from_here,
- Callback<TaskReturnType()> task,
- Callback<void(ReplyArgType)> reply) {
- DCHECK(task);
- DCHECK(reply);
+bool PostTaskAndReplyWithResult(
+ TaskRunner* task_runner,
+ const tracked_objects::Location& from_here,
+ const Callback<TaskReturnType(void)>& task,
+ const Callback<void(ReplyArgType)>& reply) {
TaskReturnType* result = new TaskReturnType();
return task_runner->PostTaskAndReply(
- from_here, base::Bind(&internal::ReturnAsParamAdapter<TaskReturnType>,
- std::move(task), result),
- base::Bind(&internal::ReplyAdapter<TaskReturnType, ReplyArgType>,
- std::move(reply), base::Owned(result)));
+ from_here,
+ base::Bind(&internal::ReturnAsParamAdapter<TaskReturnType>, task,
+ result),
+ base::Bind(&internal::ReplyAdapter<TaskReturnType, ReplyArgType>, reply,
+ base::Owned(result)));
}
} // namespace base
diff --git a/base/task_scheduler/scheduler_lock_impl.cc b/base/task_scheduler/scheduler_lock_impl.cc
index d60f25939b..7480e18da1 100644
--- a/base/task_scheduler/scheduler_lock_impl.cc
+++ b/base/task_scheduler/scheduler_lock_impl.cc
@@ -67,30 +67,19 @@ class SafeAcquisitionTracker {
// Otherwise, make sure that the previous lock acquired is an allowed
// predecessor.
AutoLock auto_lock(allowed_predecessor_map_lock_);
- // Using at() is exception-safe here as |lock| was registered already.
const SchedulerLockImpl* allowed_predecessor =
allowed_predecessor_map_.at(lock);
DCHECK_EQ(acquired_locks->back(), allowed_predecessor);
}
- // Asserts that |lock|'s registered predecessor is safe. Because
- // SchedulerLocks are registered at construction time and any predecessor
- // specified on a SchedulerLock must already exist, the first registered
- // SchedulerLock in a potential chain must have a null predecessor and is thus
- // cycle-free. Any subsequent SchedulerLock with a predecessor must come from
- // the set of registered SchedulerLocks. Since the registered SchedulerLocks
- // only contain cycle-free SchedulerLocks, this subsequent SchedulerLock is
- // itself cycle-free and may be safely added to the registered SchedulerLock
- // set.
void AssertSafePredecessor(const SchedulerLockImpl* lock) const {
allowed_predecessor_map_lock_.AssertAcquired();
- // Using at() is exception-safe here as |lock| was registered already.
- const SchedulerLockImpl* predecessor = allowed_predecessor_map_.at(lock);
- if (predecessor) {
- DCHECK(allowed_predecessor_map_.find(predecessor) !=
- allowed_predecessor_map_.end())
- << "SchedulerLock was registered before its predecessor. "
- << "Potential cycle detected";
+ for (const SchedulerLockImpl* predecessor =
+ allowed_predecessor_map_.at(lock);
+ predecessor != nullptr;
+ predecessor = allowed_predecessor_map_.at(predecessor)) {
+ DCHECK_NE(predecessor, lock) <<
+ "Scheduler lock predecessor cycle detected.";
}
}
diff --git a/base/task_scheduler/scheduler_lock_unittest.cc b/base/task_scheduler/scheduler_lock_unittest.cc
index 55182479aa..daa50257f1 100644
--- a/base/task_scheduler/scheduler_lock_unittest.cc
+++ b/base/task_scheduler/scheduler_lock_unittest.cc
@@ -10,7 +10,7 @@
#include "base/macros.h"
#include "base/rand_util.h"
#include "base/synchronization/waitable_event.h"
-#include "base/test/gtest_util.h"
+#include "base/task_scheduler/test_utils.h"
#include "base/threading/platform_thread.h"
#include "base/threading/simple_thread.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -131,7 +131,7 @@ TEST(TaskSchedulerLock, AcquirePredecessorWrongOrder) {
EXPECT_DCHECK_DEATH({
lock.Acquire();
predecessor.Acquire();
- });
+ }, "");
}
TEST(TaskSchedulerLock, AcquireNonPredecessor) {
@@ -140,7 +140,7 @@ TEST(TaskSchedulerLock, AcquireNonPredecessor) {
EXPECT_DCHECK_DEATH({
lock1.Acquire();
lock2.Acquire();
- });
+ }, "");
}
TEST(TaskSchedulerLock, AcquireMultipleLocksInOrder) {
@@ -172,7 +172,7 @@ TEST(TaskSchedulerLock, AcquireMultipleLocksNoTransitivity) {
EXPECT_DCHECK_DEATH({
lock1.Acquire();
lock3.Acquire();
- });
+ }, "");
}
TEST(TaskSchedulerLock, AcquireLocksDifferentThreadsSafely) {
@@ -258,7 +258,7 @@ TEST(TaskSchedulerLock, SelfReferentialLock) {
SchedulerLock lock;
};
- EXPECT_DCHECK_DEATH({ SelfReferentialLock lock; });
+ EXPECT_DCHECK_DEATH({ SelfReferentialLock lock; }, "");
}
TEST(TaskSchedulerLock, PredecessorCycle) {
@@ -269,7 +269,7 @@ TEST(TaskSchedulerLock, PredecessorCycle) {
SchedulerLock lock2;
};
- EXPECT_DCHECK_DEATH({ LockCycle cycle; });
+ EXPECT_DCHECK_DEATH({ LockCycle cycle; }, "");
}
TEST(TaskSchedulerLock, PredecessorLongerCycle) {
@@ -288,7 +288,7 @@ TEST(TaskSchedulerLock, PredecessorLongerCycle) {
SchedulerLock lock5;
};
- EXPECT_DCHECK_DEATH({ LockCycle cycle; });
+ EXPECT_DCHECK_DEATH({ LockCycle cycle; }, "");
}
} // namespace
diff --git a/base/task_scheduler/scoped_set_task_priority_for_current_thread.cc b/base/task_scheduler/scoped_set_task_priority_for_current_thread.cc
deleted file mode 100644
index a163863d0f..0000000000
--- a/base/task_scheduler/scoped_set_task_priority_for_current_thread.cc
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2016 The Chromium 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_scheduler/scoped_set_task_priority_for_current_thread.h"
-
-#include "base/lazy_instance.h"
-#include "base/logging.h"
-#include "base/threading/thread_local.h"
-
-namespace base {
-namespace internal {
-
-namespace {
-
-LazyInstance<ThreadLocalPointer<const TaskPriority>>::Leaky
- tls_task_priority_for_current_thread = LAZY_INSTANCE_INITIALIZER;
-
-} // namespace
-
-ScopedSetTaskPriorityForCurrentThread::ScopedSetTaskPriorityForCurrentThread(
- TaskPriority priority)
- : priority_(priority) {
- DCHECK(!tls_task_priority_for_current_thread.Get().Get());
- tls_task_priority_for_current_thread.Get().Set(&priority_);
-}
-
-ScopedSetTaskPriorityForCurrentThread::
- ~ScopedSetTaskPriorityForCurrentThread() {
- DCHECK_EQ(&priority_, tls_task_priority_for_current_thread.Get().Get());
- tls_task_priority_for_current_thread.Get().Set(nullptr);
-}
-
-TaskPriority GetTaskPriorityForCurrentThread() {
- const TaskPriority* priority =
- tls_task_priority_for_current_thread.Get().Get();
- return priority ? *priority : TaskPriority::USER_VISIBLE;
-}
-
-} // namespace internal
-} // namespace base
diff --git a/base/task_scheduler/scoped_set_task_priority_for_current_thread.h b/base/task_scheduler/scoped_set_task_priority_for_current_thread.h
deleted file mode 100644
index 4508911d9c..0000000000
--- a/base/task_scheduler/scoped_set_task_priority_for_current_thread.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2016 The Chromium 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_SCHEDULER_SCOPED_SET_TASK_PRIORITY_FOR_CURRENT_THREAD_H_
-#define BASE_TASK_SCHEDULER_SCOPED_SET_TASK_PRIORITY_FOR_CURRENT_THREAD_H_
-
-#include "base/base_export.h"
-#include "base/macros.h"
-#include "base/task_scheduler/task_traits.h"
-
-namespace base {
-namespace internal {
-
-class BASE_EXPORT ScopedSetTaskPriorityForCurrentThread {
- public:
- // Within the scope of this object, GetTaskPriorityForCurrentThread() will
- // return |priority|.
- ScopedSetTaskPriorityForCurrentThread(TaskPriority priority);
- ~ScopedSetTaskPriorityForCurrentThread();
-
- private:
- const TaskPriority priority_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedSetTaskPriorityForCurrentThread);
-};
-
-// Returns the priority of the TaskScheduler task running on the current thread,
-// or TaskPriority::USER_VISIBLE if no TaskScheduler task is running on the
-// current thread.
-BASE_EXPORT TaskPriority GetTaskPriorityForCurrentThread();
-
-} // namespace internal
-} // namespace base
-
-#endif // BASE_TASK_SCHEDULER_SCOPED_SET_TASK_PRIORITY_FOR_CURRENT_THREAD_H_
diff --git a/base/task_scheduler/scoped_set_task_priority_for_current_thread_unittest.cc b/base/task_scheduler/scoped_set_task_priority_for_current_thread_unittest.cc
deleted file mode 100644
index c497af6770..0000000000
--- a/base/task_scheduler/scoped_set_task_priority_for_current_thread_unittest.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2016 The Chromium 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_scheduler/scoped_set_task_priority_for_current_thread.h"
-
-#include "base/task_scheduler/task_traits.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace base {
-namespace internal {
-
-TEST(TaskSchedulerScopedSetTaskPriorityForCurrentThreadTest,
- ScopedSetTaskPriorityForCurrentThread) {
- EXPECT_EQ(TaskPriority::USER_VISIBLE, GetTaskPriorityForCurrentThread());
- {
- ScopedSetTaskPriorityForCurrentThread
- scoped_set_task_priority_for_current_thread(
- TaskPriority::USER_BLOCKING);
- EXPECT_EQ(TaskPriority::USER_BLOCKING, GetTaskPriorityForCurrentThread());
- }
- EXPECT_EQ(TaskPriority::USER_VISIBLE, GetTaskPriorityForCurrentThread());
-}
-
-} // namespace internal
-} // namespace base
diff --git a/base/task_scheduler/sequence.cc b/base/task_scheduler/sequence.cc
index 601b5402d0..4ecb60568c 100644
--- a/base/task_scheduler/sequence.cc
+++ b/base/task_scheduler/sequence.cc
@@ -26,30 +26,24 @@ bool Sequence::PushTask(std::unique_ptr<Task> task) {
return queue_.size() == 1;
}
-std::unique_ptr<Task> Sequence::TakeTask() {
+const Task* Sequence::PeekTask() const {
AutoSchedulerLock auto_lock(lock_);
- DCHECK(!queue_.empty());
- DCHECK(queue_.front());
- const int priority_index =
- static_cast<int>(queue_.front()->traits.priority());
- DCHECK_GT(num_tasks_per_priority_[priority_index], 0U);
- --num_tasks_per_priority_[priority_index];
+ if (queue_.empty())
+ return nullptr;
- return std::move(queue_.front());
+ return queue_.front().get();
}
-TaskTraits Sequence::PeekTaskTraits() const {
+bool Sequence::PopTask() {
AutoSchedulerLock auto_lock(lock_);
DCHECK(!queue_.empty());
- DCHECK(queue_.front());
- return queue_.front()->traits;
-}
-bool Sequence::Pop() {
- AutoSchedulerLock auto_lock(lock_);
- DCHECK(!queue_.empty());
- DCHECK(!queue_.front());
+ const int priority_index =
+ static_cast<int>(queue_.front()->traits.priority());
+ DCHECK_GT(num_tasks_per_priority_[priority_index], 0U);
+ --num_tasks_per_priority_[priority_index];
+
queue_.pop();
return queue_.empty();
}
diff --git a/base/task_scheduler/sequence.h b/base/task_scheduler/sequence.h
index ed1d0ac401..3fa037fa35 100644
--- a/base/task_scheduler/sequence.h
+++ b/base/task_scheduler/sequence.h
@@ -13,7 +13,6 @@
#include "base/base_export.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
-#include "base/sequence_token.h"
#include "base/task_scheduler/scheduler_lock.h"
#include "base/task_scheduler/sequence_sort_key.h"
#include "base/task_scheduler/task.h"
@@ -22,10 +21,7 @@
namespace base {
namespace internal {
-// A Sequence holds slots each containing up to a single Task that must be
-// executed in posting order.
-//
-// In comments below, an "empty Sequence" is a Sequence with no slot.
+// A sequence holds tasks that must be executed in posting order.
//
// Note: there is a known refcounted-ownership cycle in the Scheduler
// architecture: Sequence -> Task -> TaskRunner -> Sequence -> ...
@@ -44,38 +40,26 @@ class BASE_EXPORT Sequence : public RefCountedThreadSafe<Sequence> {
public:
Sequence();
- // Adds |task| in a new slot at the end of the Sequence. Returns true if the
- // Sequence was empty before this operation.
+ // Adds |task| at the end of the sequence's queue. Returns true if the
+ // sequence was empty before this operation.
bool PushTask(std::unique_ptr<Task> task);
- // Transfers ownership of the Task in the front slot of the Sequence to the
- // caller. The front slot of the Sequence will be nullptr and remain until
- // Pop(). Cannot be called on an empty Sequence or a Sequence whose front slot
- // is already nullptr.
- std::unique_ptr<Task> TakeTask();
-
- // Returns the TaskTraits of the Task in front of the Sequence. Cannot be
- // called on an empty Sequence or on a Sequence whose front slot is empty.
- TaskTraits PeekTaskTraits() const;
+ // Returns the task in front of the sequence's queue, if any.
+ const Task* PeekTask() const;
- // Removes the front slot of the Sequence. The front slot must have been
- // emptied by TakeTask() before this is called. Cannot be called on an empty
- // Sequence. Returns true if the Sequence is empty after this operation.
- bool Pop();
+ // Removes the task in front of the sequence's queue. Returns true if the
+ // sequence is empty after this operation. Cannot be called on an empty
+ // sequence.
+ bool PopTask();
- // Returns a SequenceSortKey representing the priority of the Sequence. Cannot
- // be called on an empty Sequence.
+ // Returns a SequenceSortKey representing the priority of the sequence. Cannot
+ // be called on an empty sequence.
SequenceSortKey GetSortKey() const;
- // Returns a token that uniquely identifies this Sequence.
- const SequenceToken& token() const { return token_; }
-
private:
friend class RefCountedThreadSafe<Sequence>;
~Sequence();
- const SequenceToken token_ = SequenceToken::Create();
-
// Synchronizes access to all members.
mutable SchedulerLock lock_;
diff --git a/base/task_scheduler/sequence_unittest.cc b/base/task_scheduler/sequence_unittest.cc
index c45d8a87d0..6a15299e1e 100644
--- a/base/task_scheduler/sequence_unittest.cc
+++ b/base/task_scheduler/sequence_unittest.cc
@@ -4,12 +4,7 @@
#include "base/task_scheduler/sequence.h"
-#include <utility>
-
-#include "base/bind.h"
#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/test/gtest_util.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -18,7 +13,6 @@ namespace internal {
namespace {
-
class TaskSchedulerSequenceTest : public testing::Test {
public:
TaskSchedulerSequenceTest()
@@ -62,7 +56,7 @@ class TaskSchedulerSequenceTest : public testing::Test {
std::unique_ptr<Task> task_e_owned_;
// Raw pointers to those same tasks for verification. This is needed because
- // the unique_ptrs above no longer point to the tasks once they have been
+ // the scoped_ptrs above no longer point to the tasks once they have been
// moved into a Sequence.
const Task* task_a_;
const Task* task_b_;
@@ -76,54 +70,54 @@ class TaskSchedulerSequenceTest : public testing::Test {
} // namespace
-TEST_F(TaskSchedulerSequenceTest, PushTakeRemove) {
+TEST_F(TaskSchedulerSequenceTest, PushPopPeek) {
scoped_refptr<Sequence> sequence(new Sequence);
// Push task A in the sequence. Its sequenced time should be updated and it
// should be in front of the sequence.
EXPECT_TRUE(sequence->PushTask(std::move(task_a_owned_)));
EXPECT_FALSE(task_a_->sequenced_time.is_null());
- EXPECT_EQ(task_a_->traits.priority(), sequence->PeekTaskTraits().priority());
+ EXPECT_EQ(task_a_, sequence->PeekTask());
// Push task B, C and D in the sequence. Their sequenced time should be
// updated and task A should always remain in front of the sequence.
EXPECT_FALSE(sequence->PushTask(std::move(task_b_owned_)));
EXPECT_FALSE(task_b_->sequenced_time.is_null());
- EXPECT_EQ(task_a_->traits.priority(), sequence->PeekTaskTraits().priority());
+ EXPECT_EQ(task_a_, sequence->PeekTask());
EXPECT_FALSE(sequence->PushTask(std::move(task_c_owned_)));
EXPECT_FALSE(task_c_->sequenced_time.is_null());
- EXPECT_EQ(task_a_->traits.priority(), sequence->PeekTaskTraits().priority());
+ EXPECT_EQ(task_a_, sequence->PeekTask());
EXPECT_FALSE(sequence->PushTask(std::move(task_d_owned_)));
EXPECT_FALSE(task_d_->sequenced_time.is_null());
- EXPECT_EQ(task_a_->traits.priority(), sequence->PeekTaskTraits().priority());
+ EXPECT_EQ(task_a_, sequence->PeekTask());
- // Get the task in front of the sequence. It should be task A.
- EXPECT_EQ(task_a_, sequence->TakeTask().get());
+ // Pop task A. Task B should now be in front.
+ EXPECT_FALSE(sequence->PopTask());
+ EXPECT_EQ(task_b_, sequence->PeekTask());
- // Remove the empty slot. Task B should now be in front.
- EXPECT_FALSE(sequence->Pop());
- EXPECT_EQ(task_b_, sequence->TakeTask().get());
+ // Pop task B. Task C should now be in front.
+ EXPECT_FALSE(sequence->PopTask());
+ EXPECT_EQ(task_c_, sequence->PeekTask());
- // Remove the empty slot. Task C should now be in front.
- EXPECT_FALSE(sequence->Pop());
- EXPECT_EQ(task_c_, sequence->TakeTask().get());
+ // Pop task C. Task D should now be in front.
+ EXPECT_FALSE(sequence->PopTask());
+ EXPECT_EQ(task_d_, sequence->PeekTask());
- // Remove the empty slot. Task D should now be in front.
- EXPECT_FALSE(sequence->Pop());
- EXPECT_EQ(task_d_, sequence->TakeTask().get());
-
- // Push task E in the sequence. Its sequenced time should be updated.
+ // Push task E in the sequence. Its sequenced time should be updated and
+ // task D should remain in front.
EXPECT_FALSE(sequence->PushTask(std::move(task_e_owned_)));
EXPECT_FALSE(task_e_->sequenced_time.is_null());
+ EXPECT_EQ(task_d_, sequence->PeekTask());
- // Remove the empty slot. Task E should now be in front.
- EXPECT_FALSE(sequence->Pop());
- EXPECT_EQ(task_e_, sequence->TakeTask().get());
+ // Pop task D. Task E should now be in front.
+ EXPECT_FALSE(sequence->PopTask());
+ EXPECT_EQ(task_e_, sequence->PeekTask());
- // Remove the empty slot. The sequence should now be empty.
- EXPECT_TRUE(sequence->Pop());
+ // Pop task E. The sequence should now be empty.
+ EXPECT_TRUE(sequence->PopTask());
+ EXPECT_EQ(nullptr, sequence->PeekTask());
}
TEST_F(TaskSchedulerSequenceTest, GetSortKey) {
@@ -158,24 +152,21 @@ TEST_F(TaskSchedulerSequenceTest, GetSortKey) {
// Pop task A. The highest priority is still USER_BLOCKING. The task in front
// of the sequence is now task B.
- sequence->TakeTask();
- sequence->Pop();
+ sequence->PopTask();
EXPECT_EQ(
SequenceSortKey(TaskPriority::USER_BLOCKING, task_b_->sequenced_time),
sequence->GetSortKey());
// Pop task B. The highest priority is still USER_BLOCKING. The task in front
// of the sequence is now task C.
- sequence->TakeTask();
- sequence->Pop();
+ sequence->PopTask();
EXPECT_EQ(
SequenceSortKey(TaskPriority::USER_BLOCKING, task_c_->sequenced_time),
sequence->GetSortKey());
// Pop task C. The highest priority is still USER_BLOCKING. The task in front
// of the sequence is now task D.
- sequence->TakeTask();
- sequence->Pop();
+ sequence->PopTask();
EXPECT_EQ(
SequenceSortKey(TaskPriority::USER_BLOCKING, task_d_->sequenced_time),
sequence->GetSortKey());
@@ -189,38 +180,10 @@ TEST_F(TaskSchedulerSequenceTest, GetSortKey) {
// Pop task D. The highest priority is now from task E (BACKGROUND). The
// task in front of the sequence is now task E.
- sequence->TakeTask();
- sequence->Pop();
+ sequence->PopTask();
EXPECT_EQ(SequenceSortKey(TaskPriority::BACKGROUND, task_e_->sequenced_time),
sequence->GetSortKey());
}
-// Verify that a DCHECK fires if Pop() is called on a sequence whose front slot
-// isn't empty.
-TEST_F(TaskSchedulerSequenceTest, PopNonEmptyFrontSlot) {
- scoped_refptr<Sequence> sequence(new Sequence);
- sequence->PushTask(
- MakeUnique<Task>(FROM_HERE, Bind(&DoNothing), TaskTraits(), TimeDelta()));
-
- EXPECT_DCHECK_DEATH({ sequence->Pop(); });
-}
-
-// Verify that a DCHECK fires if TakeTask() is called on a sequence whose front
-// slot is empty.
-TEST_F(TaskSchedulerSequenceTest, TakeEmptyFrontSlot) {
- scoped_refptr<Sequence> sequence(new Sequence);
- sequence->PushTask(
- MakeUnique<Task>(FROM_HERE, Bind(&DoNothing), TaskTraits(), TimeDelta()));
-
- EXPECT_TRUE(sequence->TakeTask());
- EXPECT_DCHECK_DEATH({ sequence->TakeTask(); });
-}
-
-// Verify that a DCHECK fires if TakeTask() is called on an empty sequence.
-TEST_F(TaskSchedulerSequenceTest, TakeEmptySequence) {
- scoped_refptr<Sequence> sequence(new Sequence);
- EXPECT_DCHECK_DEATH({ sequence->TakeTask(); });
-}
-
} // namespace internal
} // namespace base
diff --git a/base/task_scheduler/task.cc b/base/task_scheduler/task.cc
index 3780c16dcb..8a589a2021 100644
--- a/base/task_scheduler/task.cc
+++ b/base/task_scheduler/task.cc
@@ -10,20 +10,12 @@ namespace internal {
Task::Task(const tracked_objects::Location& posted_from,
const Closure& task,
const TaskTraits& traits,
- TimeDelta delay)
+ const TimeDelta& delay)
: PendingTask(posted_from,
task,
delay.is_zero() ? TimeTicks() : TimeTicks::Now() + delay,
false), // Not nestable.
- // Prevent a delayed BLOCK_SHUTDOWN task from blocking shutdown before
- // being scheduled by changing its shutdown behavior to SKIP_ON_SHUTDOWN.
- traits(!delay.is_zero() &&
- traits.shutdown_behavior() ==
- TaskShutdownBehavior::BLOCK_SHUTDOWN
- ? TaskTraits(traits).WithShutdownBehavior(
- TaskShutdownBehavior::SKIP_ON_SHUTDOWN)
- : traits),
- delay(delay) {}
+ traits(traits) {}
Task::~Task() = default;
diff --git a/base/task_scheduler/task.h b/base/task_scheduler/task.h
index c5b9bdb53b..2b53c690fd 100644
--- a/base/task_scheduler/task.h
+++ b/base/task_scheduler/task.h
@@ -23,22 +23,17 @@ namespace internal {
// profiling inherited from PendingTask.
struct BASE_EXPORT Task : public PendingTask {
// |posted_from| is the site the task was posted from. |task| is the closure
- // to run. |traits_in| is metadata about the task. |delay| is a delay that
- // must expire before the Task runs. If |delay| is non-zero and the shutdown
- // behavior in |traits| is BLOCK_SHUTDOWN, the shutdown behavior is
- // automatically adjusted to SKIP_ON_SHUTDOWN.
+ // to run. |traits| is metadata about the task. |delay| is a delay that must
+ // expire before the Task runs.
Task(const tracked_objects::Location& posted_from,
const Closure& task,
const TaskTraits& traits,
- TimeDelta delay);
+ const TimeDelta& delay);
~Task();
// The TaskTraits of this task.
const TaskTraits traits;
- // The delay that must expire before the task runs.
- const TimeDelta delay;
-
// The time at which the task was inserted in its sequence. For an undelayed
// task, this happens at post time. For a delayed task, this happens some
// time after the task's delay has expired. If the task hasn't been inserted
diff --git a/base/task_scheduler/task_traits.cc b/base/task_scheduler/task_traits.cc
index 6acf3244f5..dd55535852 100644
--- a/base/task_scheduler/task_traits.cc
+++ b/base/task_scheduler/task_traits.cc
@@ -8,29 +8,20 @@
#include <ostream>
-#include "base/logging.h"
-#include "base/task_scheduler/scoped_set_task_priority_for_current_thread.h"
-
namespace base {
// Do not rely on defaults hard-coded below beyond the guarantees described in
// the header; anything else is subject to change. Tasks should explicitly
// request defaults if the behavior is critical to the task.
TaskTraits::TaskTraits()
- : may_block_(false),
- with_base_sync_primitives_(false),
- priority_(internal::GetTaskPriorityForCurrentThread()),
+ : with_file_io_(false),
+ priority_(TaskPriority::BACKGROUND),
shutdown_behavior_(TaskShutdownBehavior::SKIP_ON_SHUTDOWN) {}
TaskTraits::~TaskTraits() = default;
-TaskTraits& TaskTraits::MayBlock() {
- may_block_ = true;
- return *this;
-}
-
-TaskTraits& TaskTraits::WithBaseSyncPrimitives() {
- with_base_sync_primitives_ = true;
+TaskTraits& TaskTraits::WithFileIO() {
+ with_file_io_ = true;
return *this;
}
@@ -45,41 +36,34 @@ TaskTraits& TaskTraits::WithShutdownBehavior(
return *this;
}
-const char* TaskPriorityToString(TaskPriority task_priority) {
+std::ostream& operator<<(std::ostream& os, const TaskPriority& task_priority) {
switch (task_priority) {
case TaskPriority::BACKGROUND:
- return "BACKGROUND";
+ os << "BACKGROUND";
+ break;
case TaskPriority::USER_VISIBLE:
- return "USER_VISIBLE";
+ os << "USER_VISIBLE";
+ break;
case TaskPriority::USER_BLOCKING:
- return "USER_BLOCKING";
+ os << "USER_BLOCKING";
+ break;
}
- NOTREACHED();
- return "";
+ return os;
}
-const char* TaskShutdownBehaviorToString(
- TaskShutdownBehavior shutdown_behavior) {
+std::ostream& operator<<(std::ostream& os,
+ const TaskShutdownBehavior& shutdown_behavior) {
switch (shutdown_behavior) {
case TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN:
- return "CONTINUE_ON_SHUTDOWN";
+ os << "CONTINUE_ON_SHUTDOWN";
+ break;
case TaskShutdownBehavior::SKIP_ON_SHUTDOWN:
- return "SKIP_ON_SHUTDOWN";
+ os << "SKIP_ON_SHUTDOWN";
+ break;
case TaskShutdownBehavior::BLOCK_SHUTDOWN:
- return "BLOCK_SHUTDOWN";
+ os << "BLOCK_SHUTDOWN";
+ break;
}
- NOTREACHED();
- return "";
-}
-
-std::ostream& operator<<(std::ostream& os, const TaskPriority& task_priority) {
- os << TaskPriorityToString(task_priority);
- return os;
-}
-
-std::ostream& operator<<(std::ostream& os,
- const TaskShutdownBehavior& shutdown_behavior) {
- os << TaskShutdownBehaviorToString(shutdown_behavior);
return os;
}
diff --git a/base/task_scheduler/task_traits.h b/base/task_scheduler/task_traits.h
index 435fdac9af..0c0d304dcf 100644
--- a/base/task_scheduler/task_traits.h
+++ b/base/task_scheduler/task_traits.h
@@ -78,57 +78,19 @@ enum class TaskShutdownBehavior {
// Describes metadata for a single task or a group of tasks.
class BASE_EXPORT TaskTraits {
public:
- // Constructs a default TaskTraits for tasks that
- // (1) don't block (ref. MayBlock() and WithBaseSyncPrimitives()),
- // (2) prefer inheriting the current priority to specifying their own, and
- // (3) can either block shutdown or be skipped on shutdown
- // (TaskScheduler implementation is free to choose a fitting default).
- // Tasks that require stricter guarantees and/or know the specific
- // TaskPriority appropriate for them should highlight those by requesting
+ // Constructs a default TaskTraits for tasks with
+ // (1) no I/O,
+ // (2) low priority, and
+ // (3) may block shutdown or be skipped on shutdown.
+ // Tasks that require stricter guarantees should highlight those by requesting
// explicit traits below.
TaskTraits();
TaskTraits(const TaskTraits& other) = default;
TaskTraits& operator=(const TaskTraits& other) = default;
~TaskTraits();
- // Tasks with this trait may block. This includes but is not limited to tasks
- // that wait on synchronous file I/O operations: read or write a file from
- // disk, interact with a pipe or a socket, rename or delete a file, enumerate
- // files in a directory, etc. This trait isn't required for the mere use of
- // locks. For tasks that block on base/ synchronization primitives, see
- // WithBaseSyncPrimitives().
- TaskTraits& MayBlock();
-
- // Tasks with this trait will pass base::AssertWaitAllowed(), i.e. will be
- // allowed on the following methods :
- // - base::WaitableEvent::Wait
- // - base::ConditionVariable::Wait
- // - base::PlatformThread::Join
- // - base::PlatformThread::Sleep
- // - base::Process::WaitForExit
- // - base::Process::WaitForExitWithTimeout
- //
- // Tasks should generally not use these methods.
- //
- // Instead of waiting on a WaitableEvent or a ConditionVariable, put the work
- // that should happen after the wait in a callback and post that callback from
- // where the WaitableEvent or ConditionVariable would have been signaled. If
- // something needs to be scheduled after many tasks have executed, use
- // base::BarrierClosure.
- //
- // Avoid creating threads. Instead, use
- // base::Create(Sequenced|SingleTreaded)TaskRunnerWithTraits(). If a thread is
- // really needed, make it non-joinable and add cleanup work at the end of the
- // thread's main function (if using base::Thread, override Cleanup()).
- //
- // On Windows, join processes asynchronously using base::win::ObjectWatcher.
- //
- // MayBlock() must be specified in conjunction with this trait if and only if
- // removing usage of methods listed above in the labeled tasks would still
- // result in tasks that may block (per MayBlock()'s definition).
- //
- // In doubt, consult with //base/task_scheduler/OWNERS.
- TaskTraits& WithBaseSyncPrimitives();
+ // Allows tasks with these traits to do file I/O.
+ TaskTraits& WithFileIO();
// Applies |priority| to tasks with these traits.
TaskTraits& WithPriority(TaskPriority priority);
@@ -136,11 +98,8 @@ class BASE_EXPORT TaskTraits {
// Applies |shutdown_behavior| to tasks with these traits.
TaskTraits& WithShutdownBehavior(TaskShutdownBehavior shutdown_behavior);
- // Returns true if tasks with these traits may block.
- bool may_block() const { return may_block_; }
-
- // Returns true if tasks with these traits may use base/ sync primitives.
- bool with_base_sync_primitives() const { return with_base_sync_primitives_; }
+ // Returns true if file I/O is allowed by these traits.
+ bool with_file_io() const { return with_file_io_; }
// Returns the priority of tasks with these traits.
TaskPriority priority() const { return priority_; }
@@ -149,22 +108,29 @@ class BASE_EXPORT TaskTraits {
TaskShutdownBehavior shutdown_behavior() const { return shutdown_behavior_; }
private:
- bool may_block_;
- bool with_base_sync_primitives_;
+ bool with_file_io_;
TaskPriority priority_;
TaskShutdownBehavior shutdown_behavior_;
};
-// Returns string literals for the enums defined in this file. These methods
-// should only be used for tracing and debugging.
-BASE_EXPORT const char* TaskPriorityToString(TaskPriority task_priority);
-BASE_EXPORT const char* TaskShutdownBehaviorToString(
- TaskShutdownBehavior task_priority);
+// Describes how tasks are executed by a task runner.
+enum class ExecutionMode {
+ // Can execute multiple tasks at a time in any order.
+ PARALLEL,
+
+ // Executes one task at a time in posting order. The sequence’s priority is
+ // equivalent to the highest priority pending task in the sequence.
+ SEQUENCED,
-// Stream operators so that the enums defined in this file can be used in
-// DCHECK and EXPECT statements.
+ // Executes one task at a time on a single thread in posting order.
+ SINGLE_THREADED,
+};
+
+// Stream operators so TaskPriority and TaskShutdownBehavior can be used in
+// DCHECK statements.
BASE_EXPORT std::ostream& operator<<(std::ostream& os,
const TaskPriority& shutdown_behavior);
+
BASE_EXPORT std::ostream& operator<<(
std::ostream& os,
const TaskShutdownBehavior& shutdown_behavior);
diff --git a/base/task_scheduler/test_utils.h b/base/task_scheduler/test_utils.h
index dbd1227f52..bafd09aa2a 100644
--- a/base/task_scheduler/test_utils.h
+++ b/base/task_scheduler/test_utils.h
@@ -5,16 +5,15 @@
#ifndef BASE_TASK_SCHEDULER_TEST_UTILS_H_
#define BASE_TASK_SCHEDULER_TEST_UTILS_H_
-namespace base {
-namespace internal {
-namespace test {
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
-// An enumeration of possible task scheduler TaskRunner types. Used to
-// parametrize relevant task_scheduler tests.
-enum class ExecutionMode { PARALLEL, SEQUENCED, SINGLE_THREADED };
-
-} // namespace test
-} // namespace internal
-} // namespace base
+// Death tests misbehave on Android.
+#if DCHECK_IS_ON() && defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID)
+#define EXPECT_DCHECK_DEATH(statement, regex) EXPECT_DEATH(statement, regex)
+#else
+#define EXPECT_DCHECK_DEATH(statement, regex)
+#endif
#endif // BASE_TASK_SCHEDULER_TEST_UTILS_H_
diff --git a/base/template_util.h b/base/template_util.h
index 42552107cf..1bfc1ac814 100644
--- a/base/template_util.h
+++ b/base/template_util.h
@@ -23,28 +23,6 @@
#define CR_USE_FALLBACKS_FOR_OLD_GLIBCXX
#endif
-// Some versions of libstdc++ have partial support for type_traits, but misses
-// a smaller subset while removing some of the older non-standard stuff. Assume
-// that all versions below 5.0 fall in this category, along with one 5.0
-// experimental release. Test for this by consulting compiler major version,
-// the only reliable option available, so theoretically this could fail should
-// you attempt to mix an earlier version of libstdc++ with >= GCC5. But
-// that's unlikely to work out, especially as GCC5 changed ABI.
-#define CR_GLIBCXX_5_0_0 20150123
-#if (defined(__GNUC__) && __GNUC__ < 5) || \
- (defined(__GLIBCXX__) && __GLIBCXX__ == CR_GLIBCXX_5_0_0)
-#define CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX
-#endif
-
-// This hacks around using gcc with libc++ which has some incompatibilies.
-// - is_trivially_* doesn't work: https://llvm.org/bugs/show_bug.cgi?id=27538
-// TODO(danakj): Remove this when android builders are all using a newer version
-// of gcc, or the android ndk is updated to a newer libc++ that works with older
-// gcc versions.
-#if !defined(__clang__) && defined(_LIBCPP_VERSION)
-#define CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX
-#endif
-
namespace base {
template <class T> struct is_non_const_reference : std::false_type {};
@@ -148,53 +126,8 @@ template <class T>
using is_trivially_destructible = std::is_trivially_destructible<T>;
#endif
-// is_trivially_copyable is especially hard to get right.
-// - Older versions of libstdc++ will fail to have it like they do for other
-// type traits. In this case we should provide it based on compiler
-// intrinsics. This is covered by the CR_USE_FALLBACKS_FOR_OLD_GLIBCXX define.
-// - An experimental release of gcc includes most of type_traits but misses
-// is_trivially_copyable, so we still have to avoid using libstdc++ in this
-// case, which is covered by CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX.
-// - When compiling libc++ from before r239653, with a gcc compiler, the
-// std::is_trivially_copyable can fail. So we need to work around that by not
-// using the one in libc++ in this case. This is covered by the
-// CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX define, and is discussed in
-// https://llvm.org/bugs/show_bug.cgi?id=27538#c1 where they point out that
-// in libc++'s commit r239653 this is fixed by libc++ checking for gcc 5.1.
-// - In both of the above cases we are using the gcc compiler. When defining
-// this ourselves on compiler intrinsics, the __is_trivially_copyable()
-// intrinsic is not available on gcc before version 5.1 (see the discussion in
-// https://llvm.org/bugs/show_bug.cgi?id=27538#c1 again), so we must check for
-// that version.
-// - When __is_trivially_copyable() is not available because we are on gcc older
-// than 5.1, we need to fall back to something, so we use __has_trivial_copy()
-// instead based on what was done one-off in bit_cast() previously.
-
-// TODO(crbug.com/554293): Remove this when all platforms have this in the std
-// namespace and it works with gcc as needed.
-#if defined(CR_USE_FALLBACKS_FOR_OLD_GLIBCXX) || \
- defined(CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX) || \
- defined(CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX)
-template <typename T>
-struct is_trivially_copyable {
-// TODO(danakj): Remove this when android builders are all using a newer version
-// of gcc, or the android ndk is updated to a newer libc++ that does this for
-// us.
-#if _GNUC_VER >= 501
- static constexpr bool value = __is_trivially_copyable(T);
-#else
- static constexpr bool value = __has_trivial_copy(T);
-#endif
-};
-#else
-template <class T>
-using is_trivially_copyable = std::is_trivially_copyable<T>;
-#endif
-
} // namespace base
#undef CR_USE_FALLBACKS_FOR_OLD_GLIBCXX
-#undef CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX
-#undef CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX
#endif // BASE_TEMPLATE_UTIL_H_
diff --git a/base/test/BUILD.gn b/base/test/BUILD.gn
index 844707ebd1..51863a2a0c 100644
--- a/base/test/BUILD.gn
+++ b/base/test/BUILD.gn
@@ -22,16 +22,11 @@ static_library("test_config") {
]
}
+# GYP: //base/base.gyp:test_support_base
static_library("test_support") {
testonly = true
sources = [
"../trace_event/trace_config_memory_test_util.h",
- "android/java_handler_thread_for_testing.cc",
- "android/java_handler_thread_for_testing.h",
- "android/test_system_message_handler_link_android.cc",
- "android/test_system_message_handler_link_android.h",
- "fuzzed_data_provider.cc",
- "fuzzed_data_provider.h",
"gtest_util.cc",
"gtest_util.h",
"gtest_xml_unittest_result_printer.cc",
@@ -40,15 +35,12 @@ static_library("test_support") {
"gtest_xml_util.h",
"histogram_tester.cc",
"histogram_tester.h",
- "icu_test_util.cc",
- "icu_test_util.h",
"ios/wait_util.h",
"ios/wait_util.mm",
"launcher/test_result.cc",
"launcher/test_result.h",
"launcher/test_results_tracker.h",
"launcher/unit_test_launcher.h",
- "mock_callback.h",
"mock_chrome_application_mac.h",
"mock_chrome_application_mac.mm",
"mock_devices_changed_observer.cc",
@@ -57,9 +49,7 @@ static_library("test_support") {
"mock_entropy_provider.h",
"mock_log.cc",
"mock_log.h",
- "multiprocess_test.cc",
"multiprocess_test.h",
- "multiprocess_test_android.cc",
"null_task_runner.cc",
"null_task_runner.h",
"opaque_ref_counted.cc",
@@ -72,20 +62,12 @@ static_library("test_support") {
"perf_time_logger.h",
"power_monitor_test_base.cc",
"power_monitor_test_base.h",
- "scoped_async_task_scheduler.cc",
- "scoped_async_task_scheduler.h",
"scoped_command_line.cc",
"scoped_command_line.h",
- "scoped_feature_list.cc",
- "scoped_feature_list.h",
"scoped_locale.cc",
"scoped_locale.h",
- "scoped_mock_time_message_loop_task_runner.cc",
- "scoped_mock_time_message_loop_task_runner.h",
"scoped_path_override.cc",
"scoped_path_override.h",
- "scoped_task_scheduler.cc",
- "scoped_task_scheduler.h",
"sequenced_task_runner_test_template.cc",
"sequenced_task_runner_test_template.h",
"sequenced_worker_pool_owner.cc",
@@ -151,6 +133,8 @@ static_library("test_support") {
"launcher/test_launcher_tracer.h",
"launcher/test_results_tracker.cc",
"launcher/unit_test_launcher.cc",
+ "multiprocess_test.cc",
+ "multiprocess_test_android.cc",
]
}
@@ -194,11 +178,7 @@ static_library("test_support") {
}
if (is_android) {
- deps += [
- ":base_unittests_jni_headers",
- ":test_support_jni_headers",
- ]
- public_deps += [ ":test_support_java" ]
+ deps += [ ":base_unittests_jni_headers" ]
}
if (is_nacl_nonsfi) {
@@ -211,8 +191,6 @@ static_library("test_support") {
sources -= [
"gtest_xml_util.cc",
"gtest_xml_util.h",
- "icu_test_util.cc",
- "icu_test_util.h",
"perf_test_suite.cc",
"perf_test_suite.h",
"scoped_path_override.cc",
@@ -277,41 +255,6 @@ static_library("run_all_unittests") {
]
}
-# These sources are linked into both the base_unittests binary and the test
-# shared library target below.
-source_set("native_library_test_utils") {
- testonly = true
- sources = [
- "native_library_test_utils.cc",
- "native_library_test_utils.h",
- ]
-}
-
-# This shared library is dynamically loaded by NativeLibrary unittests.
-shared_library("test_shared_library") {
- testonly = true
- sources = [
- "test_shared_library.cc",
- ]
-
- deps = [
- ":native_library_test_utils",
- ]
-}
-
-static_library("run_all_base_unittests") {
- # Only targets in base should depend on this, targets outside base
- # should depend on run_all_unittests above.
- visibility = [ "//base/*" ]
- testonly = true
- sources = [
- "run_all_base_unittests.cc",
- ]
- deps = [
- ":test_support",
- ]
-}
-
if (is_linux) {
shared_library("malloc_wrapper") {
testonly = true
@@ -329,47 +272,8 @@ if (is_android) {
generate_jni("base_unittests_jni_headers") {
sources = [
"android/java/src/org/chromium/base/ContentUriTestUtils.java",
- "android/java/src/org/chromium/base/TestSystemMessageHandler.java",
"android/java/src/org/chromium/base/TestUiThread.java",
]
jni_package = "base"
}
-
- generate_jni("test_support_jni_headers") {
- sources = [
- "android/java/src/org/chromium/base/MainReturnCodeResult.java",
- "android/java/src/org/chromium/base/MultiprocessTestClientLauncher.java",
- ]
- jni_package = "base"
- }
-
- android_library("test_support_java") {
- testonly = true
- deps = [
- "//base:base_java",
- "//testing/android/native_test:native_main_runner_java",
- "//third_party/android_tools:android_support_annotations_java",
- "//third_party/jsr-305:jsr_305_javalib",
- ]
- srcjar_deps = [ ":test_support_java_aidl" ]
- java_files = [
- "android/java/src/org/chromium/base/FileDescriptorInfo.java",
- "android/java/src/org/chromium/base/MainReturnCodeResult.java",
- "android/java/src/org/chromium/base/MultiprocessTestClientLauncher.java",
- "android/java/src/org/chromium/base/MultiprocessTestClientService.java",
- "android/java/src/org/chromium/base/MultiprocessTestClientService0.java",
- "android/java/src/org/chromium/base/MultiprocessTestClientService1.java",
- "android/java/src/org/chromium/base/MultiprocessTestClientService2.java",
- "android/java/src/org/chromium/base/MultiprocessTestClientService3.java",
- "android/java/src/org/chromium/base/MultiprocessTestClientService4.java",
- ]
- }
-
- android_aidl("test_support_java_aidl") {
- testonly = true
- import_include = [ "android/java/src" ]
- sources = [
- "android/java/src/org/chromium/base/ITestClient.aidl",
- ]
- }
}
diff --git a/base/test/gtest_util.cc b/base/test/gtest_util.cc
deleted file mode 100644
index 6da902da2e..0000000000
--- a/base/test/gtest_util.cc
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/test/gtest_util.h"
-
-#include <stddef.h>
-
-#include <memory>
-
-#include "base/files/file_path.h"
-#include "base/json/json_file_value_serializer.h"
-#include "base/strings/string_util.h"
-#include "base/values.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace base {
-
-TestIdentifier::TestIdentifier() {
-}
-
-TestIdentifier::TestIdentifier(const TestIdentifier& other) = default;
-
-std::string FormatFullTestName(const std::string& test_case_name,
- const std::string& test_name) {
- return test_case_name + "." + test_name;
-}
-
-std::string TestNameWithoutDisabledPrefix(const std::string& full_test_name) {
- std::string test_name_no_disabled(full_test_name);
- ReplaceSubstringsAfterOffset(&test_name_no_disabled, 0, "DISABLED_", "");
- return test_name_no_disabled;
-}
-
-std::vector<TestIdentifier> GetCompiledInTests() {
- testing::UnitTest* const unit_test = testing::UnitTest::GetInstance();
-
- std::vector<TestIdentifier> tests;
- for (int i = 0; i < unit_test->total_test_case_count(); ++i) {
- const testing::TestCase* test_case = unit_test->GetTestCase(i);
- for (int j = 0; j < test_case->total_test_count(); ++j) {
- const testing::TestInfo* test_info = test_case->GetTestInfo(j);
- TestIdentifier test_data;
- test_data.test_case_name = test_case->name();
- test_data.test_name = test_info->name();
- test_data.file = test_info->file();
- test_data.line = test_info->line();
- tests.push_back(test_data);
- }
- }
- return tests;
-}
-
-bool WriteCompiledInTestsToFile(const FilePath& path) {
- std::vector<TestIdentifier> tests(GetCompiledInTests());
-
- ListValue root;
- for (size_t i = 0; i < tests.size(); ++i) {
- std::unique_ptr<DictionaryValue> test_info(new DictionaryValue);
- test_info->SetString("test_case_name", tests[i].test_case_name);
- test_info->SetString("test_name", tests[i].test_name);
- test_info->SetString("file", tests[i].file);
- test_info->SetInteger("line", tests[i].line);
- root.Append(std::move(test_info));
- }
-
- JSONFileValueSerializer serializer(path);
- return serializer.Serialize(root);
-}
-
-bool ReadTestNamesFromFile(const FilePath& path,
- std::vector<TestIdentifier>* output) {
- JSONFileValueDeserializer deserializer(path);
- int error_code = 0;
- std::string error_message;
- std::unique_ptr<base::Value> value =
- deserializer.Deserialize(&error_code, &error_message);
- if (!value.get())
- return false;
-
- base::ListValue* tests = nullptr;
- if (!value->GetAsList(&tests))
- return false;
-
- std::vector<base::TestIdentifier> result;
- for (base::ListValue::iterator i = tests->begin(); i != tests->end(); ++i) {
- base::DictionaryValue* test = nullptr;
- if (!(*i)->GetAsDictionary(&test))
- return false;
-
- TestIdentifier test_data;
-
- if (!test->GetStringASCII("test_case_name", &test_data.test_case_name))
- return false;
-
- if (!test->GetStringASCII("test_name", &test_data.test_name))
- return false;
-
- if (!test->GetStringASCII("file", &test_data.file))
- return false;
-
- if (!test->GetInteger("line", &test_data.line))
- return false;
-
- result.push_back(test_data);
- }
-
- output->swap(result);
- return true;
-}
-
-} // namespace base
diff --git a/base/test/gtest_util.h b/base/test/gtest_util.h
deleted file mode 100644
index 8dfb1f236f..0000000000
--- a/base/test/gtest_util.h
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_TEST_GTEST_UTIL_H_
-#define BASE_TEST_GTEST_UTIL_H_
-
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/compiler_specific.h"
-#include "base/logging.h"
-#include "build/build_config.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-// EXPECT/ASSERT_DCHECK_DEATH is intended to replace EXPECT/ASSERT_DEBUG_DEATH
-// when the death is expected to be caused by a DCHECK. Contrary to
-// EXPECT/ASSERT_DEBUG_DEATH however, it doesn't execute the statement in non-
-// dcheck builds as DCHECKs are intended to catch things that should never
-// happen and as such executing the statement results in undefined behavior
-// (|statement| is compiled in unsupported configurations nonetheless).
-// Death tests misbehave on Android.
-#if DCHECK_IS_ON() && defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID)
-
-// EXPECT/ASSERT_DCHECK_DEATH tests verify that a DCHECK is hit ("Check failed"
-// is part of the error message), but intentionally do not expose the gtest
-// death test's full |regex| parameter to avoid users having to verify the exact
-// syntax of the error message produced by the DCHECK.
-#define EXPECT_DCHECK_DEATH(statement) EXPECT_DEATH(statement, "Check failed")
-#define ASSERT_DCHECK_DEATH(statement) ASSERT_DEATH(statement, "Check failed")
-
-#else
-// DCHECK_IS_ON() && defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID)
-
-// Macro copied from gtest-death-test-internal.h as it's (1) internal for now
-// and (2) only defined if !GTEST_HAS_DEATH_TEST which is only a subset of the
-// conditions in which it's needed here.
-// TODO(gab): Expose macro in upstream gtest repo for consumers like us that
-// want more specific death tests and remove this hack.
-# define GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, terminator) \
- GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
- if (::testing::internal::AlwaysTrue()) { \
- GTEST_LOG_(WARNING) \
- << "Death tests are not supported on this platform.\n" \
- << "Statement '" #statement "' cannot be verified."; \
- } else if (::testing::internal::AlwaysFalse()) { \
- ::testing::internal::RE::PartialMatch(".*", (regex)); \
- GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
- terminator; \
- } else \
- ::testing::Message()
-
-#define EXPECT_DCHECK_DEATH(statement) \
- GTEST_UNSUPPORTED_DEATH_TEST(statement, "Check failed", )
-#define ASSERT_DCHECK_DEATH(statement) \
- GTEST_UNSUPPORTED_DEATH_TEST(statement, "Check failed", return)
-
-#endif
-// DCHECK_IS_ON() && defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID)
-
-namespace base {
-
-class FilePath;
-
-struct TestIdentifier {
- TestIdentifier();
- TestIdentifier(const TestIdentifier& other);
-
- std::string test_case_name;
- std::string test_name;
- std::string file;
- int line;
-};
-
-// Constructs a full test name given a test case name and a test name,
-// e.g. for test case "A" and test name "B" returns "A.B".
-std::string FormatFullTestName(const std::string& test_case_name,
- const std::string& test_name);
-
-// Returns the full test name with the "DISABLED_" prefix stripped out.
-// e.g. for the full test names "A.DISABLED_B", "DISABLED_A.B", and
-// "DISABLED_A.DISABLED_B", returns "A.B".
-std::string TestNameWithoutDisabledPrefix(const std::string& full_test_name);
-
-// Returns a vector of gtest-based tests compiled into
-// current executable.
-std::vector<TestIdentifier> GetCompiledInTests();
-
-// Writes the list of gtest-based tests compiled into
-// current executable as a JSON file. Returns true on success.
-bool WriteCompiledInTestsToFile(const FilePath& path) WARN_UNUSED_RESULT;
-
-// Reads the list of gtest-based tests from |path| into |output|.
-// Returns true on success.
-bool ReadTestNamesFromFile(
- const FilePath& path,
- std::vector<TestIdentifier>* output) WARN_UNUSED_RESULT;
-
-} // namespace base
-
-#endif // BASE_TEST_GTEST_UTIL_H_
diff --git a/base/test/mock_entropy_provider.cc b/base/test/mock_entropy_provider.cc
deleted file mode 100644
index 5ebf19a7c7..0000000000
--- a/base/test/mock_entropy_provider.cc
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2015 The Chromium 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/test/mock_entropy_provider.h"
-
-namespace base {
-
-MockEntropyProvider::MockEntropyProvider() : entropy_value_(0.5) {}
-MockEntropyProvider::MockEntropyProvider(double entropy_value)
- : entropy_value_(entropy_value) {}
-MockEntropyProvider::~MockEntropyProvider() {}
-
-double MockEntropyProvider::GetEntropyForTrial(
- const std::string& trial_name,
- uint32_t randomization_seed) const {
- return entropy_value_;
-}
-
-} // namespace base
diff --git a/base/test/mock_entropy_provider.h b/base/test/mock_entropy_provider.h
deleted file mode 100644
index ca2b4bc8fe..0000000000
--- a/base/test/mock_entropy_provider.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_TEST_MOCK_ENTROPY_PROVIDER_H_
-#define BASE_TEST_MOCK_ENTROPY_PROVIDER_H_
-
-#include <stdint.h>
-
-#include "base/metrics/field_trial.h"
-
-namespace base {
-
-class MockEntropyProvider : public base::FieldTrial::EntropyProvider {
- public:
- MockEntropyProvider();
- explicit MockEntropyProvider(double entropy_value);
- ~MockEntropyProvider() override;
-
- // base::FieldTrial::EntropyProvider:
- double GetEntropyForTrial(const std::string& trial_name,
- uint32_t randomization_seed) const override;
-
- private:
- double entropy_value_;
-
- DISALLOW_COPY_AND_ASSIGN(MockEntropyProvider);
-};
-
-} // namespace base
-
-#endif // BASE_TEST_MOCK_ENTROPY_PROVIDER_H_
diff --git a/base/test/multiprocess_test.cc b/base/test/multiprocess_test.cc
index fcc4d123ed..de56e7f6be 100644
--- a/base/test/multiprocess_test.cc
+++ b/base/test/multiprocess_test.cc
@@ -26,19 +26,6 @@ Process SpawnMultiProcessTestChild(
return LaunchProcess(command_line, options);
}
-
-bool WaitForMultiprocessTestChildExit(const Process& process,
- TimeDelta timeout,
- int* exit_code) {
- return process.WaitForExitWithTimeout(timeout, exit_code);
-}
-
-bool TerminateMultiProcessTestChild(const Process& process,
- int exit_code,
- bool wait) {
- return process.Terminate(exit_code, wait);
-}
-
#endif // !OS_ANDROID && !__ANDROID__ && !__ANDROID_HOST__
CommandLine GetMultiProcessTestChildBaseCommandLine() {
@@ -52,8 +39,6 @@ CommandLine GetMultiProcessTestChildBaseCommandLine() {
MultiProcessTest::MultiProcessTest() {
}
-// Don't compile on Arc++.
-#if 0
Process MultiProcessTest::SpawnChild(const std::string& procname) {
LaunchOptions options;
#if defined(OS_WIN)
@@ -67,7 +52,6 @@ Process MultiProcessTest::SpawnChildWithOptions(
const LaunchOptions& options) {
return SpawnMultiProcessTestChild(procname, MakeCmdLine(procname), options);
}
-#endif
CommandLine MultiProcessTest::MakeCmdLine(const std::string& procname) {
CommandLine command_line = GetMultiProcessTestChildBaseCommandLine();
diff --git a/base/test/multiprocess_test.h b/base/test/multiprocess_test.h
index bf9663759e..ae4c3eb2ef 100644
--- a/base/test/multiprocess_test.h
+++ b/base/test/multiprocess_test.h
@@ -40,7 +40,7 @@ class CommandLine;
// // Do stuff involving |test_child_process| and the child process....
//
// int rv = -1;
-// ASSERT_TRUE(base::WaitForMultiprocessTestChildExit(test_child_process,
+// ASSERT_TRUE(test_child_process.WaitForExitWithTimeout(
// TestTimeouts::action_timeout(), &rv));
// EXPECT_EQ(0, rv);
// }
@@ -51,10 +51,6 @@ class CommandLine;
// // Code here runs in a child process....
// return 0;
// }
-//
-// If you need to terminate the child process, use the
-// TerminateMultiProcessTestChild method to ensure that test will work on
-// Android.
// Spawns a child process and executes the function |procname| declared using
// |MULTIPROCESS_TEST_MAIN()| or |MULTIPROCESS_TEST_MAIN_WITH_SETUP()|.
@@ -70,17 +66,24 @@ Process SpawnMultiProcessTestChild(
// may add any flags needed for your child process.
CommandLine GetMultiProcessTestChildBaseCommandLine();
-// Waits for the child process to exit. Returns true if the process exited
-// within |timeout| and sets |exit_code| if non null.
-bool WaitForMultiprocessTestChildExit(const Process& process,
- TimeDelta timeout,
- int* exit_code);
-
-// Terminates |process| with |exit_code|. If |wait| is true, this call blocks
-// until the process actually terminates.
-bool TerminateMultiProcessTestChild(const Process& process,
- int exit_code,
- bool wait);
+#if defined(OS_ANDROID)
+
+// Enable the alternate test child implementation which support spawning a child
+// after threads have been created. If used, this MUST be the first line of
+// main(). The main function is passed in to avoid a link-time dependency in
+// component builds.
+void InitAndroidMultiProcessTestHelper(int (*main)(int, char**));
+
+// Returns true if the current process is a test child.
+bool AndroidIsChildProcess();
+
+// Wait for a test child to exit if the alternate test child implementation is
+// being used.
+bool AndroidWaitForChildExitWithTimeout(
+ const Process& process, TimeDelta timeout, int* exit_code)
+ WARN_UNUSED_RESULT;
+
+#endif // defined(OS_ANDROID)
// MultiProcessTest ------------------------------------------------------------
diff --git a/base/test/multiprocess_test_android.cc b/base/test/multiprocess_test_android.cc
index c74f013da1..f58b452d1c 100644
--- a/base/test/multiprocess_test_android.cc
+++ b/base/test/multiprocess_test_android.cc
@@ -4,87 +4,451 @@
#include "base/test/multiprocess_test.h"
+#include <errno.h>
#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <memory>
+#include <utility>
#include <vector>
-#include "base/android/context_utils.h"
-#include "base/android/jni_android.h"
-#include "base/android/jni_array.h"
-#include "base/android/scoped_java_ref.h"
#include "base/base_switches.h"
#include "base/command_line.h"
+#include "base/containers/hash_tables.h"
+#include "base/lazy_instance.h"
#include "base/logging.h"
-#include "jni/MainReturnCodeResult_jni.h"
-#include "jni/MultiprocessTestClientLauncher_jni.h"
+#include "base/macros.h"
+#include "base/pickle.h"
+#include "base/posix/global_descriptors.h"
+#include "base/posix/unix_domain_socket_linux.h"
+#include "testing/multiprocess_func_list.h"
namespace base {
-// A very basic implementation for Android. On Android tests can run in an APK
-// and we don't have an executable to exec*. This implementation does the bare
-// minimum to execute the method specified by procname (in the child process).
-// - All options except |fds_to_remap| are ignored.
-//
-// NOTE: This MUST NOT run on the main thread of the NativeTest application.
-Process SpawnMultiProcessTestChild(const std::string& procname,
- const CommandLine& base_command_line,
- const LaunchOptions& options) {
- JNIEnv* env = android::AttachCurrentThread();
- DCHECK(env);
+namespace {
- std::vector<int> fd_keys;
- std::vector<int> fd_fds;
- if (options.fds_to_remap) {
- for (auto& iter : *options.fds_to_remap) {
- fd_keys.push_back(iter.second);
- fd_fds.push_back(iter.first);
+const int kMaxMessageSize = 1024 * 1024;
+const int kFragmentSize = 4096;
+
+// Message sent between parent process and helper child process.
+enum class MessageType : uint32_t {
+ START_REQUEST,
+ START_RESPONSE,
+ WAIT_REQUEST,
+ WAIT_RESPONSE,
+};
+
+struct MessageHeader {
+ uint32_t size;
+ MessageType type;
+};
+
+struct StartProcessRequest {
+ MessageHeader header =
+ {sizeof(StartProcessRequest), MessageType::START_REQUEST};
+
+ uint32_t num_args = 0;
+ uint32_t num_fds = 0;
+};
+
+struct StartProcessResponse {
+ MessageHeader header =
+ {sizeof(StartProcessResponse), MessageType::START_RESPONSE};
+
+ pid_t child_pid;
+};
+
+struct WaitProcessRequest {
+ MessageHeader header =
+ {sizeof(WaitProcessRequest), MessageType::WAIT_REQUEST};
+
+ pid_t pid;
+ uint64_t timeout_ms;
+};
+
+struct WaitProcessResponse {
+ MessageHeader header =
+ {sizeof(WaitProcessResponse), MessageType::WAIT_RESPONSE};
+
+ bool success = false;
+ int32_t exit_code = 0;
+};
+
+// Helper class that implements an alternate test child launcher for
+// multi-process tests. The default implementation doesn't work if the child is
+// launched after starting threads. However, for some tests (i.e. Mojo), this
+// is necessary. This implementation works around that issue by forking a helper
+// process very early in main(), before any real work is done. Then, when a
+// child needs to be spawned, a message is sent to that helper process, which
+// then forks and returns the result to the parent. The forked child then calls
+// main() and things look as though a brand new process has been fork/exec'd.
+class LaunchHelper {
+ public:
+ using MainFunction = int (*)(int, char**);
+
+ LaunchHelper() {}
+
+ // Initialise the alternate test child implementation.
+ void Init(MainFunction main);
+
+ // Starts a child test helper process.
+ Process StartChildTestHelper(const std::string& procname,
+ const CommandLine& base_command_line,
+ const LaunchOptions& options);
+
+ // Waits for a child test helper process.
+ bool WaitForChildExitWithTimeout(const Process& process, TimeDelta timeout,
+ int* exit_code);
+
+ bool IsReady() const { return child_fd_ != -1; }
+ bool IsChild() const { return is_child_; }
+
+ private:
+ // Wrappers around sendmsg/recvmsg that supports message fragmentation.
+ void Send(int fd, const MessageHeader* msg, const std::vector<int>& fds);
+ ssize_t Recv(int fd, void* buf, std::vector<ScopedFD>* fds);
+
+ // Parent process implementation.
+ void DoParent(int fd);
+ // Helper process implementation.
+ void DoHelper(int fd);
+
+ void StartProcessInHelper(const StartProcessRequest* request,
+ std::vector<ScopedFD> fds);
+ void WaitForChildInHelper(const WaitProcessRequest* request);
+
+ bool is_child_ = false;
+
+ // Parent vars.
+ int child_fd_ = -1;
+
+ // Helper vars.
+ int parent_fd_ = -1;
+ MainFunction main_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(LaunchHelper);
+};
+
+void LaunchHelper::Init(MainFunction main) {
+ main_ = main;
+
+ // Create a communication channel between the parent and child launch helper.
+ // fd[0] belongs to the parent, fd[1] belongs to the child.
+ int fds[2] = {-1, -1};
+ int rv = socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds);
+ PCHECK(rv == 0);
+ CHECK_NE(-1, fds[0]);
+ CHECK_NE(-1, fds[1]);
+
+ pid_t pid = fork();
+ PCHECK(pid >= 0) << "Fork failed";
+ if (pid) {
+ // Parent.
+ rv = close(fds[1]);
+ PCHECK(rv == 0);
+ DoParent(fds[0]);
+ } else {
+ // Helper.
+ rv = close(fds[0]);
+ PCHECK(rv == 0);
+ DoHelper(fds[1]);
+ NOTREACHED();
+ _exit(0);
+ }
+}
+
+void LaunchHelper::Send(
+ int fd, const MessageHeader* msg, const std::vector<int>& fds) {
+ uint32_t bytes_remaining = msg->size;
+ const char* buf = reinterpret_cast<const char*>(msg);
+ while (bytes_remaining) {
+ size_t send_size =
+ (bytes_remaining > kFragmentSize) ? kFragmentSize : bytes_remaining;
+ bool success = UnixDomainSocket::SendMsg(
+ fd, buf, send_size,
+ (bytes_remaining == msg->size) ? fds : std::vector<int>());
+ CHECK(success);
+ bytes_remaining -= send_size;
+ buf += send_size;
+ }
+}
+
+ssize_t LaunchHelper::Recv(int fd, void* buf, std::vector<ScopedFD>* fds) {
+ ssize_t size = UnixDomainSocket::RecvMsg(fd, buf, kFragmentSize, fds);
+ if (size <= 0)
+ return size;
+
+ const MessageHeader* header = reinterpret_cast<const MessageHeader*>(buf);
+ CHECK(header->size < kMaxMessageSize);
+ uint32_t bytes_remaining = header->size - size;
+ char* buffer = reinterpret_cast<char*>(buf);
+ buffer += size;
+ while (bytes_remaining) {
+ std::vector<ScopedFD> dummy_fds;
+ size = UnixDomainSocket::RecvMsg(fd, buffer, kFragmentSize, &dummy_fds);
+ if (size <= 0)
+ return size;
+
+ CHECK(dummy_fds.empty());
+ CHECK(size == kFragmentSize ||
+ static_cast<size_t>(size) == bytes_remaining);
+ bytes_remaining -= size;
+ buffer += size;
+ }
+ return header->size;
+}
+
+void LaunchHelper::DoParent(int fd) {
+ child_fd_ = fd;
+}
+
+void LaunchHelper::DoHelper(int fd) {
+ parent_fd_ = fd;
+ is_child_ = true;
+ std::unique_ptr<char[]> buf(new char[kMaxMessageSize]);
+ while (true) {
+ // Wait for a message from the parent.
+ std::vector<ScopedFD> fds;
+ ssize_t size = Recv(parent_fd_, buf.get(), &fds);
+ if (size == 0 || (size < 0 && errno == ECONNRESET)) {
+ _exit(0);
+ }
+ PCHECK(size > 0);
+
+ const MessageHeader* header =
+ reinterpret_cast<const MessageHeader*>(buf.get());
+ CHECK_EQ(static_cast<ssize_t>(header->size), size);
+ switch (header->type) {
+ case MessageType::START_REQUEST:
+ StartProcessInHelper(
+ reinterpret_cast<const StartProcessRequest*>(buf.get()),
+ std::move(fds));
+ break;
+ case MessageType::WAIT_REQUEST:
+ WaitForChildInHelper(
+ reinterpret_cast<const WaitProcessRequest*>(buf.get()));
+ break;
+ default:
+ LOG(FATAL) << "Unsupported message type: "
+ << static_cast<uint32_t>(header->type);
}
}
+}
- android::ScopedJavaLocalRef<jobjectArray> fds =
- android::Java_MultiprocessTestClientLauncher_makeFdInfoArray(
- env, base::android::ToJavaIntArray(env, fd_keys),
- base::android::ToJavaIntArray(env, fd_fds));
+void LaunchHelper::StartProcessInHelper(const StartProcessRequest* request,
+ std::vector<ScopedFD> fds) {
+ pid_t pid = fork();
+ PCHECK(pid >= 0) << "Fork failed";
+ if (pid) {
+ // Helper.
+ StartProcessResponse resp;
+ resp.child_pid = pid;
+ Send(parent_fd_, reinterpret_cast<const MessageHeader*>(&resp),
+ std::vector<int>());
+ } else {
+ // Child.
+ PCHECK(close(parent_fd_) == 0);
+ parent_fd_ = -1;
+ CommandLine::Reset();
+
+ Pickle serialised_extra(reinterpret_cast<const char*>(request + 1),
+ request->header.size - sizeof(StartProcessRequest));
+ PickleIterator iter(serialised_extra);
+ std::vector<std::string> args;
+ for (size_t i = 0; i < request->num_args; i++) {
+ std::string arg;
+ CHECK(iter.ReadString(&arg));
+ args.push_back(std::move(arg));
+ }
+
+ CHECK_EQ(request->num_fds, fds.size());
+ for (size_t i = 0; i < request->num_fds; i++) {
+ int new_fd;
+ CHECK(iter.ReadInt(&new_fd));
+ int old_fd = fds[i].release();
+ if (new_fd != old_fd) {
+ if (dup2(old_fd, new_fd) < 0) {
+ PLOG(FATAL) << "dup2";
+ }
+ PCHECK(close(old_fd) == 0);
+ }
+ }
+
+ // argv has argc+1 elements, where the last element is NULL.
+ std::unique_ptr<char*[]> argv(new char*[args.size() + 1]);
+ for (size_t i = 0; i < args.size(); i++) {
+ argv[i] = const_cast<char*>(args[i].c_str());
+ }
+ argv[args.size()] = nullptr;
+ _exit(main_(args.size(), argv.get()));
+ NOTREACHED();
+ }
+}
+
+void LaunchHelper::WaitForChildInHelper(const WaitProcessRequest* request) {
+ Process process(request->pid);
+ TimeDelta timeout = TimeDelta::FromMilliseconds(request->timeout_ms);
+ int exit_code = -1;
+ bool success = process.WaitForExitWithTimeout(timeout, &exit_code);
+
+ WaitProcessResponse resp;
+ resp.exit_code = exit_code;
+ resp.success = success;
+ Send(parent_fd_, reinterpret_cast<const MessageHeader*>(&resp),
+ std::vector<int>());
+}
+
+Process LaunchHelper::StartChildTestHelper(const std::string& procname,
+ const CommandLine& base_command_line,
+ const LaunchOptions& options) {
CommandLine command_line(base_command_line);
- if (!command_line.HasSwitch(switches::kTestChildProcess)) {
+ if (!command_line.HasSwitch(switches::kTestChildProcess))
command_line.AppendSwitchASCII(switches::kTestChildProcess, procname);
+
+ StartProcessRequest request;
+ Pickle serialised_extra;
+ const CommandLine::StringVector& argv = command_line.argv();
+ for (const auto& arg : argv)
+ CHECK(serialised_extra.WriteString(arg));
+ request.num_args = argv.size();
+
+ std::vector<int> fds_to_send;
+ if (options.fds_to_remap) {
+ for (auto p : *options.fds_to_remap) {
+ CHECK(serialised_extra.WriteInt(p.second));
+ fds_to_send.push_back(p.first);
+ }
+ request.num_fds = options.fds_to_remap->size();
}
- android::ScopedJavaLocalRef<jobjectArray> j_argv =
- android::ToJavaArrayOfStrings(env, command_line.argv());
- jint pid = android::Java_MultiprocessTestClientLauncher_launchClient(
- env, android::GetApplicationContext(), j_argv, fds);
- return Process(pid);
+ size_t buf_size = sizeof(StartProcessRequest) + serialised_extra.size();
+ request.header.size = buf_size;
+ std::unique_ptr<char[]> buffer(new char[buf_size]);
+ memcpy(buffer.get(), &request, sizeof(StartProcessRequest));
+ memcpy(buffer.get() + sizeof(StartProcessRequest), serialised_extra.data(),
+ serialised_extra.size());
+
+ // Send start message.
+ Send(child_fd_, reinterpret_cast<const MessageHeader*>(buffer.get()),
+ fds_to_send);
+
+ // Synchronously get response.
+ StartProcessResponse response;
+ std::vector<ScopedFD> recv_fds;
+ ssize_t resp_size = Recv(child_fd_, &response, &recv_fds);
+ PCHECK(resp_size == sizeof(StartProcessResponse));
+
+ return Process(response.child_pid);
}
-bool WaitForMultiprocessTestChildExit(const Process& process,
- TimeDelta timeout,
- int* exit_code) {
- JNIEnv* env = android::AttachCurrentThread();
- DCHECK(env);
-
- base::android::ScopedJavaLocalRef<jobject> result_code =
- android::Java_MultiprocessTestClientLauncher_waitForMainToReturn(
- env, android::GetApplicationContext(), process.Pid(),
- static_cast<int32_t>(timeout.InMilliseconds()));
- if (result_code.is_null() ||
- Java_MainReturnCodeResult_hasTimedOut(env, result_code)) {
+bool LaunchHelper::WaitForChildExitWithTimeout(
+ const Process& process, TimeDelta timeout, int* exit_code) {
+
+ WaitProcessRequest request;
+ request.pid = process.Handle();
+ request.timeout_ms = timeout.InMilliseconds();
+
+ Send(child_fd_, reinterpret_cast<const MessageHeader*>(&request),
+ std::vector<int>());
+
+ WaitProcessResponse response;
+ std::vector<ScopedFD> recv_fds;
+ ssize_t resp_size = Recv(child_fd_, &response, &recv_fds);
+ PCHECK(resp_size == sizeof(WaitProcessResponse));
+
+ if (!response.success)
return false;
- }
- if (exit_code) {
- *exit_code = Java_MainReturnCodeResult_getReturnCode(env, result_code);
- }
+
+ *exit_code = response.exit_code;
return true;
}
-bool TerminateMultiProcessTestChild(const Process& process,
- int exit_code,
- bool wait) {
- JNIEnv* env = android::AttachCurrentThread();
- DCHECK(env);
+LazyInstance<LaunchHelper>::Leaky g_launch_helper;
+
+} // namespace
+
+void InitAndroidMultiProcessTestHelper(int (*main)(int, char**)) {
+ DCHECK(main);
+ // Don't allow child processes to themselves create new child processes.
+ if (g_launch_helper.Get().IsChild())
+ return;
+ g_launch_helper.Get().Init(main);
+}
+
+bool AndroidIsChildProcess() {
+ return g_launch_helper.Get().IsChild();
+}
+
+bool AndroidWaitForChildExitWithTimeout(
+ const Process& process, TimeDelta timeout, int* exit_code) {
+ CHECK(g_launch_helper.Get().IsReady());
+ return g_launch_helper.Get().WaitForChildExitWithTimeout(
+ process, timeout, exit_code);
+}
+
+// A very basic implementation for Android. On Android tests can run in an APK
+// and we don't have an executable to exec*. This implementation does the bare
+// minimum to execute the method specified by procname (in the child process).
+// - All options except |fds_to_remap| are ignored.
+Process SpawnMultiProcessTestChild(const std::string& procname,
+ const CommandLine& base_command_line,
+ const LaunchOptions& options) {
+ if (g_launch_helper.Get().IsReady()) {
+ return g_launch_helper.Get().StartChildTestHelper(
+ procname, base_command_line, options);
+ }
+
+ // TODO(viettrungluu): The FD-remapping done below is wrong in the presence of
+ // cycles (e.g., fd1 -> fd2, fd2 -> fd1). crbug.com/326576
+ FileHandleMappingVector empty;
+ const FileHandleMappingVector* fds_to_remap =
+ options.fds_to_remap ? options.fds_to_remap : &empty;
+
+ pid_t pid = fork();
+
+ if (pid < 0) {
+ PLOG(ERROR) << "fork";
+ return Process();
+ }
+ if (pid > 0) {
+ // Parent process.
+ return Process(pid);
+ }
+ // Child process.
+ base::hash_set<int> fds_to_keep_open;
+ for (FileHandleMappingVector::const_iterator it = fds_to_remap->begin();
+ it != fds_to_remap->end(); ++it) {
+ fds_to_keep_open.insert(it->first);
+ }
+ // Keep standard FDs (stdin, stdout, stderr, etc.) open since this
+ // is not meant to spawn a daemon.
+ int base = GlobalDescriptors::kBaseDescriptor;
+ for (int fd = base; fd < sysconf(_SC_OPEN_MAX); ++fd) {
+ if (fds_to_keep_open.find(fd) == fds_to_keep_open.end()) {
+ close(fd);
+ }
+ }
+ for (FileHandleMappingVector::const_iterator it = fds_to_remap->begin();
+ it != fds_to_remap->end(); ++it) {
+ int old_fd = it->first;
+ int new_fd = it->second;
+ if (dup2(old_fd, new_fd) < 0) {
+ PLOG(FATAL) << "dup2";
+ }
+ close(old_fd);
+ }
+ CommandLine::Reset();
+ CommandLine::Init(0, nullptr);
+ CommandLine* command_line = CommandLine::ForCurrentProcess();
+ command_line->InitFromArgv(base_command_line.argv());
+ if (!command_line->HasSwitch(switches::kTestChildProcess))
+ command_line->AppendSwitchASCII(switches::kTestChildProcess, procname);
- return android::Java_MultiprocessTestClientLauncher_terminate(
- env, android::GetApplicationContext(), process.Pid(), exit_code, wait);
+ _exit(multi_process_function_list::InvokeChildProcessTest(procname));
+ return Process();
}
} // namespace base
diff --git a/base/test/opaque_ref_counted.cc b/base/test/opaque_ref_counted.cc
index 36253e5ef9..ed6c36f1a2 100644
--- a/base/test/opaque_ref_counted.cc
+++ b/base/test/opaque_ref_counted.cc
@@ -11,31 +11,17 @@ namespace base {
class OpaqueRefCounted : public RefCounted<OpaqueRefCounted> {
public:
- OpaqueRefCounted() = default;
+ OpaqueRefCounted() {}
int Return42() { return 42; }
private:
- friend class RefCounted<OpaqueRefCounted>;
- ~OpaqueRefCounted() = default;
+ virtual ~OpaqueRefCounted() {}
+ friend RefCounted<OpaqueRefCounted>;
DISALLOW_COPY_AND_ASSIGN(OpaqueRefCounted);
};
-class OpaqueRefCountedThreadSafe
- : public RefCounted<OpaqueRefCountedThreadSafe> {
- public:
- OpaqueRefCountedThreadSafe() = default;
-
- int Return42() { return 42; }
-
- private:
- friend class RefCounted<OpaqueRefCountedThreadSafe>;
- ~OpaqueRefCountedThreadSafe() = default;
-
- DISALLOW_COPY_AND_ASSIGN(OpaqueRefCountedThreadSafe);
-};
-
scoped_refptr<OpaqueRefCounted> MakeOpaqueRefCounted() {
return new OpaqueRefCounted();
}
@@ -44,16 +30,6 @@ void TestOpaqueRefCounted(scoped_refptr<OpaqueRefCounted> p) {
EXPECT_EQ(42, p->Return42());
}
-scoped_refptr<OpaqueRefCountedThreadSafe> MakeOpaqueRefCountedThreadSafe() {
- return new OpaqueRefCountedThreadSafe();
-}
-
-void TestOpaqueRefCountedThreadSafe(
- scoped_refptr<OpaqueRefCountedThreadSafe> p) {
- EXPECT_EQ(42, p->Return42());
-}
-
} // namespace base
template class scoped_refptr<base::OpaqueRefCounted>;
-template class scoped_refptr<base::OpaqueRefCountedThreadSafe>;
diff --git a/base/test/opaque_ref_counted.h b/base/test/opaque_ref_counted.h
index c0ddc87fe1..faf6a650fd 100644
--- a/base/test/opaque_ref_counted.h
+++ b/base/test/opaque_ref_counted.h
@@ -12,18 +12,13 @@ namespace base {
// OpaqueRefCounted is a test class for scoped_refptr to ensure it still works
// when the pointed-to type is opaque (i.e., incomplete).
class OpaqueRefCounted;
-class OpaqueRefCountedThreadSafe;
// Test functions that return and accept scoped_refptr<OpaqueRefCounted> values.
scoped_refptr<OpaqueRefCounted> MakeOpaqueRefCounted();
void TestOpaqueRefCounted(scoped_refptr<OpaqueRefCounted> p);
-scoped_refptr<OpaqueRefCountedThreadSafe> MakeOpaqueRefCountedThreadSafe();
-void TestOpaqueRefCountedThreadSafe(
- scoped_refptr<OpaqueRefCountedThreadSafe> p);
} // namespace base
extern template class scoped_refptr<base::OpaqueRefCounted>;
-extern template class scoped_refptr<base::OpaqueRefCountedThreadSafe>;
#endif // BASE_TEST_OPAQUE_REF_COUNTED_H_
diff --git a/base/test/scoped_feature_list.cc b/base/test/scoped_feature_list.cc
deleted file mode 100644
index f0f3f4edfb..0000000000
--- a/base/test/scoped_feature_list.cc
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2016 The Chromium 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/test/scoped_feature_list.h"
-
-#include <string>
-
-namespace base {
-namespace test {
-
-namespace {
-
-static std::string GetFeatureString(
- const std::initializer_list<base::Feature>& features) {
- std::string output;
- for (const base::Feature& feature : features) {
- if (!output.empty())
- output += ",";
- output += feature.name;
- }
- return output;
-}
-
-} // namespace
-
-ScopedFeatureList::ScopedFeatureList() {}
-
-ScopedFeatureList::~ScopedFeatureList() {
- if (original_feature_list_) {
- base::FeatureList::ClearInstanceForTesting();
- base::FeatureList::RestoreInstanceForTesting(
- std::move(original_feature_list_));
- }
-}
-
-void ScopedFeatureList::Init() {
- std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
- feature_list->InitializeFromCommandLine(std::string(), std::string());
- InitWithFeatureList(std::move(feature_list));
-}
-
-void ScopedFeatureList::InitWithFeatures(
- const std::initializer_list<base::Feature>& enabled_features,
- const std::initializer_list<base::Feature>& disabled_features) {
- InitFromCommandLine(GetFeatureString(enabled_features),
- GetFeatureString(disabled_features));
-}
-
-void ScopedFeatureList::InitWithFeatureList(
- std::unique_ptr<FeatureList> feature_list) {
- DCHECK(!original_feature_list_);
- original_feature_list_ = base::FeatureList::ClearInstanceForTesting();
- base::FeatureList::SetInstance(std::move(feature_list));
-}
-
-void ScopedFeatureList::InitFromCommandLine(
- const std::string& enable_features,
- const std::string& disable_features) {
- std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
- feature_list->InitializeFromCommandLine(enable_features, disable_features);
- InitWithFeatureList(std::move(feature_list));
-}
-
-void ScopedFeatureList::InitAndEnableFeature(const base::Feature& feature) {
- InitFromCommandLine(feature.name, std::string());
-}
-
-void ScopedFeatureList::InitAndDisableFeature(const base::Feature& feature) {
- InitFromCommandLine(std::string(), feature.name);
-}
-
-} // namespace test
-} // namespace base
diff --git a/base/test/scoped_feature_list.h b/base/test/scoped_feature_list.h
deleted file mode 100644
index 99e07f5374..0000000000
--- a/base/test/scoped_feature_list.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_TEST_SCOPED_FEATURE_LIST_H_
-#define BASE_TEST_SCOPED_FEATURE_LIST_H_
-
-#include <initializer_list>
-
-#include "base/feature_list.h"
-
-namespace base {
-namespace test {
-
-// ScopedFeatureList resets the global FeatureList instance to a new empty
-// instance and restores the original instance upon destruction.
-// Note: Re-using the same object is not allowed. To reset the feature
-// list and initialize it anew, destroy an existing scoped list and init
-// a new one.
-class ScopedFeatureList final {
- public:
- ScopedFeatureList();
- ~ScopedFeatureList();
-
- // Initializes and registers a FeatureList instance with no overrides.
- void Init();
-
- // Initializes and registers the given FeatureList instance.
- void InitWithFeatureList(std::unique_ptr<FeatureList> feature_list);
-
- // Initializes and registers a FeatureList instance with the given enabled
- // and disabled features.
- void InitWithFeatures(
- const std::initializer_list<base::Feature>& enabled_features,
- const std::initializer_list<base::Feature>& disabled_features);
-
- // Initializes and registers a FeatureList instance with the given
- // enabled and disabled features (comma-separated names).
- void InitFromCommandLine(const std::string& enable_features,
- const std::string& disable_features);
-
- // Initializes and registers a FeatureList instance enabling a single
- // feature.
- void InitAndEnableFeature(const base::Feature& feature);
-
- // Initializes and registers a FeatureList instance disabling a single
- // feature.
- void InitAndDisableFeature(const base::Feature& feature);
-
- private:
- std::unique_ptr<FeatureList> original_feature_list_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedFeatureList);
-};
-
-} // namespace test
-} // namespace base
-
-#endif // BASE_TEST_SCOPED_FEATURE_LIST_H_
diff --git a/base/test/sequenced_worker_pool_owner.cc b/base/test/sequenced_worker_pool_owner.cc
index 324d071352..8781495d7d 100644
--- a/base/test/sequenced_worker_pool_owner.cc
+++ b/base/test/sequenced_worker_pool_owner.cc
@@ -14,10 +14,7 @@ SequencedWorkerPoolOwner::SequencedWorkerPoolOwner(
size_t max_threads,
const std::string& thread_name_prefix)
: constructor_message_loop_(MessageLoop::current()),
- pool_(new SequencedWorkerPool(max_threads,
- thread_name_prefix,
- TaskPriority::USER_VISIBLE,
- this)),
+ pool_(new SequencedWorkerPool(max_threads, thread_name_prefix, this)),
has_work_call_count_(0) {}
SequencedWorkerPoolOwner::~SequencedWorkerPoolOwner() {
@@ -28,8 +25,7 @@ SequencedWorkerPoolOwner::~SequencedWorkerPoolOwner() {
exit_loop_.Run();
}
-const scoped_refptr<SequencedWorkerPool>& SequencedWorkerPoolOwner::pool()
- const {
+const scoped_refptr<SequencedWorkerPool>& SequencedWorkerPoolOwner::pool() {
return pool_;
}
diff --git a/base/test/sequenced_worker_pool_owner.h b/base/test/sequenced_worker_pool_owner.h
index 28a6cf070a..05fc7505fb 100644
--- a/base/test/sequenced_worker_pool_owner.h
+++ b/base/test/sequenced_worker_pool_owner.h
@@ -39,7 +39,7 @@ class SequencedWorkerPoolOwner : public SequencedWorkerPool::TestingObserver {
~SequencedWorkerPoolOwner() override;
// Don't change the returned pool's testing observer.
- const scoped_refptr<SequencedWorkerPool>& pool() const;
+ const scoped_refptr<SequencedWorkerPool>& pool();
// The given callback will be called on WillWaitForShutdown().
void SetWillWaitForShutdownCallback(const Closure& callback);
diff --git a/base/test/test_file_util.h b/base/test/test_file_util.h
index d9172d757e..7042e48484 100644
--- a/base/test/test_file_util.h
+++ b/base/test/test_file_util.h
@@ -20,10 +20,6 @@
#include <jni.h>
#endif
-#if defined(OS_WIN)
-#include <windows.h>
-#endif
-
namespace base {
class FilePath;
@@ -44,12 +40,14 @@ bool DieFileDie(const FilePath& file, bool recurse);
bool EvictFileFromSystemCache(const FilePath& file);
#if defined(OS_WIN)
-// Deny |permission| on the file |path| for the current user. |permission| is an
-// ACCESS_MASK structure which is defined in
-// https://msdn.microsoft.com/en-us/library/windows/desktop/aa374892.aspx
-// Refer to https://msdn.microsoft.com/en-us/library/aa822867.aspx for a list of
-// possible values.
-bool DenyFilePermission(const FilePath& path, DWORD permission);
+// Returns true if the volume supports Alternate Data Streams.
+bool VolumeSupportsADS(const FilePath& path);
+
+// Returns true if the ZoneIdentifier is correctly set to "Internet" (3).
+// Note that this function must be called from the same process as
+// the one that set the zone identifier. I.e. don't use it in UI/automation
+// based tests.
+bool HasInternetZoneIdentifier(const FilePath& full_path);
#endif // defined(OS_WIN)
// For testing, make the file unreadable or unwritable.
@@ -72,6 +70,9 @@ class FilePermissionRestorer {
};
#if defined(OS_ANDROID)
+// Register the ContentUriTestUrils JNI bindings.
+bool RegisterContentUriTestUtils(JNIEnv* env);
+
// Insert an image file into the MediaStore, and retrieve the content URI for
// testing purpose.
FilePath InsertImageIntoMediaStore(const FilePath& path);
diff --git a/base/test/test_io_thread.cc b/base/test/test_io_thread.cc
index ce4a8d10de..1fa041251c 100644
--- a/base/test/test_io_thread.cc
+++ b/base/test/test_io_thread.cc
@@ -4,7 +4,19 @@
#include "base/test/test_io_thread.h"
-#include "base/logging.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/synchronization/waitable_event.h"
+
+namespace {
+
+void PostTaskAndWaitHelper(base::WaitableEvent* event,
+ const base::Closure& task) {
+ task.Run();
+ event->Signal();
+}
+
+} // namespace
namespace base {
@@ -42,4 +54,13 @@ void TestIOThread::PostTask(const tracked_objects::Location& from_here,
task_runner()->PostTask(from_here, task);
}
+void TestIOThread::PostTaskAndWait(const tracked_objects::Location& from_here,
+ const base::Closure& task) {
+ base::WaitableEvent event(WaitableEvent::ResetPolicy::AUTOMATIC,
+ WaitableEvent::InitialState::NOT_SIGNALED);
+ task_runner()->PostTask(from_here,
+ base::Bind(&PostTaskAndWaitHelper, &event, task));
+ event.Wait();
+}
+
} // namespace base
diff --git a/base/test/test_io_thread.h b/base/test/test_io_thread.h
index 5d3885e81c..c2ed1878d1 100644
--- a/base/test/test_io_thread.h
+++ b/base/test/test_io_thread.h
@@ -18,13 +18,6 @@ namespace base {
// Create and run an IO thread with a MessageLoop, and
// making the MessageLoop accessible from its client.
// It also provides some ideomatic API like PostTaskAndWait().
-//
-// This API is not thread-safe:
-// - Start()/Stop() should only be called from the main (creation) thread.
-// - PostTask()/message_loop()/task_runner() are also safe to call from the
-// underlying thread itself (to post tasks from other threads: get the
-// task_runner() from the main thread first, it is then safe to pass _it_
-// around).
class TestIOThread {
public:
enum Mode { kAutoStart, kManualStart };
@@ -32,14 +25,19 @@ class TestIOThread {
// Stops the I/O thread if necessary.
~TestIOThread();
- // After Stop(), Start() may be called again to start a new I/O thread.
- // Stop() may be called even when the I/O thread is not started.
+ // |Start()|/|Stop()| should only be called from the main (creation) thread.
+ // After |Stop()|, |Start()| may be called again to start a new I/O thread.
+ // |Stop()| may be called even when the I/O thread is not started.
void Start();
void Stop();
// Post |task| to the IO thread.
void PostTask(const tracked_objects::Location& from_here,
const base::Closure& task);
+ // Posts |task| to the IO-thread with an WaitableEvent associated blocks on
+ // it until the posted |task| is executed, then returns.
+ void PostTaskAndWait(const tracked_objects::Location& from_here,
+ const base::Closure& task);
base::MessageLoopForIO* message_loop() {
return static_cast<base::MessageLoopForIO*>(io_thread_.message_loop());
diff --git a/base/test/test_mock_time_task_runner.cc b/base/test/test_mock_time_task_runner.cc
deleted file mode 100644
index f4bd7244b4..0000000000
--- a/base/test/test_mock_time_task_runner.cc
+++ /dev/null
@@ -1,321 +0,0 @@
-// Copyright 2015 The Chromium 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/test/test_mock_time_task_runner.h"
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/memory/ref_counted.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/clock.h"
-#include "base/time/tick_clock.h"
-
-namespace base {
-
-namespace {
-
-// MockTickClock --------------------------------------------------------------
-
-// TickClock that always returns the then-current mock time ticks of
-// |task_runner| as the current time ticks.
-class MockTickClock : public TickClock {
- public:
- explicit MockTickClock(
- scoped_refptr<const TestMockTimeTaskRunner> task_runner);
-
- // TickClock:
- TimeTicks NowTicks() override;
-
- private:
- scoped_refptr<const TestMockTimeTaskRunner> task_runner_;
-
- DISALLOW_COPY_AND_ASSIGN(MockTickClock);
-};
-
-MockTickClock::MockTickClock(
- scoped_refptr<const TestMockTimeTaskRunner> task_runner)
- : task_runner_(task_runner) {
-}
-
-TimeTicks MockTickClock::NowTicks() {
- return task_runner_->NowTicks();
-}
-
-// MockClock ------------------------------------------------------------------
-
-// Clock that always returns the then-current mock time of |task_runner| as the
-// current time.
-class MockClock : public Clock {
- public:
- explicit MockClock(scoped_refptr<const TestMockTimeTaskRunner> task_runner);
-
- // Clock:
- Time Now() override;
-
- private:
- scoped_refptr<const TestMockTimeTaskRunner> task_runner_;
-
- DISALLOW_COPY_AND_ASSIGN(MockClock);
-};
-
-MockClock::MockClock(scoped_refptr<const TestMockTimeTaskRunner> task_runner)
- : task_runner_(task_runner) {
-}
-
-Time MockClock::Now() {
- return task_runner_->Now();
-}
-
-} // namespace
-
-// TestMockTimeTaskRunner::TestOrderedPendingTask -----------------------------
-
-// Subclass of TestPendingTask which has a strictly monotonically increasing ID
-// for every task, so that tasks posted with the same 'time to run' can be run
-// in the order of being posted.
-struct TestMockTimeTaskRunner::TestOrderedPendingTask
- : public base::TestPendingTask {
- TestOrderedPendingTask();
- TestOrderedPendingTask(const tracked_objects::Location& location,
- const Closure& task,
- TimeTicks post_time,
- TimeDelta delay,
- size_t ordinal,
- TestNestability nestability);
- TestOrderedPendingTask(TestOrderedPendingTask&&);
- ~TestOrderedPendingTask();
-
- TestOrderedPendingTask& operator=(TestOrderedPendingTask&&);
-
- size_t ordinal;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(TestOrderedPendingTask);
-};
-
-TestMockTimeTaskRunner::TestOrderedPendingTask::TestOrderedPendingTask()
- : ordinal(0) {
-}
-
-TestMockTimeTaskRunner::TestOrderedPendingTask::TestOrderedPendingTask(
- TestOrderedPendingTask&&) = default;
-
-TestMockTimeTaskRunner::TestOrderedPendingTask::TestOrderedPendingTask(
- const tracked_objects::Location& location,
- const Closure& task,
- TimeTicks post_time,
- TimeDelta delay,
- size_t ordinal,
- TestNestability nestability)
- : base::TestPendingTask(location, task, post_time, delay, nestability),
- ordinal(ordinal) {}
-
-TestMockTimeTaskRunner::TestOrderedPendingTask::~TestOrderedPendingTask() {
-}
-
-TestMockTimeTaskRunner::TestOrderedPendingTask&
-TestMockTimeTaskRunner::TestOrderedPendingTask::operator=(
- TestOrderedPendingTask&&) = default;
-
-// TestMockTimeTaskRunner -----------------------------------------------------
-
-// TODO(gab): This should also set the SequenceToken for the current thread.
-// Ref. TestMockTimeTaskRunner::RunsTasksOnCurrentThread().
-TestMockTimeTaskRunner::ScopedContext::ScopedContext(
- scoped_refptr<TestMockTimeTaskRunner> scope)
- : on_destroy_(ThreadTaskRunnerHandle::OverrideForTesting(scope)) {
- scope->RunUntilIdle();
-}
-
-TestMockTimeTaskRunner::ScopedContext::~ScopedContext() = default;
-
-bool TestMockTimeTaskRunner::TemporalOrder::operator()(
- const TestOrderedPendingTask& first_task,
- const TestOrderedPendingTask& second_task) const {
- if (first_task.GetTimeToRun() == second_task.GetTimeToRun())
- return first_task.ordinal > second_task.ordinal;
- return first_task.GetTimeToRun() > second_task.GetTimeToRun();
-}
-
-TestMockTimeTaskRunner::TestMockTimeTaskRunner()
- : now_(Time::UnixEpoch()), next_task_ordinal_(0) {
-}
-
-TestMockTimeTaskRunner::TestMockTimeTaskRunner(Time start_time,
- TimeTicks start_ticks)
- : now_(Time::UnixEpoch()), now_ticks_(start_ticks), next_task_ordinal_(0) {}
-
-TestMockTimeTaskRunner::~TestMockTimeTaskRunner() {
-}
-
-void TestMockTimeTaskRunner::FastForwardBy(TimeDelta delta) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK_GE(delta, TimeDelta());
-
- const TimeTicks original_now_ticks = now_ticks_;
- ProcessAllTasksNoLaterThan(delta);
- ForwardClocksUntilTickTime(original_now_ticks + delta);
-}
-
-void TestMockTimeTaskRunner::RunUntilIdle() {
- DCHECK(thread_checker_.CalledOnValidThread());
- ProcessAllTasksNoLaterThan(TimeDelta());
-}
-
-void TestMockTimeTaskRunner::FastForwardUntilNoTasksRemain() {
- DCHECK(thread_checker_.CalledOnValidThread());
- ProcessAllTasksNoLaterThan(TimeDelta::Max());
-}
-
-void TestMockTimeTaskRunner::ClearPendingTasks() {
- DCHECK(thread_checker_.CalledOnValidThread());
- AutoLock scoped_lock(tasks_lock_);
- while (!tasks_.empty())
- tasks_.pop();
-}
-
-Time TestMockTimeTaskRunner::Now() const {
- DCHECK(thread_checker_.CalledOnValidThread());
- return now_;
-}
-
-TimeTicks TestMockTimeTaskRunner::NowTicks() const {
- DCHECK(thread_checker_.CalledOnValidThread());
- return now_ticks_;
-}
-
-std::unique_ptr<Clock> TestMockTimeTaskRunner::GetMockClock() const {
- DCHECK(thread_checker_.CalledOnValidThread());
- return MakeUnique<MockClock>(this);
-}
-
-std::unique_ptr<TickClock> TestMockTimeTaskRunner::GetMockTickClock() const {
- DCHECK(thread_checker_.CalledOnValidThread());
- return MakeUnique<MockTickClock>(this);
-}
-
-std::deque<TestPendingTask> TestMockTimeTaskRunner::TakePendingTasks() {
- AutoLock scoped_lock(tasks_lock_);
- std::deque<TestPendingTask> tasks;
- while (!tasks_.empty()) {
- // It's safe to remove const and consume |task| here, since |task| is not
- // used for ordering the item.
- tasks.push_back(
- std::move(const_cast<TestOrderedPendingTask&>(tasks_.top())));
- tasks_.pop();
- }
- return tasks;
-}
-
-bool TestMockTimeTaskRunner::HasPendingTask() const {
- DCHECK(thread_checker_.CalledOnValidThread());
- return !tasks_.empty();
-}
-
-size_t TestMockTimeTaskRunner::GetPendingTaskCount() const {
- DCHECK(thread_checker_.CalledOnValidThread());
- return tasks_.size();
-}
-
-TimeDelta TestMockTimeTaskRunner::NextPendingTaskDelay() const {
- DCHECK(thread_checker_.CalledOnValidThread());
- return tasks_.empty() ? TimeDelta::Max()
- : tasks_.top().GetTimeToRun() - now_ticks_;
-}
-
-// TODO(gab): Combine |thread_checker_| with a SequenceToken to differentiate
-// between tasks running in the scope of this TestMockTimeTaskRunner and other
-// task runners sharing this thread. http://crbug.com/631186
-bool TestMockTimeTaskRunner::RunsTasksOnCurrentThread() const {
- return thread_checker_.CalledOnValidThread();
-}
-
-bool TestMockTimeTaskRunner::PostDelayedTask(
- const tracked_objects::Location& from_here,
- const Closure& task,
- TimeDelta delay) {
- AutoLock scoped_lock(tasks_lock_);
- tasks_.push(TestOrderedPendingTask(from_here, task, now_ticks_, delay,
- next_task_ordinal_++,
- TestPendingTask::NESTABLE));
- return true;
-}
-
-bool TestMockTimeTaskRunner::PostNonNestableDelayedTask(
- const tracked_objects::Location& from_here,
- const Closure& task,
- TimeDelta delay) {
- return PostDelayedTask(from_here, task, delay);
-}
-
-bool TestMockTimeTaskRunner::IsElapsingStopped() {
- return false;
-}
-
-void TestMockTimeTaskRunner::OnBeforeSelectingTask() {
- // Empty default implementation.
-}
-
-void TestMockTimeTaskRunner::OnAfterTimePassed() {
- // Empty default implementation.
-}
-
-void TestMockTimeTaskRunner::OnAfterTaskRun() {
- // Empty default implementation.
-}
-
-void TestMockTimeTaskRunner::ProcessAllTasksNoLaterThan(TimeDelta max_delta) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK_GE(max_delta, TimeDelta());
-
- // Multiple test task runners can share the same thread for determinism in
- // unit tests. Make sure this TestMockTimeTaskRunner's tasks run in its scope.
- ScopedClosureRunner undo_override;
- if (!ThreadTaskRunnerHandle::IsSet() ||
- ThreadTaskRunnerHandle::Get() != this) {
- undo_override = ThreadTaskRunnerHandle::OverrideForTesting(this);
- }
-
- const TimeTicks original_now_ticks = now_ticks_;
- while (!IsElapsingStopped()) {
- OnBeforeSelectingTask();
- TestPendingTask task_info;
- if (!DequeueNextTask(original_now_ticks, max_delta, &task_info))
- break;
- // If tasks were posted with a negative delay, task_info.GetTimeToRun() will
- // be less than |now_ticks_|. ForwardClocksUntilTickTime() takes care of not
- // moving the clock backwards in this case.
- ForwardClocksUntilTickTime(task_info.GetTimeToRun());
- std::move(task_info.task).Run();
- OnAfterTaskRun();
- }
-}
-
-void TestMockTimeTaskRunner::ForwardClocksUntilTickTime(TimeTicks later_ticks) {
- DCHECK(thread_checker_.CalledOnValidThread());
- if (later_ticks <= now_ticks_)
- return;
-
- now_ += later_ticks - now_ticks_;
- now_ticks_ = later_ticks;
- OnAfterTimePassed();
-}
-
-bool TestMockTimeTaskRunner::DequeueNextTask(const TimeTicks& reference,
- const TimeDelta& max_delta,
- TestPendingTask* next_task) {
- AutoLock scoped_lock(tasks_lock_);
- if (!tasks_.empty() &&
- (tasks_.top().GetTimeToRun() - reference) <= max_delta) {
- // It's safe to remove const and consume |task| here, since |task| is not
- // used for ordering the item.
- *next_task = std::move(const_cast<TestOrderedPendingTask&>(tasks_.top()));
- tasks_.pop();
- return true;
- }
- return false;
-}
-
-} // namespace base
diff --git a/base/test/test_mock_time_task_runner.h b/base/test/test_mock_time_task_runner.h
deleted file mode 100644
index 54ebbdb7a8..0000000000
--- a/base/test/test_mock_time_task_runner.h
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_TEST_TEST_MOCK_TIME_TASK_RUNNER_H_
-#define BASE_TEST_TEST_MOCK_TIME_TASK_RUNNER_H_
-
-#include <stddef.h>
-
-#include <deque>
-#include <memory>
-#include <queue>
-#include <vector>
-
-#include "base/callback_helpers.h"
-#include "base/macros.h"
-#include "base/single_thread_task_runner.h"
-#include "base/synchronization/lock.h"
-#include "base/test/test_pending_task.h"
-#include "base/threading/thread_checker_impl.h"
-#include "base/time/time.h"
-
-namespace base {
-
-class Clock;
-class TickClock;
-
-// Runs pending tasks in the order of the tasks' post time + delay, and keeps
-// track of a mock (virtual) tick clock time that can be fast-forwarded.
-//
-// TestMockTimeTaskRunner has the following properties:
-//
-// - Methods RunsTasksOnCurrentThread() and Post[Delayed]Task() can be called
-// from any thread, but the rest of the methods must be called on the same
-// thread the TaskRunner was created on.
-// - It allows for reentrancy, in that it handles the running of tasks that in
-// turn call back into it (e.g., to post more tasks).
-// - Tasks are stored in a priority queue, and executed in the increasing
-// order of post time + delay, but ignoring nestability.
-// - It does not check for overflow when doing time arithmetic. A sufficient
-// condition for preventing overflows is to make sure that the sum of all
-// posted task delays and fast-forward increments is still representable by
-// a TimeDelta, and that adding this delta to the starting values of Time
-// and TickTime is still within their respective range.
-// - Tasks aren't guaranteed to be destroyed immediately after they're run.
-//
-// This is a slightly more sophisticated version of TestSimpleTaskRunner, in
-// that it supports running delayed tasks in the correct temporal order.
-class TestMockTimeTaskRunner : public SingleThreadTaskRunner {
- public:
- // Everything that is executed in the scope of a ScopedContext will behave as
- // though it ran under |scope| (i.e. ThreadTaskRunnerHandle,
- // RunsTasksOnCurrentThread, etc.). This allows the test body to be all in one
- // block when multiple TestMockTimeTaskRunners share the main thread. For
- // example:
- //
- // class ExampleFixture {
- // protected:
- // DoBarOnFoo() {
- // DCHECK(foo_task_runner_->RunsOnCurrentThread());
- // EXPECT_EQ(foo_task_runner_, ThreadTaskRunnerHandle::Get());
- // DoBar();
- // }
- //
- // // Mock main task runner.
- // base::MessageLoop message_loop_;
- // base::ScopedMockTimeMessageLoopTaskRunner main_task_runner_;
- //
- // // Mock foo task runner.
- // scoped_refptr<TestMockTimeTaskRunner> foo_task_runner_ =
- // new TestMockTimeTaskRunner();
- // };
- //
- // TEST_F(ExampleFixture, DoBarOnFoo) {
- // DoThingsOnMain();
- // {
- // TestMockTimeTaskRunner::ScopedContext scoped_context(
- // foo_task_runner_.get());
- // DoBarOnFoo();
- // }
- // DoMoreThingsOnMain();
- // }
- //
- class ScopedContext {
- public:
- // Note: |scope| is ran until idle as part of this constructor to ensure
- // that anything which runs in the underlying scope runs after any already
- // pending tasks (the contrary would break the SequencedTraskRunner
- // contract).
- explicit ScopedContext(scoped_refptr<TestMockTimeTaskRunner> scope);
- ~ScopedContext();
-
- private:
- ScopedClosureRunner on_destroy_;
- DISALLOW_COPY_AND_ASSIGN(ScopedContext);
- };
-
- // Constructs an instance whose virtual time will start at the Unix epoch, and
- // whose time ticks will start at zero.
- TestMockTimeTaskRunner();
-
- // Constructs an instance starting at the given virtual time and time ticks.
- TestMockTimeTaskRunner(Time start_time, TimeTicks start_ticks);
-
- // Fast-forwards virtual time by |delta|, causing all tasks with a remaining
- // delay less than or equal to |delta| to be executed. |delta| must be
- // non-negative.
- void FastForwardBy(TimeDelta delta);
-
- // Fast-forwards virtual time just until all tasks are executed.
- void FastForwardUntilNoTasksRemain();
-
- // Executes all tasks that have no remaining delay. Tasks with a remaining
- // delay greater than zero will remain enqueued, and no virtual time will
- // elapse.
- void RunUntilIdle();
-
- // Clears the queue of pending tasks without running them.
- void ClearPendingTasks();
-
- // Returns the current virtual time (initially starting at the Unix epoch).
- Time Now() const;
-
- // Returns the current virtual tick time (initially starting at 0).
- TimeTicks NowTicks() const;
-
- // Returns a Clock that uses the virtual time of |this| as its time source.
- // The returned Clock will hold a reference to |this|.
- std::unique_ptr<Clock> GetMockClock() const;
-
- // Returns a TickClock that uses the virtual time ticks of |this| as its tick
- // source. The returned TickClock will hold a reference to |this|.
- std::unique_ptr<TickClock> GetMockTickClock() const;
-
- std::deque<TestPendingTask> TakePendingTasks();
- bool HasPendingTask() const;
- size_t GetPendingTaskCount() const;
- TimeDelta NextPendingTaskDelay() const;
-
- // SingleThreadTaskRunner:
- bool RunsTasksOnCurrentThread() const override;
- bool PostDelayedTask(const tracked_objects::Location& from_here,
- const Closure& task,
- TimeDelta delay) override;
- bool PostNonNestableDelayedTask(const tracked_objects::Location& from_here,
- const Closure& task,
- TimeDelta delay) override;
-
- protected:
- ~TestMockTimeTaskRunner() override;
-
- // Whether the elapsing of virtual time is stopped or not. Subclasses can
- // override this method to perform early exits from a running task runner.
- // Defaults to always return false.
- virtual bool IsElapsingStopped();
-
- // Called before the next task to run is selected, so that subclasses have a
- // last chance to make sure all tasks are posted.
- virtual void OnBeforeSelectingTask();
-
- // Called after the current mock time has been incremented so that subclasses
- // can react to the passing of time.
- virtual void OnAfterTimePassed();
-
- // Called after each task is run so that subclasses may perform additional
- // activities, e.g., pump additional task runners.
- virtual void OnAfterTaskRun();
-
- private:
- struct TestOrderedPendingTask;
-
- // Predicate that defines a strict weak temporal ordering of tasks.
- class TemporalOrder {
- public:
- bool operator()(const TestOrderedPendingTask& first_task,
- const TestOrderedPendingTask& second_task) const;
- };
-
- typedef std::priority_queue<TestOrderedPendingTask,
- std::vector<TestOrderedPendingTask>,
- TemporalOrder> TaskPriorityQueue;
-
- // Core of the implementation for all flavors of fast-forward methods. Given a
- // non-negative |max_delta|, runs all tasks with a remaining delay less than
- // or equal to |max_delta|, and moves virtual time forward as needed for each
- // processed task. Pass in TimeDelta::Max() as |max_delta| to run all tasks.
- void ProcessAllTasksNoLaterThan(TimeDelta max_delta);
-
- // Forwards |now_ticks_| until it equals |later_ticks|, and forwards |now_| by
- // the same amount. Calls OnAfterTimePassed() if |later_ticks| > |now_ticks_|.
- // Does nothing if |later_ticks| <= |now_ticks_|.
- void ForwardClocksUntilTickTime(TimeTicks later_ticks);
-
- // Returns the |next_task| to run if there is any with a running time that is
- // at most |reference| + |max_delta|. This additional complexity is required
- // so that |max_delta| == TimeDelta::Max() can be supported.
- bool DequeueNextTask(const TimeTicks& reference,
- const TimeDelta& max_delta,
- TestPendingTask* next_task);
-
- // Also used for non-dcheck logic (RunsTasksOnCurrentThread()) and as such
- // needs to be a ThreadCheckerImpl.
- ThreadCheckerImpl thread_checker_;
-
- Time now_;
- TimeTicks now_ticks_;
-
- // Temporally ordered heap of pending tasks. Must only be accessed while the
- // |tasks_lock_| is held.
- TaskPriorityQueue tasks_;
-
- // The ordinal to use for the next task. Must only be accessed while the
- // |tasks_lock_| is held.
- size_t next_task_ordinal_;
-
- Lock tasks_lock_;
-
- DISALLOW_COPY_AND_ASSIGN(TestMockTimeTaskRunner);
-};
-
-} // namespace base
-
-#endif // BASE_TEST_TEST_MOCK_TIME_TASK_RUNNER_H_
diff --git a/base/test/test_pending_task.cc b/base/test/test_pending_task.cc
index 98bc0179b8..87b107e838 100644
--- a/base/test/test_pending_task.cc
+++ b/base/test/test_pending_task.cc
@@ -22,9 +22,7 @@ TestPendingTask::TestPendingTask(
delay(delay),
nestability(nestability) {}
-TestPendingTask::TestPendingTask(TestPendingTask&& other) = default;
-
-TestPendingTask& TestPendingTask::operator=(TestPendingTask&& other) = default;
+TestPendingTask::TestPendingTask(const TestPendingTask& other) = default;
TimeTicks TestPendingTask::GetTimeToRun() const {
return post_time + delay;
diff --git a/base/test/test_pending_task.h b/base/test/test_pending_task.h
index 42f3f42c7b..2dbdb7eecc 100644
--- a/base/test/test_pending_task.h
+++ b/base/test/test_pending_task.h
@@ -21,7 +21,7 @@ struct TestPendingTask {
enum TestNestability { NESTABLE, NON_NESTABLE };
TestPendingTask();
- TestPendingTask(TestPendingTask&& other);
+ TestPendingTask(const TestPendingTask& other);
TestPendingTask(const tracked_objects::Location& location,
const Closure& task,
TimeTicks post_time,
@@ -29,8 +29,6 @@ struct TestPendingTask {
TestNestability nestability);
~TestPendingTask();
- TestPendingTask& operator=(TestPendingTask&& other);
-
// Returns post_time + delay.
TimeTicks GetTimeToRun() const;
@@ -53,7 +51,7 @@ struct TestPendingTask {
bool ShouldRunBefore(const TestPendingTask& other) const;
tracked_objects::Location location;
- OnceClosure task;
+ Closure task;
TimeTicks post_time;
TimeDelta delay;
TestNestability nestability;
@@ -63,9 +61,6 @@ struct TestPendingTask {
void AsValueInto(base::trace_event::TracedValue* state) const;
std::unique_ptr<base::trace_event::ConvertableToTraceFormat> AsValue() const;
std::string ToString() const;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(TestPendingTask);
};
// gtest helpers which allow pretty printing of the tasks, very useful in unit
diff --git a/base/test/test_simple_task_runner.cc b/base/test/test_simple_task_runner.cc
index 090a72e96a..cc39fab85a 100644
--- a/base/test/test_simple_task_runner.cc
+++ b/base/test/test_simple_task_runner.cc
@@ -5,20 +5,20 @@
#include "base/test/test_simple_task_runner.h"
#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/threading/thread_task_runner_handle.h"
namespace base {
-TestSimpleTaskRunner::TestSimpleTaskRunner() = default;
+TestSimpleTaskRunner::TestSimpleTaskRunner() {}
-TestSimpleTaskRunner::~TestSimpleTaskRunner() = default;
+TestSimpleTaskRunner::~TestSimpleTaskRunner() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
bool TestSimpleTaskRunner::PostDelayedTask(
const tracked_objects::Location& from_here,
const Closure& task,
TimeDelta delay) {
- AutoLock auto_lock(lock_);
+ DCHECK(thread_checker_.CalledOnValidThread());
pending_tasks_.push_back(
TestPendingTask(from_here, task, TimeTicks(), delay,
TestPendingTask::NESTABLE));
@@ -29,70 +29,48 @@ bool TestSimpleTaskRunner::PostNonNestableDelayedTask(
const tracked_objects::Location& from_here,
const Closure& task,
TimeDelta delay) {
- AutoLock auto_lock(lock_);
+ DCHECK(thread_checker_.CalledOnValidThread());
pending_tasks_.push_back(
TestPendingTask(from_here, task, TimeTicks(), delay,
TestPendingTask::NON_NESTABLE));
return true;
}
-// TODO(gab): Use SequenceToken here to differentiate between tasks running in
-// the scope of this TestSimpleTaskRunner and other task runners sharing this
-// thread. http://crbug.com/631186
bool TestSimpleTaskRunner::RunsTasksOnCurrentThread() const {
- return thread_ref_ == PlatformThread::CurrentRef();
-}
-
-std::deque<TestPendingTask> TestSimpleTaskRunner::TakePendingTasks() {
- AutoLock auto_lock(lock_);
- return std::move(pending_tasks_);
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return true;
}
-size_t TestSimpleTaskRunner::NumPendingTasks() const {
- AutoLock auto_lock(lock_);
- return pending_tasks_.size();
+const std::deque<TestPendingTask>&
+TestSimpleTaskRunner::GetPendingTasks() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return pending_tasks_;
}
bool TestSimpleTaskRunner::HasPendingTask() const {
- AutoLock auto_lock(lock_);
+ DCHECK(thread_checker_.CalledOnValidThread());
return !pending_tasks_.empty();
}
base::TimeDelta TestSimpleTaskRunner::NextPendingTaskDelay() const {
- AutoLock auto_lock(lock_);
+ DCHECK(thread_checker_.CalledOnValidThread());
return pending_tasks_.front().GetTimeToRun() - base::TimeTicks();
}
-base::TimeDelta TestSimpleTaskRunner::FinalPendingTaskDelay() const {
- AutoLock auto_lock(lock_);
- return pending_tasks_.back().GetTimeToRun() - base::TimeTicks();
-}
-
void TestSimpleTaskRunner::ClearPendingTasks() {
- AutoLock auto_lock(lock_);
+ DCHECK(thread_checker_.CalledOnValidThread());
pending_tasks_.clear();
}
void TestSimpleTaskRunner::RunPendingTasks() {
- DCHECK(RunsTasksOnCurrentThread());
-
+ DCHECK(thread_checker_.CalledOnValidThread());
// Swap with a local variable to avoid re-entrancy problems.
std::deque<TestPendingTask> tasks_to_run;
- {
- AutoLock auto_lock(lock_);
- tasks_to_run.swap(pending_tasks_);
+ tasks_to_run.swap(pending_tasks_);
+ for (std::deque<TestPendingTask>::iterator it = tasks_to_run.begin();
+ it != tasks_to_run.end(); ++it) {
+ it->task.Run();
}
-
- // Multiple test task runners can share the same thread for determinism in
- // unit tests. Make sure this TestSimpleTaskRunner's tasks run in its scope.
- ScopedClosureRunner undo_override;
- if (!ThreadTaskRunnerHandle::IsSet() ||
- ThreadTaskRunnerHandle::Get() != this) {
- undo_override = ThreadTaskRunnerHandle::OverrideForTesting(this);
- }
-
- for (auto& task : tasks_to_run)
- std::move(task.task).Run();
}
void TestSimpleTaskRunner::RunUntilIdle() {
diff --git a/base/test/test_simple_task_runner.h b/base/test/test_simple_task_runner.h
index d089ba8a0b..338c634c8d 100644
--- a/base/test/test_simple_task_runner.h
+++ b/base/test/test_simple_task_runner.h
@@ -10,9 +10,8 @@
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/single_thread_task_runner.h"
-#include "base/synchronization/lock.h"
#include "base/test/test_pending_task.h"
-#include "base/threading/platform_thread.h"
+#include "base/threading/thread_checker.h"
namespace base {
@@ -26,6 +25,8 @@ class TimeDelta;
//
// TestSimpleTaskRunner has the following properties which make it simple:
//
+// - It is non-thread safe; all member functions must be called on
+// the same thread.
// - Tasks are simply stored in a queue in FIFO order, ignoring delay
// and nestability.
// - Tasks aren't guaranteed to be destroyed immediately after
@@ -35,6 +36,10 @@ class TimeDelta;
// handles the running of tasks that in turn call back into itself
// (e.g., to post more tasks).
//
+// If you need more complicated properties, consider using this class
+// as a template for writing a test TaskRunner implementation using
+// TestPendingTask.
+//
// Note that, like any TaskRunner, TestSimpleTaskRunner is
// ref-counted.
class TestSimpleTaskRunner : public SingleThreadTaskRunner {
@@ -51,36 +56,27 @@ class TestSimpleTaskRunner : public SingleThreadTaskRunner {
bool RunsTasksOnCurrentThread() const override;
- std::deque<TestPendingTask> TakePendingTasks();
- size_t NumPendingTasks() const;
+ const std::deque<TestPendingTask>& GetPendingTasks() const;
bool HasPendingTask() const;
base::TimeDelta NextPendingTaskDelay() const;
- base::TimeDelta FinalPendingTaskDelay() const;
// Clears the queue of pending tasks without running them.
void ClearPendingTasks();
- // Runs each current pending task in order and clears the queue. Tasks posted
- // by the tasks that run within this call do not run within this call. Can
- // only be called on the thread that created this TestSimpleTaskRunner.
- void RunPendingTasks();
+ // Runs each current pending task in order and clears the queue.
+ // Any tasks posted by the tasks are not run.
+ virtual void RunPendingTasks();
- // Runs pending tasks until the queue is empty. Can only be called on the
- // thread that created this TestSimpleTaskRunner.
+ // Runs pending tasks until the queue is empty.
void RunUntilIdle();
protected:
~TestSimpleTaskRunner() override;
- private:
- // Thread on which this was instantiated.
- const PlatformThreadRef thread_ref_ = PlatformThread::CurrentRef();
-
- // Synchronizes access to |pending_tasks_|.
- mutable Lock lock_;
-
std::deque<TestPendingTask> pending_tasks_;
+ ThreadChecker thread_checker_;
+ private:
DISALLOW_COPY_AND_ASSIGN(TestSimpleTaskRunner);
};
diff --git a/base/test/test_timeouts.cc b/base/test/test_timeouts.cc
index 0dc0f49dee..55e9a79861 100644
--- a/base/test/test_timeouts.cc
+++ b/base/test/test_timeouts.cc
@@ -46,10 +46,7 @@ void InitializeTimeout(const char* switch_name, int min_value, int* value) {
std::string string_value(base::CommandLine::ForCurrentProcess()->
GetSwitchValueASCII(switch_name));
int timeout;
- if (string_value == TestTimeouts::kNoTimeoutSwitchValue)
- timeout = kAlmostInfiniteTimeoutMs;
- else
- base::StringToInt(string_value, &timeout);
+ base::StringToInt(string_value, &timeout);
*value = std::max(*value, timeout);
}
*value *= kTimeoutMultiplier;
@@ -68,9 +65,6 @@ void InitializeTimeout(const char* switch_name, int* value) {
} // namespace
// static
-constexpr const char TestTimeouts::kNoTimeoutSwitchValue[];
-
-// static
bool TestTimeouts::initialized_ = false;
// The timeout values should increase in the order they appear in this block.
diff --git a/base/test/test_timeouts.h b/base/test/test_timeouts.h
index 9d42eb91fe..ddaf05b5e0 100644
--- a/base/test/test_timeouts.h
+++ b/base/test/test_timeouts.h
@@ -13,9 +13,6 @@
// the timeouts for different environments (like Valgrind).
class TestTimeouts {
public:
- // Argument that can be passed on the command line to indicate "no timeout".
- static constexpr const char kNoTimeoutSwitchValue[] = "-1";
-
// Initializes the timeouts. Non thread-safe. Should be called exactly once
// by the test suite.
static void Initialize();
diff --git a/base/test/trace_event_analyzer.cc b/base/test/trace_event_analyzer.cc
index e61337cccb..55a349acf2 100644
--- a/base/test/trace_event_analyzer.cc
+++ b/base/test/trace_event_analyzer.cc
@@ -34,8 +34,8 @@ TraceEvent::~TraceEvent() {
TraceEvent& TraceEvent::operator=(TraceEvent&& rhs) = default;
bool TraceEvent::SetFromJSON(const base::Value* event_value) {
- if (event_value->GetType() != base::Value::Type::DICTIONARY) {
- LOG(ERROR) << "Value must be Type::DICTIONARY";
+ if (event_value->GetType() != base::Value::TYPE_DICTIONARY) {
+ LOG(ERROR) << "Value must be TYPE_DICTIONARY";
return false;
}
const base::DictionaryValue* dictionary =
diff --git a/base/test/trace_event_analyzer_unittest.cc b/base/test/trace_event_analyzer_unittest.cc
index ce7bce22a0..b4f0950793 100644
--- a/base/test/trace_event_analyzer_unittest.cc
+++ b/base/test/trace_event_analyzer_unittest.cc
@@ -123,7 +123,7 @@ TEST_F(TraceEventAnalyzerTest, TraceEvent) {
std::unique_ptr<base::Value> arg;
EXPECT_TRUE(event.GetArgAsValue("dict", &arg));
- EXPECT_EQ(base::Value::Type::DICTIONARY, arg->GetType());
+ EXPECT_EQ(base::Value::TYPE_DICTIONARY, arg->GetType());
}
TEST_F(TraceEventAnalyzerTest, QueryEventMember) {
diff --git a/base/third_party/dynamic_annotations/dynamic_annotations.c b/base/third_party/dynamic_annotations/dynamic_annotations.c
deleted file mode 100644
index 4313ecc5be..0000000000
--- a/base/third_party/dynamic_annotations/dynamic_annotations.c
+++ /dev/null
@@ -1,269 +0,0 @@
-/* Copyright (c) 2011, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifdef _MSC_VER
-# include <windows.h>
-#endif
-
-#ifdef __cplusplus
-# error "This file should be built as pure C to avoid name mangling"
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
-
-#ifdef __GNUC__
-/* valgrind.h uses gcc extensions so it won't build with other compilers */
-# include "base/third_party/valgrind/valgrind.h"
-#endif
-
-/* Compiler-based ThreadSanitizer defines
- DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL = 1
- and provides its own definitions of the functions. */
-
-#ifndef DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL
-# define DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL 0
-#endif
-
-/* Each function is empty and called (via a macro) only in debug mode.
- The arguments are captured by dynamic tools at runtime. */
-
-#if DYNAMIC_ANNOTATIONS_ENABLED == 1 \
- && DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL == 0
-
-/* Identical code folding(-Wl,--icf=all) countermeasures.
- This makes all Annotate* functions different, which prevents the linker from
- folding them. */
-#ifdef __COUNTER__
-#define DYNAMIC_ANNOTATIONS_IMPL \
- volatile unsigned short lineno = (__LINE__ << 8) + __COUNTER__; (void)lineno;
-#else
-#define DYNAMIC_ANNOTATIONS_IMPL \
- volatile unsigned short lineno = (__LINE__ << 8); (void)lineno;
-#endif
-
-/* WARNING: always add new annotations to the end of the list.
- Otherwise, lineno (see above) numbers for different Annotate* functions may
- conflict. */
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockCreate)(
- const char *file, int line, const volatile void *lock)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockDestroy)(
- const char *file, int line, const volatile void *lock)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockAcquired)(
- const char *file, int line, const volatile void *lock, long is_w)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockReleased)(
- const char *file, int line, const volatile void *lock, long is_w)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierInit)(
- const char *file, int line, const volatile void *barrier, long count,
- long reinitialization_allowed)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierWaitBefore)(
- const char *file, int line, const volatile void *barrier)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierWaitAfter)(
- const char *file, int line, const volatile void *barrier)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierDestroy)(
- const char *file, int line, const volatile void *barrier)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarWait)(
- const char *file, int line, const volatile void *cv,
- const volatile void *lock)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarSignal)(
- const char *file, int line, const volatile void *cv)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarSignalAll)(
- const char *file, int line, const volatile void *cv)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateHappensBefore)(
- const char *file, int line, const volatile void *obj)
-{DYNAMIC_ANNOTATIONS_IMPL};
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateHappensAfter)(
- const char *file, int line, const volatile void *obj)
-{DYNAMIC_ANNOTATIONS_IMPL};
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotatePublishMemoryRange)(
- const char *file, int line, const volatile void *address, long size)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateUnpublishMemoryRange)(
- const char *file, int line, const volatile void *address, long size)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQCreate)(
- const char *file, int line, const volatile void *pcq)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQDestroy)(
- const char *file, int line, const volatile void *pcq)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQPut)(
- const char *file, int line, const volatile void *pcq)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQGet)(
- const char *file, int line, const volatile void *pcq)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateNewMemory)(
- const char *file, int line, const volatile void *mem, long size)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateExpectRace)(
- const char *file, int line, const volatile void *mem,
- const char *description)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateFlushExpectedRaces)(
- const char *file, int line)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateBenignRace)(
- const char *file, int line, const volatile void *mem,
- const char *description)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateBenignRaceSized)(
- const char *file, int line, const volatile void *mem, long size,
- const char *description)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsUsedAsCondVar)(
- const char *file, int line, const volatile void *mu)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsNotPHB)(
- const char *file, int line, const volatile void *mu)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateTraceMemory)(
- const char *file, int line, const volatile void *arg)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateThreadName)(
- const char *file, int line, const char *name)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreReadsBegin)(
- const char *file, int line)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreReadsEnd)(
- const char *file, int line)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreWritesBegin)(
- const char *file, int line)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreWritesEnd)(
- const char *file, int line)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreSyncBegin)(
- const char *file, int line)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreSyncEnd)(
- const char *file, int line)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateEnableRaceDetection)(
- const char *file, int line, int enable)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateNoOp)(
- const char *file, int line, const volatile void *arg)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-void DYNAMIC_ANNOTATIONS_NAME(AnnotateFlushState)(
- const char *file, int line)
-{DYNAMIC_ANNOTATIONS_IMPL}
-
-#endif /* DYNAMIC_ANNOTATIONS_ENABLED == 1
- && DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL == 0 */
-
-#if DYNAMIC_ANNOTATIONS_PROVIDE_RUNNING_ON_VALGRIND == 1 \
- && DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL == 0
-static int GetRunningOnValgrind(void) {
-#ifdef RUNNING_ON_VALGRIND
- if (RUNNING_ON_VALGRIND) return 1;
-#endif
-
-#ifndef _MSC_VER
- char *running_on_valgrind_str = getenv("RUNNING_ON_VALGRIND");
- if (running_on_valgrind_str) {
- return strcmp(running_on_valgrind_str, "0") != 0;
- }
-#else
- /* Visual Studio issues warnings if we use getenv,
- * so we use GetEnvironmentVariableA instead.
- */
- char value[100] = "1";
- int res = GetEnvironmentVariableA("RUNNING_ON_VALGRIND",
- value, sizeof(value));
- /* value will remain "1" if res == 0 or res >= sizeof(value). The latter
- * can happen only if the given value is long, in this case it can't be "0".
- */
- if (res > 0 && strcmp(value, "0") != 0)
- return 1;
-#endif
- return 0;
-}
-
-/* See the comments in dynamic_annotations.h */
-int RunningOnValgrind(void) {
- static volatile int running_on_valgrind = -1;
- /* C doesn't have thread-safe initialization of statics, and we
- don't want to depend on pthread_once here, so hack it. */
- int local_running_on_valgrind = running_on_valgrind;
- if (local_running_on_valgrind == -1)
- running_on_valgrind = local_running_on_valgrind = GetRunningOnValgrind();
- return local_running_on_valgrind;
-}
-
-#endif /* DYNAMIC_ANNOTATIONS_PROVIDE_RUNNING_ON_VALGRIND == 1
- && DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL == 0 */
diff --git a/base/threading/non_thread_safe.h b/base/threading/non_thread_safe.h
index 64ae8e4da9..d41c08608c 100644
--- a/base/threading/non_thread_safe.h
+++ b/base/threading/non_thread_safe.h
@@ -10,7 +10,14 @@
// There is a specific macro to do it: NON_EXPORTED_BASE(), defined in
// compiler_specific.h
#include "base/compiler_specific.h"
-#include "base/logging.h"
+
+// See comment at top of thread_checker.h
+#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
+#define ENABLE_NON_THREAD_SAFE 1
+#else
+#define ENABLE_NON_THREAD_SAFE 0
+#endif
+
#include "base/threading/non_thread_safe_impl.h"
namespace base {
@@ -51,11 +58,13 @@ class NonThreadSafeDoNothing {
// to have a base::ThreadChecker as a member, rather than inherit from
// NonThreadSafe. For more details about when to choose one over the other, see
// the documentation for base::ThreadChecker.
-#if DCHECK_IS_ON()
+#if ENABLE_NON_THREAD_SAFE
typedef NonThreadSafeImpl NonThreadSafe;
#else
typedef NonThreadSafeDoNothing NonThreadSafe;
-#endif // DCHECK_IS_ON()
+#endif // ENABLE_NON_THREAD_SAFE
+
+#undef ENABLE_NON_THREAD_SAFE
} // namespace base
diff --git a/base/threading/non_thread_safe_unittest.cc b/base/threading/non_thread_safe_unittest.cc
index 5752d5f2b3..d523fc55b1 100644
--- a/base/threading/non_thread_safe_unittest.cc
+++ b/base/threading/non_thread_safe_unittest.cc
@@ -8,7 +8,6 @@
#include "base/logging.h"
#include "base/macros.h"
-#include "base/test/gtest_util.h"
#include "base/threading/simple_thread.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -107,6 +106,8 @@ TEST(NonThreadSafeTest, DetachThenDestructOnDifferentThread) {
delete_on_thread.Join();
}
+#if GTEST_HAS_DEATH_TEST || !ENABLE_NON_THREAD_SAFE
+
void NonThreadSafeClass::MethodOnDifferentThreadImpl() {
std::unique_ptr<NonThreadSafeClass> non_thread_safe_class(
new NonThreadSafeClass);
@@ -119,15 +120,17 @@ void NonThreadSafeClass::MethodOnDifferentThreadImpl() {
call_on_thread.Join();
}
-#if DCHECK_IS_ON()
+#if ENABLE_NON_THREAD_SAFE
TEST(NonThreadSafeDeathTest, MethodNotAllowedOnDifferentThreadInDebug) {
- ASSERT_DCHECK_DEATH({ NonThreadSafeClass::MethodOnDifferentThreadImpl(); });
+ ASSERT_DEATH({
+ NonThreadSafeClass::MethodOnDifferentThreadImpl();
+ }, "");
}
#else
TEST(NonThreadSafeTest, MethodAllowedOnDifferentThreadInRelease) {
NonThreadSafeClass::MethodOnDifferentThreadImpl();
}
-#endif // DCHECK_IS_ON()
+#endif // ENABLE_NON_THREAD_SAFE
void NonThreadSafeClass::DestructorOnDifferentThreadImpl() {
std::unique_ptr<NonThreadSafeClass> non_thread_safe_class(
@@ -142,15 +145,21 @@ void NonThreadSafeClass::DestructorOnDifferentThreadImpl() {
delete_on_thread.Join();
}
-#if DCHECK_IS_ON()
+#if ENABLE_NON_THREAD_SAFE
TEST(NonThreadSafeDeathTest, DestructorNotAllowedOnDifferentThreadInDebug) {
- ASSERT_DCHECK_DEATH(
- { NonThreadSafeClass::DestructorOnDifferentThreadImpl(); });
+ ASSERT_DEATH({
+ NonThreadSafeClass::DestructorOnDifferentThreadImpl();
+ }, "");
}
#else
TEST(NonThreadSafeTest, DestructorAllowedOnDifferentThreadInRelease) {
NonThreadSafeClass::DestructorOnDifferentThreadImpl();
}
-#endif // DCHECK_IS_ON()
+#endif // ENABLE_NON_THREAD_SAFE
+
+#endif // GTEST_HAS_DEATH_TEST || !ENABLE_NON_THREAD_SAFE
+
+// Just in case we ever get lumped together with other compilation units.
+#undef ENABLE_NON_THREAD_SAFE
} // namespace base
diff --git a/base/threading/platform_thread.h b/base/threading/platform_thread.h
index 8c0d8e4432..9b217a9c65 100644
--- a/base/threading/platform_thread.h
+++ b/base/threading/platform_thread.h
@@ -18,8 +18,6 @@
#if defined(OS_WIN)
#include <windows.h>
-#elif defined(OS_MACOSX)
-#include <mach/mach_types.h>
#elif defined(OS_POSIX)
#include <pthread.h>
#include <unistd.h>
@@ -30,8 +28,6 @@ namespace base {
// Used for logging. Always an integer value.
#if defined(OS_WIN)
typedef DWORD PlatformThreadId;
-#elif defined(OS_MACOSX)
-typedef mach_port_t PlatformThreadId;
#elif defined(OS_POSIX)
typedef pid_t PlatformThreadId;
#endif
@@ -63,8 +59,6 @@ class PlatformThreadRef {
return id_ == other.id_;
}
- bool operator!=(PlatformThreadRef other) const { return id_ != other.id_; }
-
bool is_null() const {
return id_ == 0;
}
@@ -181,12 +175,6 @@ class BASE_EXPORT PlatformThread {
// PlatformThreadHandle.
static bool CreateNonJoinable(size_t stack_size, Delegate* delegate);
- // CreateNonJoinableWithPriority() does the same thing as CreateNonJoinable()
- // except the priority of the thread is set based on |priority|.
- static bool CreateNonJoinableWithPriority(size_t stack_size,
- Delegate* delegate,
- ThreadPriority priority);
-
// Joins with a thread created via the Create function. This function blocks
// the caller until the designated thread exits. This will invalidate
// |thread_handle|.
@@ -196,10 +184,6 @@ class BASE_EXPORT PlatformThread {
// and |thread_handle| is invalidated after this call.
static void Detach(PlatformThreadHandle thread_handle);
- // Returns true if SetCurrentThreadPriority() can be used to increase the
- // priority of the current thread.
- static bool CanIncreaseCurrentThreadPriority();
-
// Toggles the current thread's priority at runtime. A thread may not be able
// to raise its priority back up after lowering it if the process does not
// have a proper permission, e.g. CAP_SYS_NICE on Linux. A thread may not be
@@ -211,20 +195,6 @@ class BASE_EXPORT PlatformThread {
static ThreadPriority GetCurrentThreadPriority();
-#if defined(OS_LINUX)
- // Toggles a specific thread's priority at runtime. This can be used to
- // change the priority of a thread in a different process and will fail
- // if the calling process does not have proper permissions. The
- // SetCurrentThreadPriority() function above is preferred in favor of
- // security but on platforms where sandboxed processes are not allowed to
- // change priority this function exists to allow a non-sandboxed process
- // to change the priority of sandboxed threads for improved performance.
- // Warning: Don't use this for a main thread because that will change the
- // whole thread group's (i.e. process) priority.
- static void SetThreadPriority(PlatformThreadId thread_id,
- ThreadPriority priority);
-#endif
-
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformThread);
};
diff --git a/base/threading/platform_thread_linux.cc b/base/threading/platform_thread_linux.cc
index 474410f18a..ab7c97ef51 100644
--- a/base/threading/platform_thread_linux.cc
+++ b/base/threading/platform_thread_linux.cc
@@ -8,10 +8,8 @@
#include <sched.h>
#include <stddef.h>
-#include "base/files/file_util.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
-#include "base/strings/string_number_conversions.h"
#include "base/threading/platform_thread_internal_posix.h"
#include "base/threading/thread_id_name_manager.h"
#include "base/tracked_objects.h"
@@ -20,68 +18,11 @@
#if !defined(OS_NACL)
#include <pthread.h>
#include <sys/prctl.h>
-#include <sys/resource.h>
-#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#endif
namespace base {
-namespace {
-#if !defined(OS_NACL)
-const FilePath::CharType kCgroupDirectory[] =
- FILE_PATH_LITERAL("/sys/fs/cgroup");
-
-FilePath ThreadPriorityToCgroupDirectory(const FilePath& cgroup_filepath,
- ThreadPriority priority) {
- switch (priority) {
- case ThreadPriority::NORMAL:
- return cgroup_filepath;
- case ThreadPriority::BACKGROUND:
- return cgroup_filepath.Append(FILE_PATH_LITERAL("non-urgent"));
- case ThreadPriority::DISPLAY:
- case ThreadPriority::REALTIME_AUDIO:
- return cgroup_filepath.Append(FILE_PATH_LITERAL("urgent"));
- }
- NOTREACHED();
- return FilePath();
-}
-
-void SetThreadCgroup(PlatformThreadId thread_id,
- const FilePath& cgroup_directory) {
- FilePath tasks_filepath = cgroup_directory.Append(FILE_PATH_LITERAL("tasks"));
- std::string tid = IntToString(thread_id);
- int bytes_written = WriteFile(tasks_filepath, tid.c_str(), tid.size());
- if (bytes_written != static_cast<int>(tid.size())) {
- DVLOG(1) << "Failed to add " << tid << " to " << tasks_filepath.value();
- }
-}
-
-void SetThreadCgroupForThreadPriority(PlatformThreadId thread_id,
- const FilePath& cgroup_filepath,
- ThreadPriority priority) {
- // Append "chrome" suffix.
- FilePath cgroup_directory = ThreadPriorityToCgroupDirectory(
- cgroup_filepath.Append(FILE_PATH_LITERAL("chrome")), priority);
-
- // Silently ignore request if cgroup directory doesn't exist.
- if (!DirectoryExists(cgroup_directory))
- return;
-
- SetThreadCgroup(thread_id, cgroup_directory);
-}
-
-void SetThreadCgroupsForThreadPriority(PlatformThreadId thread_id,
- ThreadPriority priority) {
- FilePath cgroup_filepath(kCgroupDirectory);
- SetThreadCgroupForThreadPriority(
- thread_id, cgroup_filepath.Append(FILE_PATH_LITERAL("cpuset")), priority);
- SetThreadCgroupForThreadPriority(
- thread_id, cgroup_filepath.Append(FILE_PATH_LITERAL("schedtune")),
- priority);
-}
-#endif
-} // namespace
namespace internal {
@@ -100,7 +41,6 @@ const ThreadPriorityToNiceValuePair kThreadPriorityToNiceValueMap[4] = {
bool SetCurrentThreadPriorityForPlatform(ThreadPriority priority) {
#if !defined(OS_NACL)
- SetThreadCgroupsForThreadPriority(PlatformThread::CurrentId(), priority);
return priority == ThreadPriority::REALTIME_AUDIO &&
pthread_setschedparam(pthread_self(), SCHED_RR, &kRealTimePrio) == 0;
#else
@@ -150,25 +90,6 @@ void PlatformThread::SetName(const std::string& name) {
#endif // !defined(OS_NACL)
}
-#if !defined(OS_NACL)
-// static
-void PlatformThread::SetThreadPriority(PlatformThreadId thread_id,
- ThreadPriority priority) {
- // Changing current main threads' priority is not permitted in favor of
- // security, this interface is restricted to change only non-main thread
- // priority.
- CHECK_NE(thread_id, getpid());
-
- SetThreadCgroupsForThreadPriority(thread_id, priority);
-
- const int nice_setting = internal::ThreadPriorityToNiceValue(priority);
- if (setpriority(PRIO_PROCESS, thread_id, nice_setting)) {
- DVPLOG(1) << "Failed to set nice value of thread (" << thread_id << ") to "
- << nice_setting;
- }
-}
-#endif // !defined(OS_NACL)
-
void InitThreading() {}
void TerminateOnThread() {}
diff --git a/base/threading/platform_thread_mac.mm b/base/threading/platform_thread_mac.mm
index e743044ec1..51f3621af2 100644
--- a/base/threading/platform_thread_mac.mm
+++ b/base/threading/platform_thread_mac.mm
@@ -162,11 +162,6 @@ void SetPriorityRealtimeAudio(mach_port_t mach_thread_id) {
} // anonymous namespace
// static
-bool PlatformThread::CanIncreaseCurrentThreadPriority() {
- return true;
-}
-
-// static
void PlatformThread::SetCurrentThreadPriority(ThreadPriority priority) {
// Convert from pthread_t to mach thread identifier.
mach_port_t mach_thread_id =
diff --git a/base/threading/platform_thread_posix.cc b/base/threading/platform_thread_posix.cc
index 9a6a2bb999..2321b3cd49 100644
--- a/base/threading/platform_thread_posix.cc
+++ b/base/threading/platform_thread_posix.cc
@@ -11,12 +11,9 @@
#include <stdint.h>
#include <sys/resource.h>
#include <sys/time.h>
-#include <sys/types.h>
-#include <unistd.h>
#include <memory>
-#include "base/debug/activity_tracker.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/threading/platform_thread_internal_posix.h"
@@ -26,6 +23,8 @@
#if defined(OS_LINUX)
#include <sys/syscall.h>
+#elif defined(OS_ANDROID)
+#include <sys/types.h>
#endif
namespace base {
@@ -188,32 +187,21 @@ const char* PlatformThread::GetName() {
bool PlatformThread::CreateWithPriority(size_t stack_size, Delegate* delegate,
PlatformThreadHandle* thread_handle,
ThreadPriority priority) {
- return CreateThread(stack_size, true /* joinable thread */, delegate,
- thread_handle, priority);
+ return CreateThread(stack_size, true, // joinable thread
+ delegate, thread_handle, priority);
}
// static
bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) {
- return CreateNonJoinableWithPriority(stack_size, delegate,
- ThreadPriority::NORMAL);
-}
-
-// static
-bool PlatformThread::CreateNonJoinableWithPriority(size_t stack_size,
- Delegate* delegate,
- ThreadPriority priority) {
PlatformThreadHandle unused;
bool result = CreateThread(stack_size, false /* non-joinable thread */,
- delegate, &unused, priority);
+ delegate, &unused, ThreadPriority::NORMAL);
return result;
}
// static
void PlatformThread::Join(PlatformThreadHandle thread_handle) {
- // Record the event that this thread is blocking upon (for hang diagnosis).
- base::debug::ScopedThreadJoinActivity thread_activity(&thread_handle);
-
// Joining another thread may block the current thread for a long time, since
// the thread referred to by |thread_handle| may still be running long-lived /
// blocking tasks.
@@ -230,18 +218,6 @@ void PlatformThread::Detach(PlatformThreadHandle thread_handle) {
#if !defined(OS_MACOSX)
// static
-bool PlatformThread::CanIncreaseCurrentThreadPriority() {
-#if defined(OS_NACL)
- return false;
-#else
- // Only root can raise thread priority on POSIX environment. On Linux, users
- // who have CAP_SYS_NICE permission also can raise the thread priority, but
- // libcap.so would be needed to check the capability.
- return geteuid() == 0;
-#endif // defined(OS_NACL)
-}
-
-// static
void PlatformThread::SetCurrentThreadPriority(ThreadPriority priority) {
#if defined(OS_NACL)
NOTIMPLEMENTED();
diff --git a/base/threading/platform_thread_unittest.cc b/base/threading/platform_thread_unittest.cc
index 0febf8ba9b..2d99ed8750 100644
--- a/base/threading/platform_thread_unittest.cc
+++ b/base/threading/platform_thread_unittest.cc
@@ -12,6 +12,8 @@
#include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_POSIX)
+#include <sys/types.h>
+#include <unistd.h>
#include "base/threading/platform_thread_internal_posix.h"
#elif defined(OS_WIN)
#include <windows.h>
@@ -233,6 +235,17 @@ const ThreadPriority kThreadPriorityTestValues[] = {
ThreadPriority::NORMAL,
ThreadPriority::BACKGROUND};
+bool IsBumpingPriorityAllowed() {
+#if defined(OS_POSIX)
+ // Only root can raise thread priority on POSIX environment. On Linux, users
+ // who have CAP_SYS_NICE permission also can raise the thread priority, but
+ // libcap.so would be needed to check the capability.
+ return geteuid() == 0;
+#else
+ return true;
+#endif
+}
+
class ThreadPriorityTestThread : public FunctionTestThread {
public:
explicit ThreadPriorityTestThread(ThreadPriority priority)
@@ -260,9 +273,8 @@ class ThreadPriorityTestThread : public FunctionTestThread {
// Test changing a created thread's priority (which has different semantics on
// some platforms).
TEST(PlatformThreadTest, ThreadPriorityCurrentThread) {
- const bool increase_priority_allowed =
- PlatformThread::CanIncreaseCurrentThreadPriority();
- if (increase_priority_allowed) {
+ const bool bumping_priority_allowed = IsBumpingPriorityAllowed();
+ if (bumping_priority_allowed) {
// Bump the priority in order to verify that new threads are started with
// normal priority.
PlatformThread::SetCurrentThreadPriority(ThreadPriority::DISPLAY);
@@ -270,7 +282,7 @@ TEST(PlatformThreadTest, ThreadPriorityCurrentThread) {
// Toggle each supported priority on the thread and confirm it affects it.
for (size_t i = 0; i < arraysize(kThreadPriorityTestValues); ++i) {
- if (!increase_priority_allowed &&
+ if (!bumping_priority_allowed &&
kThreadPriorityTestValues[i] >
PlatformThread::GetCurrentThreadPriority()) {
continue;
diff --git a/base/threading/post_task_and_reply_impl.cc b/base/threading/post_task_and_reply_impl.cc
index d16f8bd225..c906866cfb 100644
--- a/base/threading/post_task_and_reply_impl.cc
+++ b/base/threading/post_task_and_reply_impl.cc
@@ -4,46 +4,42 @@
#include "base/threading/post_task_and_reply_impl.h"
-#include <utility>
-
#include "base/bind.h"
-#include "base/debug/leak_annotations.h"
-#include "base/logging.h"
-#include "base/memory/ref_counted.h"
-#include "base/sequence_checker.h"
-#include "base/sequenced_task_runner.h"
-#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
namespace base {
namespace {
-// This relay class remembers the sequence that it was created on, and ensures
-// that both the |task| and |reply| Closures are deleted on this same sequence.
-// Also, |task| is guaranteed to be deleted before |reply| is run or deleted.
+// This relay class remembers the MessageLoop that it was created on, and
+// ensures that both the |task| and |reply| Closures are deleted on this same
+// thread. Also, |task| is guaranteed to be deleted before |reply| is run or
+// deleted.
//
-// If RunReplyAndSelfDestruct() doesn't run because the originating execution
-// context is no longer available, then the |task| and |reply| Closures are
-// leaked. Leaking is considered preferable to having a thread-safetey
-// violations caused by invoking the Closure destructor on the wrong sequence.
+// If this is not possible because the originating MessageLoop is no longer
+// available, the the |task| and |reply| Closures are leaked. Leaking is
+// considered preferable to having a thread-safetey violations caused by
+// invoking the Closure destructor on the wrong thread.
class PostTaskAndReplyRelay {
public:
PostTaskAndReplyRelay(const tracked_objects::Location& from_here,
- Closure task,
- Closure reply)
- : sequence_checker_(),
- from_here_(from_here),
- origin_task_runner_(SequencedTaskRunnerHandle::Get()),
- reply_(std::move(reply)),
- task_(std::move(task)) {}
+ const Closure& task,
+ const Closure& reply)
+ : from_here_(from_here),
+ origin_task_runner_(ThreadTaskRunnerHandle::Get()) {
+ task_ = task;
+ reply_ = reply;
+ }
~PostTaskAndReplyRelay() {
- DCHECK(sequence_checker_.CalledOnValidSequence());
+ DCHECK(origin_task_runner_->BelongsToCurrentThread());
task_.Reset();
reply_.Reset();
}
- void RunTaskAndPostReply() {
+ void Run() {
task_.Run();
origin_task_runner_->PostTask(
from_here_, Bind(&PostTaskAndReplyRelay::RunReplyAndSelfDestruct,
@@ -52,7 +48,7 @@ class PostTaskAndReplyRelay {
private:
void RunReplyAndSelfDestruct() {
- DCHECK(sequence_checker_.CalledOnValidSequence());
+ DCHECK(origin_task_runner_->BelongsToCurrentThread());
// Force |task_| to be released before |reply_| is to ensure that no one
// accidentally depends on |task_| keeping one of its arguments alive while
@@ -65,9 +61,8 @@ class PostTaskAndReplyRelay {
delete this;
}
- const SequenceChecker sequence_checker_;
- const tracked_objects::Location from_here_;
- const scoped_refptr<SequencedTaskRunner> origin_task_runner_;
+ tracked_objects::Location from_here_;
+ scoped_refptr<SingleThreadTaskRunner> origin_task_runner_;
Closure reply_;
Closure task_;
};
@@ -78,19 +73,14 @@ namespace internal {
bool PostTaskAndReplyImpl::PostTaskAndReply(
const tracked_objects::Location& from_here,
- Closure task,
- Closure reply) {
- DCHECK(!task.is_null()) << from_here.ToString();
- DCHECK(!reply.is_null()) << from_here.ToString();
+ const Closure& task,
+ const Closure& reply) {
+ // TODO(tzik): Use DCHECK here once the crash is gone. http://crbug.com/541319
+ CHECK(!task.is_null()) << from_here.ToString();
+ CHECK(!reply.is_null()) << from_here.ToString();
PostTaskAndReplyRelay* relay =
- new PostTaskAndReplyRelay(from_here, std::move(task), std::move(reply));
- // PostTaskAndReplyRelay self-destructs after executing |reply|. On the flip
- // side though, it is intentionally leaked if the |task| doesn't complete
- // before the origin sequence stops executing tasks. Annotate |relay| as leaky
- // to avoid having to suppress every callsite which happens to flakily trigger
- // this race.
- ANNOTATE_LEAKING_OBJECT_PTR(relay);
- if (!PostTask(from_here, Bind(&PostTaskAndReplyRelay::RunTaskAndPostReply,
+ new PostTaskAndReplyRelay(from_here, task, reply);
+ if (!PostTask(from_here, Bind(&PostTaskAndReplyRelay::Run,
Unretained(relay)))) {
delete relay;
return false;
diff --git a/base/threading/post_task_and_reply_impl.h b/base/threading/post_task_and_reply_impl.h
index 696b668a4c..d21ab78de8 100644
--- a/base/threading/post_task_and_reply_impl.h
+++ b/base/threading/post_task_and_reply_impl.h
@@ -8,29 +8,30 @@
#ifndef BASE_THREADING_POST_TASK_AND_REPLY_IMPL_H_
#define BASE_THREADING_POST_TASK_AND_REPLY_IMPL_H_
-#include "base/base_export.h"
-#include "base/callback.h"
+#include "base/callback_forward.h"
#include "base/location.h"
namespace base {
namespace internal {
-// Inherit from this in a class that implements PostTask to send a task to a
-// custom execution context.
+// Inherit from this in a class that implements PostTask appropriately
+// for sending to a destination thread.
//
-// If you're looking for a concrete implementation of PostTaskAndReply, you
-// probably want base::TaskRunner, or you may want base::WorkerPool.
-class BASE_EXPORT PostTaskAndReplyImpl {
+// Note that 'reply' will always get posted back to your current
+// MessageLoop.
+//
+// If you're looking for a concrete implementation of
+// PostTaskAndReply, you probably want base::SingleThreadTaskRunner, or you
+// may want base::WorkerPool.
+class PostTaskAndReplyImpl {
public:
virtual ~PostTaskAndReplyImpl() = default;
- // Posts |task| by calling PostTask(). On completion, |reply| is posted to the
- // sequence or thread that called this. Can only be called when
- // SequencedTaskRunnerHandle::IsSet(). Both |task| and |reply| are guaranteed
- // to be deleted on the sequence or thread that called this.
+ // Implementation for TaskRunner::PostTaskAndReply and
+ // WorkerPool::PostTaskAndReply.
bool PostTaskAndReply(const tracked_objects::Location& from_here,
- Closure task,
- Closure reply);
+ const Closure& task,
+ const Closure& reply);
private:
virtual bool PostTask(const tracked_objects::Location& from_here,
diff --git a/base/threading/sequenced_task_runner_handle.cc b/base/threading/sequenced_task_runner_handle.cc
index 90f68b33ab..88b36a8d64 100644
--- a/base/threading/sequenced_task_runner_handle.cc
+++ b/base/threading/sequenced_task_runner_handle.cc
@@ -16,56 +16,39 @@ namespace base {
namespace {
-LazyInstance<ThreadLocalPointer<SequencedTaskRunnerHandle>>::Leaky
+base::LazyInstance<base::ThreadLocalPointer<SequencedTaskRunnerHandle>>::Leaky
lazy_tls_ptr = LAZY_INSTANCE_INITIALIZER;
} // namespace
// static
scoped_refptr<SequencedTaskRunner> SequencedTaskRunnerHandle::Get() {
- // Return the registered SingleThreadTaskRunner, if any. This must be at the
- // top so that a SingleThreadTaskRunner has priority over a
- // SequencedTaskRunner (RLZ registers both on the same thread despite that
- // being prevented by DCHECKs).
- // TODO(fdoray): Move this to the bottom once RLZ stops registering a
- // SingleThreadTaskRunner and a SequencedTaskRunner on the same thread.
- // https://crbug.com/618530#c14
- if (ThreadTaskRunnerHandle::IsSet()) {
- // Various modes of setting SequencedTaskRunnerHandle don't combine.
- DCHECK(!lazy_tls_ptr.Pointer()->Get());
- DCHECK(!SequencedWorkerPool::GetSequenceTokenForCurrentThread().IsValid());
-
- return ThreadTaskRunnerHandle::Get();
- }
-
// Return the registered SequencedTaskRunner, if any.
const SequencedTaskRunnerHandle* handle = lazy_tls_ptr.Pointer()->Get();
if (handle) {
// Various modes of setting SequencedTaskRunnerHandle don't combine.
- DCHECK(!SequencedWorkerPool::GetSequenceTokenForCurrentThread().IsValid());
-
+ DCHECK(!base::ThreadTaskRunnerHandle::IsSet());
+ DCHECK(!SequencedWorkerPool::GetSequencedTaskRunnerForCurrentThread());
return handle->task_runner_;
}
- // If we are on a worker thread for a SequencedBlockingPool that is running a
- // sequenced task, return a SequencedTaskRunner for it.
- scoped_refptr<SequencedWorkerPool> pool =
- SequencedWorkerPool::GetWorkerPoolForCurrentThread();
- DCHECK(pool);
- SequencedWorkerPool::SequenceToken sequence_token =
- SequencedWorkerPool::GetSequenceTokenForCurrentThread();
- DCHECK(sequence_token.IsValid());
- scoped_refptr<SequencedTaskRunner> sequenced_task_runner(
- pool->GetSequencedTaskRunner(sequence_token));
- DCHECK(sequenced_task_runner->RunsTasksOnCurrentThread());
- return sequenced_task_runner;
+ // Return the SequencedTaskRunner obtained from SequencedWorkerPool, if any.
+ scoped_refptr<base::SequencedTaskRunner> task_runner =
+ SequencedWorkerPool::GetSequencedTaskRunnerForCurrentThread();
+ if (task_runner) {
+ DCHECK(!base::ThreadTaskRunnerHandle::IsSet());
+ return task_runner;
+ }
+
+ // Return the SingleThreadTaskRunner for the current thread otherwise.
+ return base::ThreadTaskRunnerHandle::Get();
}
// static
bool SequencedTaskRunnerHandle::IsSet() {
return lazy_tls_ptr.Pointer()->Get() ||
- SequencedWorkerPool::GetSequenceTokenForCurrentThread().IsValid() ||
- ThreadTaskRunnerHandle::IsSet();
+ SequencedWorkerPool::GetWorkerPoolForCurrentThread() ||
+ base::ThreadTaskRunnerHandle::IsSet();
}
SequencedTaskRunnerHandle::SequencedTaskRunnerHandle(
diff --git a/base/threading/sequenced_task_runner_handle.h b/base/threading/sequenced_task_runner_handle.h
index b7f4bae8aa..e6dec1e9f8 100644
--- a/base/threading/sequenced_task_runner_handle.h
+++ b/base/threading/sequenced_task_runner_handle.h
@@ -25,9 +25,8 @@ class BASE_EXPORT SequencedTaskRunnerHandle {
// instantiating a SequencedTaskRunnerHandle.
// b) The current thread has a ThreadTaskRunnerHandle (which includes any
// thread that has a MessageLoop associated with it), or
- // c) The current thread is a worker thread belonging to a SequencedWorkerPool
- // *and* is currently running a sequenced task (note: not supporting
- // unsequenced tasks is intentional: https://crbug.com/618043#c4).
+ // c) The current thread is a worker thread belonging to a
+ // SequencedWorkerPool.
static bool IsSet();
// Binds |task_runner| to the current thread.
diff --git a/base/threading/sequenced_worker_pool.cc b/base/threading/sequenced_worker_pool.cc
index ce594cd7fb..57961b5cd5 100644
--- a/base/threading/sequenced_worker_pool.cc
+++ b/base/threading/sequenced_worker_pool.cc
@@ -10,7 +10,6 @@
#include <map>
#include <memory>
#include <set>
-#include <unordered_map>
#include <utility>
#include <vector>
@@ -18,7 +17,6 @@
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/critical_closure.h"
-#include "base/debug/dump_without_crashing.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/macros.h"
@@ -27,21 +25,15 @@
#include "base/strings/stringprintf.h"
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
-// Don't enable the redirect to TaskScheduler on Arc++ to avoid pulling a bunch
-// of dependencies. Some code also #ifdef'ed below.
-#if 0
-#include "base/task_scheduler/post_task.h"
-#include "base/task_scheduler/task_scheduler.h"
-#endif
#include "base/threading/platform_thread.h"
-#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/simple_thread.h"
#include "base/threading/thread_local.h"
#include "base/threading/thread_restrictions.h"
+#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
+#include "base/trace_event/heap_profiler.h"
#include "base/trace_event/trace_event.h"
#include "base/tracked_objects.h"
-#include "base/tracking_info.h"
#include "build/build_config.h"
#if defined(OS_MACOSX)
@@ -51,36 +43,13 @@
#endif
#if !defined(OS_NACL)
-#include "base/metrics/histogram_macros.h"
+#include "base/metrics/histogram.h"
#endif
namespace base {
namespace {
-// An enum representing the state of all pools. A non-test process should only
-// ever transition from POST_TASK_DISABLED to one of the active states. A test
-// process may transition from one of the active states to POST_TASK_DISABLED
-// when DisableForProcessForTesting() is called.
-//
-// External memory synchronization is required to call a method that reads
-// |g_all_pools_state| after calling a method that modifies it.
-//
-// TODO(gab): Remove this if http://crbug.com/622400 fails (SequencedWorkerPool
-// will be phased out completely otherwise).
-enum class AllPoolsState {
- POST_TASK_DISABLED,
- USE_WORKER_POOL,
- REDIRECTED_TO_TASK_SCHEDULER,
-};
-
-// TODO(fdoray): Change the initial state to POST_TASK_DISABLED. It is initially
-// USE_WORKER_POOL to avoid a revert of the CL that adds
-// debug::DumpWithoutCrashing() in case of waterfall failures.
-AllPoolsState g_all_pools_state = AllPoolsState::USE_WORKER_POOL;
-
-TaskPriority g_max_task_priority = TaskPriority::HIGHEST;
-
struct SequencedTask : public TrackingInfo {
SequencedTask()
: sequence_token_id(0),
@@ -123,14 +92,6 @@ struct SequencedTaskLessThan {
}
};
-// Create a process-wide unique ID to represent this task in trace events. This
-// will be mangled with a Process ID hash to reduce the likelyhood of colliding
-// with MessageLoop pointers on other processes.
-uint64_t GetTaskTraceID(const SequencedTask& task, void* pool) {
- return (static_cast<uint64_t>(task.trace_id) << 32) |
- static_cast<uint64_t>(reinterpret_cast<intptr_t>(pool));
-}
-
// SequencedWorkerPoolTaskRunner ---------------------------------------------
// A TaskRunner which posts tasks to a SequencedWorkerPool with a
// fixed ShutdownBehavior.
@@ -181,17 +142,14 @@ bool SequencedWorkerPoolTaskRunner::RunsTasksOnCurrentThread() const {
return pool_->RunsTasksOnCurrentThread();
}
-} // namespace
-
-// SequencedWorkerPool::PoolSequencedTaskRunner ------------------------------
+// SequencedWorkerPoolSequencedTaskRunner ------------------------------------
// A SequencedTaskRunner which posts tasks to a SequencedWorkerPool with a
// fixed sequence token.
//
// Note that this class is RefCountedThreadSafe (inherited from TaskRunner).
-class SequencedWorkerPool::PoolSequencedTaskRunner
- : public SequencedTaskRunner {
+class SequencedWorkerPoolSequencedTaskRunner : public SequencedTaskRunner {
public:
- PoolSequencedTaskRunner(
+ SequencedWorkerPoolSequencedTaskRunner(
scoped_refptr<SequencedWorkerPool> pool,
SequencedWorkerPool::SequenceToken token,
SequencedWorkerPool::WorkerShutdown shutdown_behavior);
@@ -208,7 +166,7 @@ class SequencedWorkerPool::PoolSequencedTaskRunner
TimeDelta delay) override;
private:
- ~PoolSequencedTaskRunner() override;
+ ~SequencedWorkerPoolSequencedTaskRunner() override;
const scoped_refptr<SequencedWorkerPool> pool_;
@@ -216,25 +174,25 @@ class SequencedWorkerPool::PoolSequencedTaskRunner
const SequencedWorkerPool::WorkerShutdown shutdown_behavior_;
- DISALLOW_COPY_AND_ASSIGN(PoolSequencedTaskRunner);
+ DISALLOW_COPY_AND_ASSIGN(SequencedWorkerPoolSequencedTaskRunner);
};
-SequencedWorkerPool::PoolSequencedTaskRunner::
- PoolSequencedTaskRunner(
- scoped_refptr<SequencedWorkerPool> pool,
- SequencedWorkerPool::SequenceToken token,
- SequencedWorkerPool::WorkerShutdown shutdown_behavior)
+SequencedWorkerPoolSequencedTaskRunner::SequencedWorkerPoolSequencedTaskRunner(
+ scoped_refptr<SequencedWorkerPool> pool,
+ SequencedWorkerPool::SequenceToken token,
+ SequencedWorkerPool::WorkerShutdown shutdown_behavior)
: pool_(std::move(pool)),
token_(token),
shutdown_behavior_(shutdown_behavior) {}
-SequencedWorkerPool::PoolSequencedTaskRunner::
- ~PoolSequencedTaskRunner() = default;
+SequencedWorkerPoolSequencedTaskRunner::
+~SequencedWorkerPoolSequencedTaskRunner() {
+}
-bool SequencedWorkerPool::PoolSequencedTaskRunner::
- PostDelayedTask(const tracked_objects::Location& from_here,
- const Closure& task,
- TimeDelta delay) {
+bool SequencedWorkerPoolSequencedTaskRunner::PostDelayedTask(
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay) {
if (delay.is_zero()) {
return pool_->PostSequencedWorkerTaskWithShutdownBehavior(
token_, from_here, task, shutdown_behavior_);
@@ -242,20 +200,29 @@ bool SequencedWorkerPool::PoolSequencedTaskRunner::
return pool_->PostDelayedSequencedWorkerTask(token_, from_here, task, delay);
}
-bool SequencedWorkerPool::PoolSequencedTaskRunner::
- RunsTasksOnCurrentThread() const {
+bool SequencedWorkerPoolSequencedTaskRunner::RunsTasksOnCurrentThread() const {
return pool_->IsRunningSequenceOnCurrentThread(token_);
}
-bool SequencedWorkerPool::PoolSequencedTaskRunner::
- PostNonNestableDelayedTask(const tracked_objects::Location& from_here,
- const Closure& task,
- TimeDelta delay) {
+bool SequencedWorkerPoolSequencedTaskRunner::PostNonNestableDelayedTask(
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay) {
// There's no way to run nested tasks, so simply forward to
// PostDelayedTask.
return PostDelayedTask(from_here, task, delay);
}
+// Create a process-wide unique ID to represent this task in trace events. This
+// will be mangled with a Process ID hash to reduce the likelyhood of colliding
+// with MessageLoop pointers on other processes.
+uint64_t GetTaskTraceID(const SequencedTask& task, void* pool) {
+ return (static_cast<uint64_t>(task.trace_id) << 32) |
+ static_cast<uint64_t>(reinterpret_cast<intptr_t>(pool));
+}
+
+} // namespace
+
// Worker ---------------------------------------------------------------------
class SequencedWorkerPool::Worker : public SimpleThread {
@@ -280,14 +247,6 @@ class SequencedWorkerPool::Worker : public SimpleThread {
is_processing_task_ = true;
task_sequence_token_ = token;
task_shutdown_behavior_ = shutdown_behavior;
-
- // It is dangerous for tasks with CONTINUE_ON_SHUTDOWN to access a class
- // that implements a non-leaky base::Singleton because they are generally
- // destroyed before the process terminates via an AtExitManager
- // registration. This will trigger a DCHECK to warn of such cases. See the
- // comment about CONTINUE_ON_SHUTDOWN for more details.
- ThreadRestrictions::SetSingletonAllowed(task_shutdown_behavior_ !=
- CONTINUE_ON_SHUTDOWN);
}
// Indicates that the task has finished running.
@@ -333,10 +292,8 @@ class SequencedWorkerPool::Inner {
public:
// Take a raw pointer to |worker| to avoid cycles (since we're owned
// by it).
- Inner(SequencedWorkerPool* worker_pool,
- size_t max_threads,
+ Inner(SequencedWorkerPool* worker_pool, size_t max_threads,
const std::string& thread_name_prefix,
- base::TaskPriority task_priority,
TestingObserver* observer);
~Inner();
@@ -359,6 +316,11 @@ class SequencedWorkerPool::Inner {
bool IsRunningSequenceOnCurrentThread(SequenceToken sequence_token) const;
+ bool IsRunningSequence(SequenceToken sequence_token) const;
+
+ void SetRunningTaskInfoForCurrentThread(SequenceToken sequence_token,
+ WorkerShutdown shutdown_behavior);
+
void CleanupForTesting();
void SignalHasWorkForTesting();
@@ -387,25 +349,6 @@ class SequencedWorkerPool::Inner {
CLEANUP_DONE,
};
- // Clears ScheduledTasks in |tasks_to_delete| while ensuring that
- // |this_worker| has the desired task info context during ~ScheduledTask() to
- // allow sequence-checking.
- void DeleteWithoutLock(std::vector<SequencedTask>* tasks_to_delete,
- Worker* this_worker);
-
- // Helper used by PostTask() to complete the work when redirection is on.
- // Returns true if the task may run at some point in the future and false if
- // it will definitely not run.
- // Coalesce upon resolution of http://crbug.com/622400.
- bool PostTaskToTaskScheduler(const SequencedTask& sequenced,
- const TimeDelta& delay);
-
- // Returns the TaskScheduler TaskRunner for the specified |sequence_token_id|
- // and |traits|.
- scoped_refptr<TaskRunner> GetTaskSchedulerTaskRunner(
- int sequence_token_id,
- const TaskTraits& traits);
-
// Called from within the lock, this converts the given token name into a
// token ID, creating a new one if necessary.
int LockedGetNamedTokenID(const std::string& name);
@@ -430,7 +373,7 @@ class SequencedWorkerPool::Inner {
// See the implementation for a more detailed description.
GetWorkStatus GetWork(SequencedTask* task,
TimeDelta* wait_time,
- std::vector<SequencedTask>* delete_these_outside_lock);
+ std::vector<Closure>* delete_these_outside_lock);
void HandleCleanup();
@@ -454,7 +397,7 @@ class SequencedWorkerPool::Inner {
// 0 or more. The caller should then call FinishStartingAdditionalThread to
// complete initialization once the lock is released.
//
- // If another thread is not necessary, return 0;
+ // If another thread is not necessary, returne 0;
//
// See the implementedion for more.
int PrepareToStartAdditionalThreadIfHelpful();
@@ -554,27 +497,6 @@ class SequencedWorkerPool::Inner {
TestingObserver* const testing_observer_;
- // Members below are used for the experimental redirection to TaskScheduler.
- // TODO(gab): Remove these if http://crbug.com/622400 fails
- // (SequencedWorkerPool will be phased out completely otherwise).
-
- // The TaskPriority to be used for SequencedWorkerPool tasks redirected to the
- // TaskScheduler as an experiment (unused otherwise).
- const base::TaskPriority task_priority_;
-
- // A map of SequenceToken IDs to TaskScheduler TaskRunners used to redirect
- // sequenced tasks to the TaskScheduler.
- std::unordered_map<int, scoped_refptr<TaskRunner>> sequenced_task_runner_map_;
-
- // TaskScheduler TaskRunners to redirect unsequenced tasks to the
- // TaskScheduler. Indexed by TaskShutdownBehavior.
- scoped_refptr<TaskRunner> unsequenced_task_runners_[3];
-
- // A dummy TaskRunner obtained from TaskScheduler with the same TaskTraits as
- // used by this SequencedWorkerPool to query for RunsTasksOnCurrentThread().
- // Mutable so it can be lazily instantiated from RunsTasksOnCurrentThread().
- mutable scoped_refptr<TaskRunner> runs_tasks_on_verifier_;
-
DISALLOW_COPY_AND_ASSIGN(Inner);
};
@@ -588,7 +510,6 @@ SequencedWorkerPool::Worker::Worker(
worker_pool_(std::move(worker_pool)),
task_shutdown_behavior_(BLOCK_SHUTDOWN),
is_processing_task_(false) {
- DCHECK_EQ(AllPoolsState::USE_WORKER_POOL, g_all_pools_state);
Start();
}
@@ -596,8 +517,6 @@ SequencedWorkerPool::Worker::~Worker() {
}
void SequencedWorkerPool::Worker::Run() {
- DCHECK_EQ(AllPoolsState::USE_WORKER_POOL, g_all_pools_state);
-
#if defined(OS_WIN)
win::ScopedCOMInitializer com_initializer;
#endif
@@ -633,11 +552,11 @@ LazyInstance<ThreadLocalPointer<SequencedWorkerPool::Worker>>::Leaky
// Inner definitions ---------------------------------------------------------
-SequencedWorkerPool::Inner::Inner(SequencedWorkerPool* worker_pool,
- size_t max_threads,
- const std::string& thread_name_prefix,
- base::TaskPriority task_priority,
- TestingObserver* observer)
+SequencedWorkerPool::Inner::Inner(
+ SequencedWorkerPool* worker_pool,
+ size_t max_threads,
+ const std::string& thread_name_prefix,
+ TestingObserver* observer)
: worker_pool_(worker_pool),
lock_(),
has_work_cv_(&lock_),
@@ -655,13 +574,7 @@ SequencedWorkerPool::Inner::Inner(SequencedWorkerPool* worker_pool,
cleanup_state_(CLEANUP_DONE),
cleanup_idlers_(0),
cleanup_cv_(&lock_),
- testing_observer_(observer),
- task_priority_(static_cast<int>(task_priority) <=
- static_cast<int>(g_max_task_priority)
- ? task_priority
- : g_max_task_priority) {
- DCHECK_GT(max_threads_, 1U);
-}
+ testing_observer_(observer) {}
SequencedWorkerPool::Inner::~Inner() {
// You must call Shutdown() before destroying the pool.
@@ -698,13 +611,6 @@ bool SequencedWorkerPool::Inner::PostTask(
const tracked_objects::Location& from_here,
const Closure& task,
TimeDelta delay) {
- // TODO(fdoray): Uncomment this DCHECK. It is initially commented to avoid a
- // revert of the CL that adds debug::DumpWithoutCrashing() if it fails on the
- // waterfall. https://crbug.com/622400
- // DCHECK_NE(AllPoolsState::POST_TASK_DISABLED, g_all_pools_state);
- if (g_all_pools_state == AllPoolsState::POST_TASK_DISABLED)
- debug::DumpWithoutCrashing();
-
DCHECK(delay.is_zero() || shutdown_behavior == SKIP_ON_SHUTDOWN);
SequencedTask sequenced(from_here);
sequenced.sequence_token_id = sequence_token.id_;
@@ -718,7 +624,6 @@ bool SequencedWorkerPool::Inner::PostTask(
int create_thread_id = 0;
{
AutoLock lock(lock_);
-
if (shutdown_called_) {
// Don't allow a new task to be posted if it doesn't block shutdown.
if (shutdown_behavior != BLOCK_SHUTDOWN)
@@ -754,178 +659,64 @@ bool SequencedWorkerPool::Inner::PostTask(
if (optional_token_name)
sequenced.sequence_token_id = LockedGetNamedTokenID(*optional_token_name);
- // See on top of the file why we don't compile this on Arc++.
-#if 0
- if (g_all_pools_state == AllPoolsState::REDIRECTED_TO_TASK_SCHEDULER) {
- if (!PostTaskToTaskScheduler(sequenced, delay))
- return false;
- } else {
-#endif
- pending_tasks_.insert(sequenced);
+ pending_tasks_.insert(sequenced);
+ if (shutdown_behavior == BLOCK_SHUTDOWN)
+ blocking_shutdown_pending_task_count_++;
- if (sequenced.shutdown_behavior == BLOCK_SHUTDOWN)
- blocking_shutdown_pending_task_count_++;
-
- create_thread_id = PrepareToStartAdditionalThreadIfHelpful();
- }
-#if 0
- }
-#endif
-
- // Use != REDIRECTED_TO_TASK_SCHEDULER instead of == USE_WORKER_POOL to ensure
- // correct behavior if a task is posted to a SequencedWorkerPool before
- // Enable(WithRedirectionToTaskScheduler)ForProcess() in a non-DCHECK build.
- if (g_all_pools_state != AllPoolsState::REDIRECTED_TO_TASK_SCHEDULER) {
- // Actually start the additional thread or signal an existing one outside
- // the lock.
- if (create_thread_id)
- FinishStartingAdditionalThread(create_thread_id);
- else
- SignalHasWork();
+ create_thread_id = PrepareToStartAdditionalThreadIfHelpful();
}
-#if DCHECK_IS_ON()
- {
- AutoLock lock_for_dcheck(lock_);
- // Some variables are exposed in both modes for convenience but only really
- // intended for one of them at runtime, confirm exclusive usage here.
- if (g_all_pools_state == AllPoolsState::REDIRECTED_TO_TASK_SCHEDULER) {
- DCHECK(pending_tasks_.empty());
- DCHECK_EQ(0, create_thread_id);
- } else {
- DCHECK(sequenced_task_runner_map_.empty());
- }
- }
-#endif // DCHECK_IS_ON()
+ // Actually start the additional thread or signal an existing one now that
+ // we're outside the lock.
+ if (create_thread_id)
+ FinishStartingAdditionalThread(create_thread_id);
+ else
+ SignalHasWork();
return true;
}
-bool SequencedWorkerPool::Inner::PostTaskToTaskScheduler(
- const SequencedTask& sequenced,
- const TimeDelta& delay) {
-#if 1
- NOTREACHED();
- ALLOW_UNUSED_PARAM(sequenced);
- ALLOW_UNUSED_PARAM(delay);
- return false;
-#else
- DCHECK_EQ(AllPoolsState::REDIRECTED_TO_TASK_SCHEDULER, g_all_pools_state);
-
- lock_.AssertAcquired();
-
- // Confirm that the TaskScheduler's shutdown behaviors use the same
- // underlying values as SequencedWorkerPool.
- static_assert(
- static_cast<int>(TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN) ==
- static_cast<int>(CONTINUE_ON_SHUTDOWN),
- "TaskShutdownBehavior and WorkerShutdown enum mismatch for "
- "CONTINUE_ON_SHUTDOWN.");
- static_assert(static_cast<int>(TaskShutdownBehavior::SKIP_ON_SHUTDOWN) ==
- static_cast<int>(SKIP_ON_SHUTDOWN),
- "TaskShutdownBehavior and WorkerShutdown enum mismatch for "
- "SKIP_ON_SHUTDOWN.");
- static_assert(static_cast<int>(TaskShutdownBehavior::BLOCK_SHUTDOWN) ==
- static_cast<int>(BLOCK_SHUTDOWN),
- "TaskShutdownBehavior and WorkerShutdown enum mismatch for "
- "BLOCK_SHUTDOWN.");
-
- const TaskShutdownBehavior task_shutdown_behavior =
- static_cast<TaskShutdownBehavior>(sequenced.shutdown_behavior);
- const TaskTraits traits = TaskTraits()
- .MayBlock()
- .WithBaseSyncPrimitives()
- .WithPriority(task_priority_)
- .WithShutdownBehavior(task_shutdown_behavior);
- return GetTaskSchedulerTaskRunner(sequenced.sequence_token_id, traits)
- ->PostDelayedTask(sequenced.posted_from, sequenced.task, delay);
-#endif
-}
-
-scoped_refptr<TaskRunner>
-SequencedWorkerPool::Inner::GetTaskSchedulerTaskRunner(
- int sequence_token_id,
- const TaskTraits& traits) {
-#if 1
- NOTREACHED();
- ALLOW_UNUSED_PARAM(sequence_token_id);
- ALLOW_UNUSED_PARAM(traits);
- return scoped_refptr<TaskRunner>();
-#else
- DCHECK_EQ(AllPoolsState::REDIRECTED_TO_TASK_SCHEDULER, g_all_pools_state);
-
- lock_.AssertAcquired();
-
- static_assert(
- static_cast<int>(TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN) == 0,
- "TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN must be equal to 0 to be "
- "used as an index in |unsequenced_task_runners_|.");
- static_assert(static_cast<int>(TaskShutdownBehavior::SKIP_ON_SHUTDOWN) == 1,
- "TaskShutdownBehavior::SKIP_ON_SHUTDOWN must be equal to 1 to "
- "be used as an index in |unsequenced_task_runners_|.");
- static_assert(static_cast<int>(TaskShutdownBehavior::BLOCK_SHUTDOWN) == 2,
- "TaskShutdownBehavior::BLOCK_SHUTDOWN must be equal to 2 to be "
- "used as an index in |unsequenced_task_runners_|.");
- static_assert(arraysize(unsequenced_task_runners_) == 3,
- "The size of |unsequenced_task_runners_| doesn't match the "
- "number of shutdown behaviors.");
-
- scoped_refptr<TaskRunner>& task_runner =
- sequence_token_id ? sequenced_task_runner_map_[sequence_token_id]
- : unsequenced_task_runners_[static_cast<int>(
- traits.shutdown_behavior())];
-
- // TODO(fdoray): DCHECK that all tasks posted to the same sequence have the
- // same shutdown behavior.
-
- if (!task_runner) {
- task_runner = sequence_token_id
- ? CreateSequencedTaskRunnerWithTraits(traits)
- : CreateTaskRunnerWithTraits(traits);
- }
-
- return task_runner;
-#endif
-}
-
bool SequencedWorkerPool::Inner::RunsTasksOnCurrentThread() const {
AutoLock lock(lock_);
- if (g_all_pools_state == AllPoolsState::REDIRECTED_TO_TASK_SCHEDULER) {
-#if 0
- if (!runs_tasks_on_verifier_) {
- runs_tasks_on_verifier_ = CreateTaskRunnerWithTraits(
- TaskTraits().MayBlock().WithBaseSyncPrimitives().WithPriority(
- task_priority_));
- }
-#endif
- return runs_tasks_on_verifier_->RunsTasksOnCurrentThread();
- } else {
- return ContainsKey(threads_, PlatformThread::CurrentId());
- }
+ return ContainsKey(threads_, PlatformThread::CurrentId());
}
bool SequencedWorkerPool::Inner::IsRunningSequenceOnCurrentThread(
SequenceToken sequence_token) const {
+ AutoLock lock(lock_);
+ ThreadMap::const_iterator found = threads_.find(PlatformThread::CurrentId());
+ if (found == threads_.end())
+ return false;
+ return found->second->is_processing_task() &&
+ sequence_token.Equals(found->second->task_sequence_token());
+}
+
+bool SequencedWorkerPool::Inner::IsRunningSequence(
+ SequenceToken sequence_token) const {
DCHECK(sequence_token.IsValid());
+ AutoLock lock(lock_);
+ return !IsSequenceTokenRunnable(sequence_token.id_);
+}
+void SequencedWorkerPool::Inner::SetRunningTaskInfoForCurrentThread(
+ SequenceToken sequence_token,
+ WorkerShutdown shutdown_behavior) {
AutoLock lock(lock_);
+ ThreadMap::const_iterator found = threads_.find(PlatformThread::CurrentId());
+ DCHECK(found != threads_.end());
+ DCHECK(found->second->is_processing_task());
+ DCHECK(!found->second->task_sequence_token().IsValid());
+ found->second->set_running_task_info(sequence_token, shutdown_behavior);
- if (g_all_pools_state == AllPoolsState::REDIRECTED_TO_TASK_SCHEDULER) {
- const auto sequenced_task_runner_it =
- sequenced_task_runner_map_.find(sequence_token.id_);
- return sequenced_task_runner_it != sequenced_task_runner_map_.end() &&
- sequenced_task_runner_it->second->RunsTasksOnCurrentThread();
- } else {
- ThreadMap::const_iterator found =
- threads_.find(PlatformThread::CurrentId());
- return found != threads_.end() && found->second->is_processing_task() &&
- sequence_token.Equals(found->second->task_sequence_token());
- }
+ // Mark the sequence token as in use.
+ bool success = current_sequences_.insert(sequence_token.id_).second;
+ DCHECK(success);
}
// See https://code.google.com/p/chromium/issues/detail?id=168415
void SequencedWorkerPool::Inner::CleanupForTesting() {
- DCHECK_NE(g_all_pools_state, AllPoolsState::REDIRECTED_TO_TASK_SCHEDULER);
+ DCHECK(!RunsTasksOnCurrentThread());
+ base::ThreadRestrictions::ScopedAllowWait allow_wait;
AutoLock lock(lock_);
CHECK_EQ(CLEANUP_DONE, cleanup_state_);
if (shutdown_called_)
@@ -953,12 +744,8 @@ void SequencedWorkerPool::Inner::Shutdown(
if (shutdown_called_)
return;
shutdown_called_ = true;
-
max_blocking_tasks_after_shutdown_ = max_new_blocking_tasks_after_shutdown;
- if (g_all_pools_state != AllPoolsState::USE_WORKER_POOL)
- return;
-
// Tickle the threads. This will wake up a waiting one so it will know that
// it can exit, which in turn will wake up any other waiting ones.
SignalHasWork();
@@ -996,7 +783,6 @@ bool SequencedWorkerPool::Inner::IsShutdownInProgress() {
}
void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) {
- DCHECK_EQ(AllPoolsState::USE_WORKER_POOL, g_all_pools_state);
{
AutoLock lock(lock_);
DCHECK(thread_being_created_);
@@ -1015,15 +801,18 @@ void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) {
// See GetWork for what delete_these_outside_lock is doing.
SequencedTask task;
TimeDelta wait_time;
- std::vector<SequencedTask> delete_these_outside_lock;
+ std::vector<Closure> delete_these_outside_lock;
GetWorkStatus status =
GetWork(&task, &wait_time, &delete_these_outside_lock);
if (status == GET_WORK_FOUND) {
- TRACE_TASK_EXECUTION("SequencedWorkerPool::Inner::ThreadLoop", task);
- TRACE_EVENT_WITH_FLOW0(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"),
- "SequencedWorkerPool::Inner::PostTask",
+ TRACE_EVENT_WITH_FLOW2(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"),
+ "SequencedWorkerPool::Inner::ThreadLoop",
TRACE_ID_MANGLE(GetTaskTraceID(task, static_cast<void*>(this))),
- TRACE_EVENT_FLAG_FLOW_IN);
+ TRACE_EVENT_FLAG_FLOW_IN,
+ "src_file", task.posted_from.file_name(),
+ "src_func", task.posted_from.function_name());
+ TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION task_event(
+ task.posted_from.file_name());
int new_thread_id = WillRunWorkerTask(task);
{
AutoUnlock unlock(lock_);
@@ -1032,7 +821,7 @@ void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) {
// already get a signal for each new task, but it doesn't
// hurt.)
SignalHasWork();
- DeleteWithoutLock(&delete_these_outside_lock, this_worker);
+ delete_these_outside_lock.clear();
// Complete thread creation outside the lock if necessary.
if (new_thread_id)
@@ -1049,6 +838,11 @@ void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) {
tracked_objects::ThreadData::TallyRunOnNamedThreadIfTracking(
task, stopwatch);
+ // Update the sequence token in case it has been set from within the
+ // task, so it can be removed from the set of currently running
+ // sequences in DidRunWorkerTask() below.
+ task.sequence_token_id = this_worker->task_sequence_token().id_;
+
// Make sure our task is erased outside the lock for the
// same reason we do this with delete_these_oustide_lock.
// Also, do it before calling reset_running_task_info() so
@@ -1063,7 +857,7 @@ void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) {
switch (status) {
case GET_WORK_WAIT: {
AutoUnlock unlock(lock_);
- DeleteWithoutLock(&delete_these_outside_lock, this_worker);
+ delete_these_outside_lock.clear();
}
break;
case GET_WORK_NOT_FOUND:
@@ -1085,7 +879,7 @@ void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) {
// help this case.
if (shutdown_called_ && blocking_shutdown_pending_task_count_ == 0) {
AutoUnlock unlock(lock_);
- DeleteWithoutLock(&delete_these_outside_lock, this_worker);
+ delete_these_outside_lock.clear();
break;
}
@@ -1093,7 +887,7 @@ void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) {
// deletion must happen outside of the lock.
if (delete_these_outside_lock.size()) {
AutoUnlock unlock(lock_);
- DeleteWithoutLock(&delete_these_outside_lock, this_worker);
+ delete_these_outside_lock.clear();
// Since the lock has been released, |status| may no longer be
// accurate. It might read GET_WORK_WAIT even if there are tasks
@@ -1116,9 +910,6 @@ void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) {
}
waiting_thread_count_--;
}
- // |delete_these_outside_lock| should have been cleared via
- // DeleteWithoutLock() above already.
- DCHECK(delete_these_outside_lock.empty());
}
} // Release lock_.
@@ -1130,22 +921,7 @@ void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) {
can_shutdown_cv_.Signal();
}
-void SequencedWorkerPool::Inner::DeleteWithoutLock(
- std::vector<SequencedTask>* tasks_to_delete,
- Worker* this_worker) {
- while (!tasks_to_delete->empty()) {
- const SequencedTask& deleted_task = tasks_to_delete->back();
- this_worker->set_running_task_info(
- SequenceToken(deleted_task.sequence_token_id),
- deleted_task.shutdown_behavior);
- tasks_to_delete->pop_back();
- }
- this_worker->reset_running_task_info();
-}
-
void SequencedWorkerPool::Inner::HandleCleanup() {
- DCHECK_EQ(AllPoolsState::USE_WORKER_POOL, g_all_pools_state);
-
lock_.AssertAcquired();
if (cleanup_state_ == CLEANUP_DONE)
return;
@@ -1210,9 +986,7 @@ int64_t SequencedWorkerPool::Inner::LockedGetNextSequenceTaskNumber() {
SequencedWorkerPool::Inner::GetWorkStatus SequencedWorkerPool::Inner::GetWork(
SequencedTask* task,
TimeDelta* wait_time,
- std::vector<SequencedTask>* delete_these_outside_lock) {
- DCHECK_EQ(AllPoolsState::USE_WORKER_POOL, g_all_pools_state);
-
+ std::vector<Closure>* delete_these_outside_lock) {
lock_.AssertAcquired();
// Find the next task with a sequence token that's not currently in use.
@@ -1256,17 +1030,18 @@ SequencedWorkerPool::Inner::GetWorkStatus SequencedWorkerPool::Inner::GetWork(
// shutdown. Delete it and get more work.
//
// Note that we do not want to delete unrunnable tasks. Deleting a task
- // can have side effects (like freeing some objects) and deleting a task
- // that's supposed to run after one that's currently running could cause
- // an obscure crash.
+ // can have side effects (like freeing some objects) and deleting a
+ // task that's supposed to run after one that's currently running could
+ // cause an obscure crash.
//
// We really want to delete these tasks outside the lock in case the
- // closures are holding refs to objects that want to post work from their
- // destructors (which would deadlock). The closures are internally
- // refcounted, so we just need to keep a copy of them alive until the lock
- // is exited. The calling code can just clear() the vector they passed to
- // us once the lock is exited to make this happen.
- delete_these_outside_lock->push_back(*i);
+ // closures are holding refs to objects that want to post work from
+ // their destructorss (which would deadlock). The closures are
+ // internally refcounted, so we just need to keep a copy of them alive
+ // until the lock is exited. The calling code can just clear() the
+ // vector they passed to us once the lock is exited to make this
+ // happen.
+ delete_these_outside_lock->push_back(i->task);
pending_tasks_.erase(i++);
continue;
}
@@ -1277,7 +1052,7 @@ SequencedWorkerPool::Inner::GetWorkStatus SequencedWorkerPool::Inner::GetWork(
status = GET_WORK_WAIT;
if (cleanup_state_ == CLEANUP_RUNNING) {
// Deferred tasks are deleted when cleaning up, see Inner::ThreadLoop.
- delete_these_outside_lock->push_back(*i);
+ delete_these_outside_lock->push_back(i->task);
pending_tasks_.erase(i);
}
break;
@@ -1298,8 +1073,6 @@ SequencedWorkerPool::Inner::GetWorkStatus SequencedWorkerPool::Inner::GetWork(
}
int SequencedWorkerPool::Inner::WillRunWorkerTask(const SequencedTask& task) {
- DCHECK_EQ(AllPoolsState::USE_WORKER_POOL, g_all_pools_state);
-
lock_.AssertAcquired();
// Mark the task's sequence number as in use.
@@ -1331,8 +1104,6 @@ int SequencedWorkerPool::Inner::WillRunWorkerTask(const SequencedTask& task) {
}
void SequencedWorkerPool::Inner::DidRunWorkerTask(const SequencedTask& task) {
- DCHECK_EQ(AllPoolsState::USE_WORKER_POOL, g_all_pools_state);
-
lock_.AssertAcquired();
if (task.shutdown_behavior != CONTINUE_ON_SHUTDOWN) {
@@ -1346,8 +1117,6 @@ void SequencedWorkerPool::Inner::DidRunWorkerTask(const SequencedTask& task) {
bool SequencedWorkerPool::Inner::IsSequenceTokenRunnable(
int sequence_token_id) const {
- DCHECK_NE(AllPoolsState::REDIRECTED_TO_TASK_SCHEDULER, g_all_pools_state);
-
lock_.AssertAcquired();
return !sequence_token_id ||
current_sequences_.find(sequence_token_id) ==
@@ -1355,8 +1124,6 @@ bool SequencedWorkerPool::Inner::IsSequenceTokenRunnable(
}
int SequencedWorkerPool::Inner::PrepareToStartAdditionalThreadIfHelpful() {
- DCHECK_NE(AllPoolsState::REDIRECTED_TO_TASK_SCHEDULER, g_all_pools_state);
-
lock_.AssertAcquired();
// How thread creation works:
//
@@ -1407,8 +1174,6 @@ int SequencedWorkerPool::Inner::PrepareToStartAdditionalThreadIfHelpful() {
void SequencedWorkerPool::Inner::FinishStartingAdditionalThread(
int thread_number) {
- DCHECK_EQ(AllPoolsState::USE_WORKER_POOL, g_all_pools_state);
-
// Called outside of the lock.
DCHECK_GT(thread_number, 0);
@@ -1418,8 +1183,6 @@ void SequencedWorkerPool::Inner::FinishStartingAdditionalThread(
}
void SequencedWorkerPool::Inner::SignalHasWork() {
- DCHECK_NE(AllPoolsState::REDIRECTED_TO_TASK_SCHEDULER, g_all_pools_state);
-
has_work_cv_.Signal();
if (testing_observer_) {
testing_observer_->OnHasWork();
@@ -1427,7 +1190,6 @@ void SequencedWorkerPool::Inner::SignalHasWork() {
}
bool SequencedWorkerPool::Inner::CanShutdown() const {
- DCHECK_EQ(AllPoolsState::USE_WORKER_POOL, g_all_pools_state);
lock_.AssertAcquired();
// See PrepareToStartAdditionalThreadIfHelpful for how thread creation works.
return !thread_being_created_ &&
@@ -1465,61 +1227,44 @@ SequencedWorkerPool::GetWorkerPoolForCurrentThread() {
}
// static
-void SequencedWorkerPool::EnableForProcess() {
- // TODO(fdoray): Uncomment this line. It is initially commented to avoid a
- // revert of the CL that adds debug::DumpWithoutCrashing() in case of
- // waterfall failures.
- // DCHECK_EQ(AllPoolsState::POST_TASK_DISABLED, g_all_pools_state);
- g_all_pools_state = AllPoolsState::USE_WORKER_POOL;
-}
+scoped_refptr<SequencedTaskRunner>
+SequencedWorkerPool::GetSequencedTaskRunnerForCurrentThread() {
+ Worker* worker = Worker::GetForCurrentThread();
-// static
-void SequencedWorkerPool::EnableWithRedirectionToTaskSchedulerForProcess(
- TaskPriority max_task_priority) {
-#if 1
- NOTREACHED();
- ALLOW_UNUSED_PARAM(max_task_priority);
-#else
- // TODO(fdoray): Uncomment this line. It is initially commented to avoid a
- // revert of the CL that adds debug::DumpWithoutCrashing() in case of
- // waterfall failures.
- // DCHECK_EQ(AllPoolsState::POST_TASK_DISABLED, g_all_pools_state);
- DCHECK(TaskScheduler::GetInstance());
- g_all_pools_state = AllPoolsState::REDIRECTED_TO_TASK_SCHEDULER;
- g_max_task_priority = max_task_priority;
-#endif
-}
+ // If there is no worker, this thread is not a worker thread. Otherwise, it is
+ // currently running a task (sequenced or unsequenced).
+ if (!worker)
+ return nullptr;
-// static
-void SequencedWorkerPool::DisableForProcessForTesting() {
- g_all_pools_state = AllPoolsState::POST_TASK_DISABLED;
-}
+ scoped_refptr<SequencedWorkerPool> pool = worker->worker_pool();
+ SequenceToken sequence_token = worker->task_sequence_token();
+ WorkerShutdown shutdown_behavior = worker->task_shutdown_behavior();
+ if (!sequence_token.IsValid()) {
+ // Create a new sequence token and bind this thread to it, to make sure that
+ // a task posted to the SequencedTaskRunner we are going to return is not
+ // immediately going to run on a different thread.
+ sequence_token = Inner::GetSequenceToken();
+ pool->inner_->SetRunningTaskInfoForCurrentThread(sequence_token,
+ shutdown_behavior);
+ }
-// static
-bool SequencedWorkerPool::IsEnabled() {
- return g_all_pools_state != AllPoolsState::POST_TASK_DISABLED;
+ DCHECK(pool->IsRunningSequenceOnCurrentThread(sequence_token));
+ return new SequencedWorkerPoolSequencedTaskRunner(
+ std::move(pool), sequence_token, shutdown_behavior);
}
SequencedWorkerPool::SequencedWorkerPool(size_t max_threads,
- const std::string& thread_name_prefix,
- base::TaskPriority task_priority)
- : constructor_task_runner_(SequencedTaskRunnerHandle::Get()),
- inner_(new Inner(this,
- max_threads,
- thread_name_prefix,
- task_priority,
- NULL)) {}
+ const std::string& thread_name_prefix)
+ : constructor_task_runner_(ThreadTaskRunnerHandle::Get()),
+ inner_(new Inner(this, max_threads, thread_name_prefix, NULL)) {
+}
SequencedWorkerPool::SequencedWorkerPool(size_t max_threads,
const std::string& thread_name_prefix,
- base::TaskPriority task_priority,
TestingObserver* observer)
- : constructor_task_runner_(SequencedTaskRunnerHandle::Get()),
- inner_(new Inner(this,
- max_threads,
- thread_name_prefix,
- task_priority,
- observer)) {}
+ : constructor_task_runner_(ThreadTaskRunnerHandle::Get()),
+ inner_(new Inner(this, max_threads, thread_name_prefix, observer)) {
+}
SequencedWorkerPool::~SequencedWorkerPool() {}
@@ -1550,7 +1295,7 @@ scoped_refptr<SequencedTaskRunner> SequencedWorkerPool::GetSequencedTaskRunner(
scoped_refptr<SequencedTaskRunner>
SequencedWorkerPool::GetSequencedTaskRunnerWithShutdownBehavior(
SequenceToken token, WorkerShutdown shutdown_behavior) {
- return new PoolSequencedTaskRunner(
+ return new SequencedWorkerPoolSequencedTaskRunner(
this, token, shutdown_behavior);
}
@@ -1633,19 +1378,18 @@ bool SequencedWorkerPool::RunsTasksOnCurrentThread() const {
return inner_->RunsTasksOnCurrentThread();
}
+bool SequencedWorkerPool::IsRunningSequenceOnCurrentThread(
+ SequenceToken sequence_token) const {
+ return inner_->IsRunningSequenceOnCurrentThread(sequence_token);
+}
+
+bool SequencedWorkerPool::IsRunningSequence(
+ SequenceToken sequence_token) const {
+ return inner_->IsRunningSequence(sequence_token);
+}
+
void SequencedWorkerPool::FlushForTesting() {
- DCHECK(!RunsTasksOnCurrentThread());
- base::ThreadRestrictions::ScopedAllowWait allow_wait;
- if (g_all_pools_state == AllPoolsState::REDIRECTED_TO_TASK_SCHEDULER) {
-#if 1
- NOTREACHED();
-#else
- // TODO(gab): Remove this if http://crbug.com/622400 fails.
- TaskScheduler::GetInstance()->FlushForTesting();
-#endif
- } else {
- inner_->CleanupForTesting();
- }
+ inner_->CleanupForTesting();
}
void SequencedWorkerPool::SignalHasWorkForTesting() {
@@ -1653,7 +1397,7 @@ void SequencedWorkerPool::SignalHasWorkForTesting() {
}
void SequencedWorkerPool::Shutdown(int max_new_blocking_tasks_after_shutdown) {
- DCHECK(constructor_task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(constructor_task_runner_->BelongsToCurrentThread());
inner_->Shutdown(max_new_blocking_tasks_after_shutdown);
}
@@ -1661,9 +1405,4 @@ bool SequencedWorkerPool::IsShutdownInProgress() {
return inner_->IsShutdownInProgress();
}
-bool SequencedWorkerPool::IsRunningSequenceOnCurrentThread(
- SequenceToken sequence_token) const {
- return inner_->IsRunningSequenceOnCurrentThread(sequence_token);
-}
-
} // namespace base
diff --git a/base/threading/sequenced_worker_pool.h b/base/threading/sequenced_worker_pool.h
index 0d42de9138..cbec39561a 100644
--- a/base/threading/sequenced_worker_pool.h
+++ b/base/threading/sequenced_worker_pool.h
@@ -13,11 +13,10 @@
#include "base/base_export.h"
#include "base/callback_forward.h"
-#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
#include "base/task_runner.h"
-#include "base/task_scheduler/task_traits.h"
namespace tracked_objects {
class Location;
@@ -25,10 +24,12 @@ class Location;
namespace base {
-class SequencedTaskRunner;
+class SingleThreadTaskRunner;
template <class T> class DeleteHelper;
+class SequencedTaskRunner;
+
// A worker thread pool that enforces ordering between sets of tasks. It also
// allows you to specify what should happen to your tasks on shutdown.
//
@@ -46,7 +47,8 @@ template <class T> class DeleteHelper;
// destruction will be visible to T2.
//
// Example:
-// SequencedWorkerPool::SequenceToken token = pool.GetSequenceToken();
+// SequencedWorkerPool::SequenceToken token =
+// SequencedWorkerPool::GetSequenceToken();
// pool.PostSequencedWorkerTask(token, SequencedWorkerPool::SKIP_ON_SHUTDOWN,
// FROM_HERE, base::Bind(...));
// pool.PostSequencedWorkerTask(token, SequencedWorkerPool::SKIP_ON_SHUTDOWN,
@@ -59,10 +61,6 @@ template <class T> class DeleteHelper;
// These will be executed in an unspecified order. The order of execution
// between tasks with different sequence tokens is also unspecified.
//
-// You must call EnableForProcess() or
-// EnableWithRedirectionToTaskSchedulerForProcess() before starting to post
-// tasks to a process' SequencedWorkerPools.
-//
// This class may be leaked on shutdown to facilitate fast shutdown. The
// expected usage, however, is to call Shutdown(), which correctly accounts
// for CONTINUE_ON_SHUTDOWN behavior and is required for BLOCK_SHUTDOWN
@@ -166,65 +164,36 @@ class BASE_EXPORT SequencedWorkerPool : public TaskRunner {
// an unsequenced task, returns an invalid SequenceToken.
static SequenceToken GetSequenceTokenForCurrentThread();
- // Returns the SequencedWorkerPool that owns this thread, or null if the
- // current thread is not a SequencedWorkerPool worker thread.
- //
- // Always returns nullptr when SequencedWorkerPool is redirected to
- // TaskScheduler.
- //
- // DEPRECATED. Use SequencedTaskRunnerHandle::Get() instead. Consequentially
- // the only remaining use case is in sequenced_task_runner_handle.cc to
- // implement that and will soon be removed along with SequencedWorkerPool:
- // http://crbug.com/622400.
- static scoped_refptr<SequencedWorkerPool> GetWorkerPoolForCurrentThread();
+ // Gets a SequencedTaskRunner for the current thread. If the current thread is
+ // running an unsequenced task, a new SequenceToken will be generated and set,
+ // so that the returned SequencedTaskRunner is guaranteed to run tasks after
+ // the current task has finished running.
+ static scoped_refptr<SequencedTaskRunner>
+ GetSequencedTaskRunnerForCurrentThread();
// Returns a unique token that can be used to sequence tasks posted to
// PostSequencedWorkerTask(). Valid tokens are always nonzero.
+ // TODO(bauerb): Rename this to better differentiate from
+ // GetSequenceTokenForCurrentThread().
static SequenceToken GetSequenceToken();
- // Enables posting tasks to this process' SequencedWorkerPools. Cannot be
- // called if already enabled. This is not thread-safe; proper synchronization
- // is required to use any SequencedWorkerPool method after calling this.
- static void EnableForProcess();
-
- // Same as EnableForProcess(), but tasks are redirected to the registered
- // TaskScheduler. All redirections' TaskPriority will be capped to
- // |max_task_priority|. There must be a registered TaskScheduler when this is
- // called.
- // TODO(gab): Remove this if http://crbug.com/622400 fails
- // (SequencedWorkerPool will be phased out completely otherwise).
- static void EnableWithRedirectionToTaskSchedulerForProcess(
- TaskPriority max_task_priority = TaskPriority::HIGHEST);
-
- // Disables posting tasks to this process' SequencedWorkerPools. Calling this
- // while there are active SequencedWorkerPools is not supported. This is not
- // thread-safe; proper synchronization is required to use any
- // SequencedWorkerPool method after calling this.
- static void DisableForProcessForTesting();
-
- // Returns true if posting tasks to this process' SequencedWorkerPool is
- // enabled (with or without redirection to TaskScheduler).
- static bool IsEnabled();
+ // Returns the SequencedWorkerPool that owns this thread, or null if the
+ // current thread is not a SequencedWorkerPool worker thread.
+ static scoped_refptr<SequencedWorkerPool> GetWorkerPoolForCurrentThread();
// When constructing a SequencedWorkerPool, there must be a
// ThreadTaskRunnerHandle on the current thread unless you plan to
// deliberately leak it.
- // Constructs a SequencedWorkerPool which will lazily create up to
- // |max_threads| and a prefix for the thread name to aid in debugging.
- // |max_threads| must be greater than 1. |task_priority| will be used to hint
- // base::TaskScheduler for an experiment in which all SequencedWorkerPool
- // tasks will be redirected to it in processes where a base::TaskScheduler was
- // instantiated.
+ // Pass the maximum number of threads (they will be lazily created as needed)
+ // and a prefix for the thread name to aid in debugging.
SequencedWorkerPool(size_t max_threads,
- const std::string& thread_name_prefix,
- base::TaskPriority task_priority);
+ const std::string& thread_name_prefix);
// Like above, but with |observer| for testing. Does not take ownership of
// |observer|.
SequencedWorkerPool(size_t max_threads,
const std::string& thread_name_prefix,
- base::TaskPriority task_priority,
TestingObserver* observer);
// Returns the sequence token associated with the given name. Calling this
@@ -238,7 +207,7 @@ class BASE_EXPORT SequencedWorkerPool : public TaskRunner {
// delay are posted with SKIP_ON_SHUTDOWN behavior and tasks with zero delay
// are posted with BLOCK_SHUTDOWN behavior.
scoped_refptr<SequencedTaskRunner> GetSequencedTaskRunner(
- SequenceToken token) WARN_UNUSED_RESULT;
+ SequenceToken token);
// Returns a SequencedTaskRunner wrapper which posts to this
// SequencedWorkerPool using the given sequence token. Tasks with nonzero
@@ -246,14 +215,14 @@ class BASE_EXPORT SequencedWorkerPool : public TaskRunner {
// are posted with the given shutdown behavior.
scoped_refptr<SequencedTaskRunner> GetSequencedTaskRunnerWithShutdownBehavior(
SequenceToken token,
- WorkerShutdown shutdown_behavior) WARN_UNUSED_RESULT;
+ WorkerShutdown shutdown_behavior);
// Returns a TaskRunner wrapper which posts to this SequencedWorkerPool using
// the given shutdown behavior. Tasks with nonzero delay are posted with
// SKIP_ON_SHUTDOWN behavior and tasks with zero delay are posted with the
// given shutdown behavior.
scoped_refptr<TaskRunner> GetTaskRunnerWithShutdownBehavior(
- WorkerShutdown shutdown_behavior) WARN_UNUSED_RESULT;
+ WorkerShutdown shutdown_behavior);
// Posts the given task for execution in the worker pool. Tasks posted with
// this function will execute in an unspecified order on a background thread.
@@ -347,21 +316,23 @@ class BASE_EXPORT SequencedWorkerPool : public TaskRunner {
TimeDelta delay) override;
bool RunsTasksOnCurrentThread() const override;
+ // Returns true if the current thread is processing a task with the given
+ // sequence_token.
+ bool IsRunningSequenceOnCurrentThread(SequenceToken sequence_token) const;
+
+ // Returns true if any thread is currently processing a task with the given
+ // sequence token. Should only be called with a valid sequence token.
+ bool IsRunningSequence(SequenceToken sequence_token) const;
+
// Blocks until all pending tasks are complete. This should only be called in
// unit tests when you want to validate something that should have happened.
- // Does not wait for delayed tasks. If redirection to TaskScheduler is
- // disabled, delayed tasks are deleted. If redirection to TaskScheduler is
- // enabled, this will wait for all tasks posted to TaskScheduler (not just
- // tasks posted to this SequencedWorkerPool).
+ // This will not flush delayed tasks; delayed tasks get deleted.
//
// Note that calling this will not prevent other threads from posting work to
// the queue while the calling thread is waiting on Flush(). In this case,
// Flush will return only when there's no more work in the queue. Normally,
// this doesn't come up since in a test, all the work is being posted from
// the main thread.
- //
- // TODO(gab): Remove mentions of TaskScheduler in this comment if
- // http://crbug.com/622400 fails.
void FlushForTesting();
// Spuriously signal that there is work to be done.
@@ -397,14 +368,9 @@ class BASE_EXPORT SequencedWorkerPool : public TaskRunner {
friend class DeleteHelper<SequencedWorkerPool>;
class Inner;
- class PoolSequencedTaskRunner;
class Worker;
- // Returns true if the current thread is processing a task with the given
- // sequence_token.
- bool IsRunningSequenceOnCurrentThread(SequenceToken sequence_token) const;
-
- const scoped_refptr<SequencedTaskRunner> constructor_task_runner_;
+ const scoped_refptr<SingleThreadTaskRunner> constructor_task_runner_;
// Avoid pulling in too many headers by putting (almost) everything
// into |inner_|.
diff --git a/base/threading/simple_thread.cc b/base/threading/simple_thread.cc
index 9eb443afab..6c64a17d6a 100644
--- a/base/threading/simple_thread.cc
+++ b/base/threading/simple_thread.cc
@@ -12,55 +12,62 @@
namespace base {
SimpleThread::SimpleThread(const std::string& name_prefix)
- : SimpleThread(name_prefix, Options()) {}
+ : name_prefix_(name_prefix),
+ name_(name_prefix),
+ thread_(),
+ event_(WaitableEvent::ResetPolicy::MANUAL,
+ WaitableEvent::InitialState::NOT_SIGNALED),
+ tid_(0),
+ joined_(false) {}
SimpleThread::SimpleThread(const std::string& name_prefix,
const Options& options)
: name_prefix_(name_prefix),
+ name_(name_prefix),
options_(options),
+ thread_(),
event_(WaitableEvent::ResetPolicy::MANUAL,
- WaitableEvent::InitialState::NOT_SIGNALED) {}
+ WaitableEvent::InitialState::NOT_SIGNALED),
+ tid_(0),
+ joined_(false) {}
SimpleThread::~SimpleThread() {
DCHECK(HasBeenStarted()) << "SimpleThread was never started.";
- DCHECK(!options_.joinable || HasBeenJoined())
- << "Joinable SimpleThread destroyed without being Join()ed.";
+ DCHECK(HasBeenJoined()) << "SimpleThread destroyed without being Join()ed.";
}
void SimpleThread::Start() {
DCHECK(!HasBeenStarted()) << "Tried to Start a thread multiple times.";
- bool success =
- options_.joinable
- ? PlatformThread::CreateWithPriority(options_.stack_size, this,
- &thread_, options_.priority)
- : PlatformThread::CreateNonJoinableWithPriority(
- options_.stack_size, this, options_.priority);
+ bool success;
+ if (options_.priority() == ThreadPriority::NORMAL) {
+ success = PlatformThread::Create(options_.stack_size(), this, &thread_);
+ } else {
+ success = PlatformThread::CreateWithPriority(options_.stack_size(), this,
+ &thread_, options_.priority());
+ }
DCHECK(success);
- ThreadRestrictions::ScopedAllowWait allow_wait;
+ base::ThreadRestrictions::ScopedAllowWait allow_wait;
event_.Wait(); // Wait for the thread to complete initialization.
}
void SimpleThread::Join() {
- DCHECK(options_.joinable) << "A non-joinable thread can't be joined.";
DCHECK(HasBeenStarted()) << "Tried to Join a never-started thread.";
DCHECK(!HasBeenJoined()) << "Tried to Join a thread multiple times.";
PlatformThread::Join(thread_);
- thread_ = PlatformThreadHandle();
joined_ = true;
}
bool SimpleThread::HasBeenStarted() {
- ThreadRestrictions::ScopedAllowWait allow_wait;
+ base::ThreadRestrictions::ScopedAllowWait allow_wait;
return event_.IsSignaled();
}
void SimpleThread::ThreadMain() {
tid_ = PlatformThread::CurrentId();
// Construct our full name of the form "name_prefix_/TID".
- std::string name(name_prefix_);
- name.push_back('/');
- name.append(IntToString(tid_));
- PlatformThread::SetName(name);
+ name_.push_back('/');
+ name_.append(IntToString(tid_));
+ PlatformThread::SetName(name_);
// We've initialized our new thread, signal that we're done to Start().
event_.Signal();
@@ -70,26 +77,24 @@ void SimpleThread::ThreadMain() {
DelegateSimpleThread::DelegateSimpleThread(Delegate* delegate,
const std::string& name_prefix)
- : DelegateSimpleThread(delegate, name_prefix, Options()) {}
+ : SimpleThread(name_prefix),
+ delegate_(delegate) {
+}
DelegateSimpleThread::DelegateSimpleThread(Delegate* delegate,
const std::string& name_prefix,
const Options& options)
: SimpleThread(name_prefix, options),
delegate_(delegate) {
- DCHECK(delegate_);
}
-DelegateSimpleThread::~DelegateSimpleThread() = default;
+DelegateSimpleThread::~DelegateSimpleThread() {
+}
void DelegateSimpleThread::Run() {
DCHECK(delegate_) << "Tried to call Run without a delegate (called twice?)";
-
- // Non-joinable DelegateSimpleThreads are allowed to be deleted during Run().
- // Member state must not be accessed after invoking Run().
- Delegate* delegate = delegate_;
- delegate_ = nullptr;
- delegate->Run();
+ delegate_->Run();
+ delegate_ = NULL;
}
DelegateSimpleThreadPool::DelegateSimpleThreadPool(
diff --git a/base/threading/simple_thread.h b/base/threading/simple_thread.h
index f9f5e91045..3deeb1018c 100644
--- a/base/threading/simple_thread.h
+++ b/base/threading/simple_thread.h
@@ -48,7 +48,6 @@
#include "base/base_export.h"
#include "base/compiler_specific.h"
-#include "base/macros.h"
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/platform_thread.h"
@@ -59,26 +58,25 @@ namespace base {
// virtual Run method, or you can use the DelegateSimpleThread interface.
class BASE_EXPORT SimpleThread : public PlatformThread::Delegate {
public:
- struct BASE_EXPORT Options {
+ class BASE_EXPORT Options {
public:
- Options() = default;
- explicit Options(ThreadPriority priority_in) : priority(priority_in) {}
- ~Options() = default;
+ Options() : stack_size_(0), priority_(ThreadPriority::NORMAL) {}
+ explicit Options(ThreadPriority priority)
+ : stack_size_(0), priority_(priority) {}
+ ~Options() {}
- // Allow copies.
- Options(const Options& other) = default;
- Options& operator=(const Options& other) = default;
+ // We use the standard compiler-supplied copy constructor.
// A custom stack size, or 0 for the system default.
- size_t stack_size = 0;
-
- ThreadPriority priority = ThreadPriority::NORMAL;
-
- // If false, the underlying thread's PlatformThreadHandle will not be kept
- // around and as such the SimpleThread instance will not be Join()able and
- // must not be deleted before Run() is invoked. After that, it's up to
- // the subclass to determine when it is safe to delete itself.
- bool joinable = true;
+ void set_stack_size(size_t size) { stack_size_ = size; }
+ size_t stack_size() const { return stack_size_; }
+
+ // A custom thread priority.
+ void set_priority(ThreadPriority priority) { priority_ = priority; }
+ ThreadPriority priority() const { return priority_; }
+ private:
+ size_t stack_size_;
+ ThreadPriority priority_;
};
// Create a SimpleThread. |options| should be used to manage any specific
@@ -96,13 +94,19 @@ class BASE_EXPORT SimpleThread : public PlatformThread::Delegate {
// Subclasses should override the Run method.
virtual void Run() = 0;
+ // Return the thread name prefix, or "unnamed" if none was supplied.
+ std::string name_prefix() { return name_prefix_; }
+
+ // Return the completed name including TID, only valid after Start().
+ std::string name() { return name_; }
+
// Return the thread id, only valid after Start().
PlatformThreadId tid() { return tid_; }
// Return True if Start() has ever been called.
bool HasBeenStarted();
- // Return True if Join() has ever been called.
+ // Return True if Join() has evern been called.
bool HasBeenJoined() { return joined_; }
// Overridden from PlatformThread::Delegate:
@@ -112,24 +116,18 @@ class BASE_EXPORT SimpleThread : public PlatformThread::Delegate {
const std::string name_prefix_;
std::string name_;
const Options options_;
- PlatformThreadHandle thread_; // PlatformThread handle, reset after Join.
+ PlatformThreadHandle thread_; // PlatformThread handle, invalid after Join!
WaitableEvent event_; // Signaled if Start() was ever called.
- PlatformThreadId tid_ = kInvalidThreadId; // The backing thread's id.
- bool joined_ = false; // True if Join has been called.
-
- DISALLOW_COPY_AND_ASSIGN(SimpleThread);
+ PlatformThreadId tid_; // The backing thread's id.
+ bool joined_; // True if Join has been called.
};
-// A SimpleThread which delegates Run() to its Delegate. Non-joinable
-// DelegateSimpleThread are safe to delete after Run() was invoked, their
-// Delegates are also safe to delete after that point from this class' point of
-// view (although implementations must of course make sure that Run() will not
-// use their Delegate's member state after its deletion).
class BASE_EXPORT DelegateSimpleThread : public SimpleThread {
public:
class BASE_EXPORT Delegate {
public:
- virtual ~Delegate() = default;
+ Delegate() { }
+ virtual ~Delegate() { }
virtual void Run() = 0;
};
@@ -144,8 +142,6 @@ class BASE_EXPORT DelegateSimpleThread : public SimpleThread {
private:
Delegate* delegate_;
-
- DISALLOW_COPY_AND_ASSIGN(DelegateSimpleThread);
};
// DelegateSimpleThreadPool allows you to start up a fixed number of threads,
@@ -190,8 +186,6 @@ class BASE_EXPORT DelegateSimpleThreadPool
std::queue<Delegate*> delegates_;
base::Lock lock_; // Locks delegates_
WaitableEvent dry_; // Not signaled when there is no work to do.
-
- DISALLOW_COPY_AND_ASSIGN(DelegateSimpleThreadPool);
};
} // namespace base
diff --git a/base/threading/simple_thread_unittest.cc b/base/threading/simple_thread_unittest.cc
index 0e52500c52..14dd4591f1 100644
--- a/base/threading/simple_thread_unittest.cc
+++ b/base/threading/simple_thread_unittest.cc
@@ -2,13 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <memory>
-
#include "base/atomic_sequence_num.h"
-#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/synchronization/waitable_event.h"
-#include "base/test/gtest_util.h"
#include "base/threading/simple_thread.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -21,49 +17,11 @@ class SetIntRunner : public DelegateSimpleThread::Delegate {
SetIntRunner(int* ptr, int val) : ptr_(ptr), val_(val) { }
~SetIntRunner() override {}
- private:
void Run() override { *ptr_ = val_; }
+ private:
int* ptr_;
int val_;
-
- DISALLOW_COPY_AND_ASSIGN(SetIntRunner);
-};
-
-// Signals |started_| when Run() is invoked and waits until |released_| is
-// signaled to return, signaling |done_| before doing so. Useful for tests that
-// care to control Run()'s flow.
-class ControlledRunner : public DelegateSimpleThread::Delegate {
- public:
- ControlledRunner()
- : started_(WaitableEvent::ResetPolicy::MANUAL,
- WaitableEvent::InitialState::NOT_SIGNALED),
- released_(WaitableEvent::ResetPolicy::MANUAL,
- WaitableEvent::InitialState::NOT_SIGNALED),
- done_(WaitableEvent::ResetPolicy::MANUAL,
- WaitableEvent::InitialState::NOT_SIGNALED) {}
-
- ~ControlledRunner() override { ReleaseAndWaitUntilDone(); }
-
- void WaitUntilStarted() { started_.Wait(); }
-
- void ReleaseAndWaitUntilDone() {
- released_.Signal();
- done_.Wait();
- }
-
- private:
- void Run() override {
- started_.Signal();
- released_.Wait();
- done_.Signal();
- }
-
- WaitableEvent started_;
- WaitableEvent released_;
- WaitableEvent done_;
-
- DISALLOW_COPY_AND_ASSIGN(ControlledRunner);
};
class WaitEventRunner : public DelegateSimpleThread::Delegate {
@@ -71,28 +29,22 @@ class WaitEventRunner : public DelegateSimpleThread::Delegate {
explicit WaitEventRunner(WaitableEvent* event) : event_(event) { }
~WaitEventRunner() override {}
- private:
void Run() override {
EXPECT_FALSE(event_->IsSignaled());
event_->Signal();
EXPECT_TRUE(event_->IsSignaled());
}
-
+ private:
WaitableEvent* event_;
-
- DISALLOW_COPY_AND_ASSIGN(WaitEventRunner);
};
class SeqRunner : public DelegateSimpleThread::Delegate {
public:
explicit SeqRunner(AtomicSequenceNumber* seq) : seq_(seq) { }
-
- private:
void Run() override { seq_->GetNext(); }
+ private:
AtomicSequenceNumber* seq_;
-
- DISALLOW_COPY_AND_ASSIGN(SeqRunner);
};
// We count up on a sequence number, firing on the event when we've hit our
@@ -104,7 +56,6 @@ class VerifyPoolRunner : public DelegateSimpleThread::Delegate {
int total, WaitableEvent* event)
: seq_(seq), total_(total), event_(event) { }
- private:
void Run() override {
if (seq_->GetNext() == total_) {
event_->Signal();
@@ -113,11 +64,10 @@ class VerifyPoolRunner : public DelegateSimpleThread::Delegate {
}
}
+ private:
AtomicSequenceNumber* seq_;
int total_;
WaitableEvent* event_;
-
- DISALLOW_COPY_AND_ASSIGN(VerifyPoolRunner);
};
} // namespace
@@ -158,44 +108,29 @@ TEST(SimpleThreadTest, WaitForEvent) {
thread.Join();
}
-TEST(SimpleThreadTest, NonJoinableStartAndDieOnJoin) {
- ControlledRunner runner;
+TEST(SimpleThreadTest, NamedWithOptions) {
+ WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL,
+ WaitableEvent::InitialState::NOT_SIGNALED);
+ WaitEventRunner runner(&event);
SimpleThread::Options options;
- options.joinable = false;
- DelegateSimpleThread thread(&runner, "non_joinable", options);
+ DelegateSimpleThread thread(&runner, "event_waiter", options);
+ EXPECT_EQ(thread.name_prefix(), "event_waiter");
+ EXPECT_FALSE(event.IsSignaled());
- EXPECT_FALSE(thread.HasBeenStarted());
thread.Start();
- EXPECT_TRUE(thread.HasBeenStarted());
-
- // Note: this is not quite the same as |thread.HasBeenStarted()| which
- // represents ThreadMain() getting ready to invoke Run() whereas
- // |runner.WaitUntilStarted()| ensures Run() was actually invoked.
- runner.WaitUntilStarted();
-
- EXPECT_FALSE(thread.HasBeenJoined());
- EXPECT_DCHECK_DEATH({ thread.Join(); });
-}
-
-TEST(SimpleThreadTest, NonJoinableInactiveDelegateDestructionIsOkay) {
- std::unique_ptr<ControlledRunner> runner(new ControlledRunner);
-
- SimpleThread::Options options;
- options.joinable = false;
- std::unique_ptr<DelegateSimpleThread> thread(
- new DelegateSimpleThread(runner.get(), "non_joinable", options));
-
- thread->Start();
- runner->WaitUntilStarted();
+ EXPECT_EQ(thread.name_prefix(), "event_waiter");
+ EXPECT_EQ(thread.name(),
+ std::string("event_waiter/") + IntToString(thread.tid()));
+ event.Wait();
- // Deleting a non-joinable SimpleThread after Run() was invoked is okay.
- thread.reset();
+ EXPECT_TRUE(event.IsSignaled());
+ thread.Join();
- runner->WaitUntilStarted();
- runner->ReleaseAndWaitUntilDone();
- // It should be safe to destroy a Delegate after its Run() method completed.
- runner.reset();
+ // We keep the name and tid, even after the thread is gone.
+ EXPECT_EQ(thread.name_prefix(), "event_waiter");
+ EXPECT_EQ(thread.name(),
+ std::string("event_waiter/") + IntToString(thread.tid()));
}
TEST(SimpleThreadTest, ThreadPool) {
diff --git a/base/threading/thread.cc b/base/threading/thread.cc
index c30320f0dc..9cdc6912ea 100644
--- a/base/threading/thread.cc
+++ b/base/threading/thread.cc
@@ -5,10 +5,8 @@
#include "base/threading/thread.h"
#include "base/bind.h"
-#include "base/bind_helpers.h"
#include "base/lazy_instance.h"
#include "base/location.h"
-#include "base/logging.h"
#include "base/run_loop.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread_id_name_manager.h"
@@ -16,10 +14,6 @@
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
-#if defined(OS_POSIX) && !defined(OS_NACL)
-#include "base/files/file_descriptor_watcher_posix.h"
-#endif
-
#if defined(OS_WIN)
#include "base/win/scoped_com_initializer.h"
#endif
@@ -32,31 +26,53 @@ namespace {
// because its Stop method was called. This allows us to catch cases where
// MessageLoop::QuitWhenIdle() is called directly, which is unexpected when
// using a Thread to setup and run a MessageLoop.
-base::LazyInstance<base::ThreadLocalBoolean>::Leaky lazy_tls_bool =
+base::LazyInstance<base::ThreadLocalBoolean> lazy_tls_bool =
LAZY_INSTANCE_INITIALIZER;
} // namespace
-Thread::Options::Options() = default;
+// This is used to trigger the message loop to exit.
+void ThreadQuitHelper() {
+ MessageLoop::current()->QuitWhenIdle();
+ Thread::SetThreadWasQuitProperly(true);
+}
+
+Thread::Options::Options()
+ : message_loop_type(MessageLoop::TYPE_DEFAULT),
+ timer_slack(TIMER_SLACK_NONE),
+ stack_size(0),
+ priority(ThreadPriority::NORMAL) {
+}
-Thread::Options::Options(MessageLoop::Type type, size_t size)
- : message_loop_type(type), stack_size(size) {}
+Thread::Options::Options(MessageLoop::Type type,
+ size_t size)
+ : message_loop_type(type),
+ timer_slack(TIMER_SLACK_NONE),
+ stack_size(size),
+ priority(ThreadPriority::NORMAL) {
+}
Thread::Options::Options(const Options& other) = default;
-Thread::Options::~Options() = default;
+Thread::Options::~Options() {
+}
Thread::Thread(const std::string& name)
- : id_event_(WaitableEvent::ResetPolicy::MANUAL,
+ :
+#if defined(OS_WIN)
+ com_status_(NONE),
+#endif
+ stopping_(false),
+ running_(false),
+ thread_(0),
+ id_(kInvalidThreadId),
+ id_event_(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED),
+ message_loop_(nullptr),
+ message_loop_timer_slack_(TIMER_SLACK_NONE),
name_(name),
start_event_(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED) {
- // Only bind the sequence on Start(): the state is constant between
- // construction and Start() and it's thus valid for Start() to be called on
- // another sequence as long as every other operation is then performed on that
- // sequence.
- owning_sequence_checker_.DetachFromSequence();
}
Thread::~Thread() {
@@ -64,8 +80,6 @@ Thread::~Thread() {
}
bool Thread::Start() {
- DCHECK(owning_sequence_checker_.CalledOnValidSequence());
-
Options options;
#if defined(OS_WIN)
if (com_status_ == STA)
@@ -75,11 +89,7 @@ bool Thread::Start() {
}
bool Thread::StartWithOptions(const Options& options) {
- DCHECK(owning_sequence_checker_.CalledOnValidSequence());
DCHECK(!message_loop_);
- DCHECK(!IsRunning());
- DCHECK(!stopping_) << "Starting a non-joinable thread a second time? That's "
- << "not allowed!";
#if defined(OS_WIN)
DCHECK((com_status_ != STA) ||
(options.message_loop_type == MessageLoop::TYPE_UI));
@@ -96,41 +106,32 @@ bool Thread::StartWithOptions(const Options& options) {
type = MessageLoop::TYPE_CUSTOM;
message_loop_timer_slack_ = options.timer_slack;
- std::unique_ptr<MessageLoop> message_loop_owned =
+ std::unique_ptr<MessageLoop> message_loop =
MessageLoop::CreateUnbound(type, options.message_pump_factory);
- message_loop_ = message_loop_owned.get();
+ message_loop_ = message_loop.get();
start_event_.Reset();
- // Hold |thread_lock_| while starting the new thread to synchronize with
- // Stop() while it's not guaranteed to be sequenced (until crbug/629139 is
- // fixed).
+ // Hold the thread_lock_ while starting a new thread, so that we can make sure
+ // that thread_ is populated before the newly created thread accesses it.
{
AutoLock lock(thread_lock_);
- bool success =
- options.joinable
- ? PlatformThread::CreateWithPriority(options.stack_size, this,
- &thread_, options.priority)
- : PlatformThread::CreateNonJoinableWithPriority(
- options.stack_size, this, options.priority);
- if (!success) {
+ if (!PlatformThread::CreateWithPriority(options.stack_size, this, &thread_,
+ options.priority)) {
DLOG(ERROR) << "failed to create thread";
message_loop_ = nullptr;
return false;
}
}
- joinable_ = options.joinable;
-
- // The ownership of |message_loop_| is managed by the newly created thread
+ // The ownership of message_loop is managemed by the newly created thread
// within the ThreadMain.
- ignore_result(message_loop_owned.release());
+ ignore_result(message_loop.release());
DCHECK(message_loop_);
return true;
}
bool Thread::StartAndWaitForTesting() {
- DCHECK(owning_sequence_checker_.CalledOnValidSequence());
bool result = Start();
if (!result)
return false;
@@ -139,7 +140,6 @@ bool Thread::StartAndWaitForTesting() {
}
bool Thread::WaitUntilThreadStarted() const {
- DCHECK(owning_sequence_checker_.CalledOnValidSequence());
if (!message_loop_)
return false;
base::ThreadRestrictions::ScopedAllowWait allow_wait;
@@ -147,74 +147,37 @@ bool Thread::WaitUntilThreadStarted() const {
return true;
}
-void Thread::FlushForTesting() {
- DCHECK(owning_sequence_checker_.CalledOnValidSequence());
- if (!message_loop_)
- return;
-
- WaitableEvent done(WaitableEvent::ResetPolicy::AUTOMATIC,
- WaitableEvent::InitialState::NOT_SIGNALED);
- task_runner()->PostTask(FROM_HERE,
- Bind(&WaitableEvent::Signal, Unretained(&done)));
- done.Wait();
-}
-
void Thread::Stop() {
- DCHECK(joinable_);
-
- // TODO(gab): Fix improper usage of this API (http://crbug.com/629139) and
- // enable this check, until then synchronization with Start() via
- // |thread_lock_| is required...
- // DCHECK(owning_sequence_checker_.CalledOnValidSequence());
AutoLock lock(thread_lock_);
-
- StopSoon();
-
- // Can't join if the |thread_| is either already gone or is non-joinable.
if (thread_.is_null())
return;
+ StopSoon();
+
// Wait for the thread to exit.
//
- // TODO(darin): Unfortunately, we need to keep |message_loop_| around until
+ // TODO(darin): Unfortunately, we need to keep message_loop_ around until
// the thread exits. Some consumers are abusing the API. Make them stop.
//
PlatformThread::Join(thread_);
thread_ = base::PlatformThreadHandle();
- // The thread should nullify |message_loop_| on exit (note: Join() adds an
- // implicit memory barrier and no lock is thus required for this check).
+ // The thread should nullify message_loop_ on exit.
DCHECK(!message_loop_);
stopping_ = false;
}
void Thread::StopSoon() {
- // TODO(gab): Fix improper usage of this API (http://crbug.com/629139) and
- // enable this check.
- // DCHECK(owning_sequence_checker_.CalledOnValidSequence());
+ // We should only be called on the same thread that started us.
+
+ DCHECK_NE(GetThreadId(), PlatformThread::CurrentId());
if (stopping_ || !message_loop_)
return;
stopping_ = true;
-
- if (using_external_message_loop_) {
- // Setting |stopping_| to true above should have been sufficient for this
- // thread to be considered "stopped" per it having never set its |running_|
- // bit by lack of its own ThreadMain.
- DCHECK(!IsRunning());
- message_loop_ = nullptr;
- return;
- }
-
- task_runner()->PostTask(
- FROM_HERE, base::Bind(&Thread::ThreadQuitHelper, Unretained(this)));
-}
-
-void Thread::DetachFromSequence() {
- DCHECK(owning_sequence_checker_.CalledOnValidSequence());
- owning_sequence_checker_.DetachFromSequence();
+ task_runner()->PostTask(FROM_HERE, base::Bind(&ThreadQuitHelper));
}
PlatformThreadId Thread::GetThreadId() const {
@@ -225,36 +188,26 @@ PlatformThreadId Thread::GetThreadId() const {
}
bool Thread::IsRunning() const {
- // TODO(gab): Fix improper usage of this API (http://crbug.com/629139) and
- // enable this check.
- // DCHECK(owning_sequence_checker_.CalledOnValidSequence());
-
- // If the thread's already started (i.e. |message_loop_| is non-null) and not
- // yet requested to stop (i.e. |stopping_| is false) we can just return true.
- // (Note that |stopping_| is touched only on the same sequence that starts /
- // started the new thread so we need no locking here.)
+ // If the thread's already started (i.e. message_loop_ is non-null) and
+ // not yet requested to stop (i.e. stopping_ is false) we can just return
+ // true. (Note that stopping_ is touched only on the same thread that
+ // starts / started the new thread so we need no locking here.)
if (message_loop_ && !stopping_)
return true;
- // Otherwise check the |running_| flag, which is set to true by the new thread
+ // Otherwise check the running_ flag, which is set to true by the new thread
// only while it is inside Run().
AutoLock lock(running_lock_);
return running_;
}
-void Thread::Run(RunLoop* run_loop) {
- // Overridable protected method to be called from our |thread_| only.
- DCHECK(id_event_.IsSignaled());
- DCHECK_EQ(id_, PlatformThread::CurrentId());
-
- run_loop->Run();
+void Thread::Run(MessageLoop*) {
+ RunLoop().Run();
}
-// static
void Thread::SetThreadWasQuitProperly(bool flag) {
lazy_tls_bool.Pointer()->Set(flag);
}
-// static
bool Thread::GetThreadWasQuitProperly() {
bool quit_properly = true;
#ifndef NDEBUG
@@ -263,27 +216,9 @@ bool Thread::GetThreadWasQuitProperly() {
return quit_properly;
}
-void Thread::SetMessageLoop(MessageLoop* message_loop) {
- DCHECK(owning_sequence_checker_.CalledOnValidSequence());
- DCHECK(message_loop);
-
- // Setting |message_loop_| should suffice for this thread to be considered
- // as "running", until Stop() is invoked.
- DCHECK(!IsRunning());
- message_loop_ = message_loop;
- DCHECK(IsRunning());
-
- using_external_message_loop_ = true;
-}
-
void Thread::ThreadMain() {
// First, make GetThreadId() available to avoid deadlocks. It could be called
// any place in the following thread initialization code.
- DCHECK(!id_event_.IsSignaled());
- // Note: this read of |id_| while |id_event_| isn't signaled is exceptionally
- // okay because ThreadMain has a happens-after relationship with the other
- // write in StartWithOptions().
- DCHECK_EQ(kInvalidThreadId, id_);
id_ = PlatformThread::CurrentId();
DCHECK_NE(kInvalidThreadId, id_);
id_event_.Signal();
@@ -291,22 +226,12 @@ void Thread::ThreadMain() {
// Complete the initialization of our Thread object.
PlatformThread::SetName(name_.c_str());
- // Lazily initialize the |message_loop| so that it can run on this thread.
+ // Lazily initialize the message_loop so that it can run on this thread.
DCHECK(message_loop_);
std::unique_ptr<MessageLoop> message_loop(message_loop_);
message_loop_->BindToCurrentThread();
message_loop_->SetTimerSlack(message_loop_timer_slack_);
-#if defined(OS_POSIX) && !defined(OS_NACL)
- // Allow threads running a MessageLoopForIO to use FileDescriptorWatcher API.
- std::unique_ptr<FileDescriptorWatcher> file_descriptor_watcher;
- if (MessageLoopForIO::IsCurrent()) {
- DCHECK_EQ(message_loop_, MessageLoopForIO::current());
- file_descriptor_watcher.reset(
- new FileDescriptorWatcher(MessageLoopForIO::current()));
- }
-#endif
-
#if defined(OS_WIN)
std::unique_ptr<win::ScopedCOMInitializer> com_initializer;
if (com_status_ != NONE) {
@@ -326,9 +251,7 @@ void Thread::ThreadMain() {
start_event_.Signal();
- RunLoop run_loop;
- run_loop_ = &run_loop;
- Run(run_loop_);
+ Run(message_loop_);
{
AutoLock lock(running_lock_);
@@ -343,22 +266,15 @@ void Thread::ThreadMain() {
#endif
if (message_loop->type() != MessageLoop::TYPE_CUSTOM) {
- // Assert that RunLoop::QuitWhenIdle was called by ThreadQuitHelper. Don't
- // check for custom message pumps, because their shutdown might not allow
- // this.
+ // Assert that MessageLoop::QuitWhenIdle was called by ThreadQuitHelper.
+ // Don't check for custom message pumps, because their shutdown might not
+ // allow this.
DCHECK(GetThreadWasQuitProperly());
}
// We can't receive messages anymore.
// (The message loop is destructed at the end of this block)
message_loop_ = nullptr;
- run_loop_ = nullptr;
-}
-
-void Thread::ThreadQuitHelper() {
- DCHECK(run_loop_);
- run_loop_->QuitWhenIdle();
- SetThreadWasQuitProperly(true);
}
} // namespace base
diff --git a/base/threading/thread.h b/base/threading/thread.h
index 01f7d8e250..c9a77d7323 100644
--- a/base/threading/thread.h
+++ b/base/threading/thread.h
@@ -15,9 +15,7 @@
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/timer_slack.h"
-#include "base/sequence_checker.h"
#include "base/single_thread_task_runner.h"
-#include "base/synchronization/atomic_flag.h"
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/platform_thread.h"
@@ -26,7 +24,6 @@
namespace base {
class MessagePump;
-class RunLoop;
// A simple thread abstraction that establishes a MessageLoop on a new thread.
// The consumer uses the MessageLoop of the thread to cause code to execute on
@@ -41,18 +38,6 @@ class RunLoop;
// (1) Thread::CleanUp()
// (2) MessageLoop::~MessageLoop
// (3.b) MessageLoop::DestructionObserver::WillDestroyCurrentMessageLoop
-//
-// This API is not thread-safe: unless indicated otherwise its methods are only
-// valid from the owning sequence (which is the one from which Start() is
-// invoked -- should it differ from the one on which it was constructed).
-//
-// Sometimes it's useful to kick things off on the initial sequence (e.g.
-// construction, Start(), task_runner()), but to then hand the Thread over to a
-// pool of users for the last one of them to destroy it when done. For that use
-// case, Thread::DetachFromSequence() allows the owning sequence to give up
-// ownership. The caller is then responsible to ensure a happens-after
-// relationship between the DetachFromSequence() call and the next use of that
-// Thread object (including ~Thread()).
class BASE_EXPORT Thread : PlatformThread::Delegate {
public:
struct BASE_EXPORT Options {
@@ -65,10 +50,10 @@ class BASE_EXPORT Thread : PlatformThread::Delegate {
// Specifies the type of message loop that will be allocated on the thread.
// This is ignored if message_pump_factory.is_null() is false.
- MessageLoop::Type message_loop_type = MessageLoop::TYPE_DEFAULT;
+ MessageLoop::Type message_loop_type;
// Specifies timer slack for thread message loop.
- TimerSlack timer_slack = TIMER_SLACK_NONE;
+ TimerSlack timer_slack;
// Used to create the MessagePump for the MessageLoop. The callback is Run()
// on the thread. If message_pump_factory.is_null(), then a MessagePump
@@ -79,18 +64,10 @@ class BASE_EXPORT Thread : PlatformThread::Delegate {
// Specifies the maximum stack size that the thread is allowed to use.
// This does not necessarily correspond to the thread's initial stack size.
// A value of 0 indicates that the default maximum should be used.
- size_t stack_size = 0;
+ size_t stack_size;
// Specifies the initial thread priority.
- ThreadPriority priority = ThreadPriority::NORMAL;
-
- // If false, the thread will not be joined on destruction. This is intended
- // for threads that want TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN
- // semantics. Non-joinable threads can't be joined (must be leaked and
- // can't be destroyed or Stop()'ed).
- // TODO(gab): allow non-joinable instances to be deleted without causing
- // user-after-frees (proposal @ https://crbug.com/629139#c14)
- bool joinable = true;
+ ThreadPriority priority;
};
// Constructor.
@@ -148,19 +125,12 @@ class BASE_EXPORT Thread : PlatformThread::Delegate {
// carefully for production code.
bool WaitUntilThreadStarted() const;
- // Blocks until all tasks previously posted to this thread have been executed.
- void FlushForTesting();
-
- // Signals the thread to exit and returns once the thread has exited. The
- // Thread object is completely reset and may be used as if it were newly
- // constructed (i.e., Start may be called again). Can only be called if
- // |joinable_|.
+ // Signals the thread to exit and returns once the thread has exited. After
+ // this method returns, the Thread object is completely reset and may be used
+ // as if it were newly constructed (i.e., Start may be called again).
//
// Stop may be called multiple times and is simply ignored if the thread is
- // already stopped or currently stopping.
- //
- // Start/Stop are not thread-safe and callers that desire to invoke them from
- // different threads must ensure mutual exclusion.
+ // already stopped.
//
// NOTE: If you are a consumer of Thread, it is not necessary to call this
// before deleting your Thread objects, as the destructor will do it.
@@ -175,17 +145,11 @@ class BASE_EXPORT Thread : PlatformThread::Delegate {
// deadlock on Windows with printer worker thread. In any other case, Stop()
// should be used.
//
- // Call Stop() to reset the thread object once it is known that the thread has
- // quit.
+ // StopSoon should not be called multiple times as it is risky to do so. It
+ // could cause a timing issue in message_loop() access. Call Stop() to reset
+ // the thread object once it is known that the thread has quit.
void StopSoon();
- // Detaches the owning sequence, indicating that the next call to this API
- // (including ~Thread()) can happen from a different sequence (to which it
- // will be rebound). This call itself must happen on the current owning
- // sequence and the caller must ensure the next API call has a happens-after
- // relationship with this one.
- void DetachFromSequence();
-
// Returns the message loop for this thread. Use the MessageLoop's
// PostTask methods to execute code on the thread. This only returns
// non-null after a successful call to Start. After Stop has been called,
@@ -194,52 +158,29 @@ class BASE_EXPORT Thread : PlatformThread::Delegate {
// NOTE: You must not call this MessageLoop's Quit method directly. Use
// the Thread's Stop method instead.
//
- // In addition to this Thread's owning sequence, this can also safely be
- // called from the underlying thread itself.
- MessageLoop* message_loop() const {
- // This class doesn't provide synchronization around |message_loop_| and as
- // such only the owner should access it (and the underlying thread which
- // never sees it before it's set). In practice, many callers are coming from
- // unrelated threads but provide their own implicit (e.g. memory barriers
- // from task posting) or explicit (e.g. locks) synchronization making the
- // access of |message_loop_| safe... Changing all of those callers is
- // unfeasible; instead verify that they can reliably see
- // |message_loop_ != nullptr| without synchronization as a proof that their
- // external synchronization catches the unsynchronized effects of Start().
- // TODO(gab): Despite all of the above this test has to be disabled for now
- // per crbug.com/629139#c6.
- // DCHECK(owning_sequence_checker_.CalledOnValidSequence() ||
- // (id_event_.IsSignaled() && id_ == PlatformThread::CurrentId()) ||
- // message_loop_);
- return message_loop_;
- }
+ MessageLoop* message_loop() const { return message_loop_; }
// Returns a TaskRunner for this thread. Use the TaskRunner's PostTask
// methods to execute code on the thread. Returns nullptr if the thread is not
// running (e.g. before Start or after Stop have been called). Callers can
// hold on to this even after the thread is gone; in this situation, attempts
// to PostTask() will fail.
- //
- // In addition to this Thread's owning sequence, this can also safely be
- // called from the underlying thread itself.
scoped_refptr<SingleThreadTaskRunner> task_runner() const {
- // Refer to the DCHECK and comment inside |message_loop()|.
- DCHECK(owning_sequence_checker_.CalledOnValidSequence() ||
- (id_event_.IsSignaled() && id_ == PlatformThread::CurrentId()) ||
- message_loop_);
return message_loop_ ? message_loop_->task_runner() : nullptr;
}
// Returns the name of this thread (for display in debugger too).
const std::string& thread_name() const { return name_; }
+ // The native thread handle.
+ PlatformThreadHandle thread_handle() { return thread_; }
+
// Returns the thread ID. Should not be called before the first Start*()
// call. Keeps on returning the same ID even after a Stop() call. The next
// Start*() call renews the ID.
//
// WARNING: This function will block if the thread hasn't started yet.
//
- // This method is thread-safe.
PlatformThreadId GetThreadId() const;
// Returns true if the thread has been started, and not yet stopped.
@@ -249,8 +190,8 @@ class BASE_EXPORT Thread : PlatformThread::Delegate {
// Called just prior to starting the message loop
virtual void Init() {}
- // Called to start the run loop
- virtual void Run(RunLoop* run_loop);
+ // Called to start the message loop
+ virtual void Run(MessageLoop* message_loop);
// Called just after the message loop ends
virtual void CleanUp() {}
@@ -258,11 +199,8 @@ class BASE_EXPORT Thread : PlatformThread::Delegate {
static void SetThreadWasQuitProperly(bool flag);
static bool GetThreadWasQuitProperly();
- // Bind this Thread to an existing MessageLoop instead of starting a new one.
- void SetMessageLoop(MessageLoop* message_loop);
-
- bool using_external_message_loop() const {
- return using_external_message_loop_;
+ void set_message_loop(MessageLoop* message_loop) {
+ message_loop_ = message_loop;
}
private:
@@ -277,25 +215,19 @@ class BASE_EXPORT Thread : PlatformThread::Delegate {
// PlatformThread::Delegate methods:
void ThreadMain() override;
- void ThreadQuitHelper();
-
#if defined(OS_WIN)
// Whether this thread needs to initialize COM, and if so, in what mode.
- ComStatus com_status_ = NONE;
+ ComStatus com_status_;
#endif
- // Mirrors the Options::joinable field used to start this thread. Verified
- // on Stop() -- non-joinable threads can't be joined (must be leaked).
- bool joinable_ = true;
-
// If true, we're in the middle of stopping, and shouldn't access
// |message_loop_|. It may non-nullptr and invalid.
// Should be written on the thread that created this thread. Also read data
// could be wrong on other threads.
- bool stopping_ = false;
+ bool stopping_;
// True while inside of Run().
- bool running_ = false;
+ bool running_;
mutable base::Lock running_lock_; // Protects |running_|.
// The thread's handle.
@@ -303,35 +235,24 @@ class BASE_EXPORT Thread : PlatformThread::Delegate {
mutable base::Lock thread_lock_; // Protects |thread_|.
// The thread's id once it has started.
- PlatformThreadId id_ = kInvalidThreadId;
- // Protects |id_| which must only be read while it's signaled.
- mutable WaitableEvent id_event_;
-
- // The thread's MessageLoop and RunLoop. Valid only while the thread is alive.
- // Set by the created thread.
- MessageLoop* message_loop_ = nullptr;
- RunLoop* run_loop_ = nullptr;
-
- // True only if |message_loop_| was externally provided by |SetMessageLoop()|
- // in which case this Thread has no underlying |thread_| and should merely
- // drop |message_loop_| on Stop(). In that event, this remains true after
- // Stop() was invoked so that subclasses can use this state to build their own
- // cleanup logic as required.
- bool using_external_message_loop_ = false;
+ PlatformThreadId id_;
+ mutable WaitableEvent id_event_; // Protects |id_|.
+
+ // The thread's message loop. Valid only while the thread is alive. Set
+ // by the created thread.
+ MessageLoop* message_loop_;
// Stores Options::timer_slack_ until the message loop has been bound to
// a thread.
- TimerSlack message_loop_timer_slack_ = TIMER_SLACK_NONE;
+ TimerSlack message_loop_timer_slack_;
// The name of the thread. Used for debugging purposes.
- const std::string name_;
+ std::string name_;
// Signaled when the created thread gets ready to use the message loop.
mutable WaitableEvent start_event_;
- // This class is not thread-safe, use this to verify access from the owning
- // sequence of the Thread.
- SequenceChecker owning_sequence_checker_;
+ friend void ThreadQuitHelper();
DISALLOW_COPY_AND_ASSIGN(Thread);
};
diff --git a/base/threading/thread_checker.h b/base/threading/thread_checker.h
index 1d4eb1c7b0..1d970f093e 100644
--- a/base/threading/thread_checker.h
+++ b/base/threading/thread_checker.h
@@ -8,6 +8,16 @@
#include "base/logging.h"
#include "base/threading/thread_checker_impl.h"
+// Apart from debug builds, we also enable the thread checker in
+// builds with DCHECK_ALWAYS_ON so that trybots and waterfall bots
+// with this define will get the same level of thread checking as
+// debug bots.
+#if DCHECK_IS_ON()
+#define ENABLE_THREAD_CHECKER 1
+#else
+#define ENABLE_THREAD_CHECKER 0
+#endif
+
namespace base {
// Do nothing implementation, for use in release mode.
@@ -53,20 +63,16 @@ class ThreadCheckerDoNothing {
// ThreadChecker thread_checker_;
// }
//
-// Note that, when enabled, CalledOnValidThread() returns false when called from
-// tasks posted to SingleThreadTaskRunners bound to different sequences, even if
-// the tasks happen to run on the same thread (e.g. two independent TaskRunners
-// with ExecutionMode::SINGLE_THREADED on the TaskScheduler that happen to share
-// a thread).
-//
// In Release mode, CalledOnValidThread will always return true.
-#if DCHECK_IS_ON()
+#if ENABLE_THREAD_CHECKER
class ThreadChecker : public ThreadCheckerImpl {
};
#else
class ThreadChecker : public ThreadCheckerDoNothing {
};
-#endif // DCHECK_IS_ON()
+#endif // ENABLE_THREAD_CHECKER
+
+#undef ENABLE_THREAD_CHECKER
} // namespace base
diff --git a/base/threading/thread_checker_impl.cc b/base/threading/thread_checker_impl.cc
index d5ccbdb943..eb87bae772 100644
--- a/base/threading/thread_checker_impl.cc
+++ b/base/threading/thread_checker_impl.cc
@@ -4,54 +4,31 @@
#include "base/threading/thread_checker_impl.h"
-#include "base/threading/thread_task_runner_handle.h"
-
namespace base {
-ThreadCheckerImpl::ThreadCheckerImpl() {
- AutoLock auto_lock(lock_);
- EnsureAssigned();
+ThreadCheckerImpl::ThreadCheckerImpl()
+ : valid_thread_id_() {
+ EnsureThreadIdAssigned();
}
-ThreadCheckerImpl::~ThreadCheckerImpl() = default;
+ThreadCheckerImpl::~ThreadCheckerImpl() {}
bool ThreadCheckerImpl::CalledOnValidThread() const {
+ EnsureThreadIdAssigned();
AutoLock auto_lock(lock_);
- EnsureAssigned();
-
- // Always return true when called from the task from which this
- // ThreadCheckerImpl was assigned to a thread.
- if (task_token_ == TaskToken::GetForCurrentThread())
- return true;
-
- // If this ThreadCheckerImpl is bound to a valid SequenceToken, it must be
- // equal to the current SequenceToken and there must be a registered
- // ThreadTaskRunnerHandle. Otherwise, the fact that the current task runs on
- // the thread to which this ThreadCheckerImpl is bound is fortuitous.
- if (sequence_token_.IsValid() &&
- (sequence_token_ != SequenceToken::GetForCurrentThread() ||
- !ThreadTaskRunnerHandle::IsSet())) {
- return false;
- }
-
- return thread_id_ == PlatformThread::CurrentRef();
+ return valid_thread_id_ == PlatformThread::CurrentRef();
}
void ThreadCheckerImpl::DetachFromThread() {
AutoLock auto_lock(lock_);
- thread_id_ = PlatformThreadRef();
- task_token_ = TaskToken();
- sequence_token_ = SequenceToken();
+ valid_thread_id_ = PlatformThreadRef();
}
-void ThreadCheckerImpl::EnsureAssigned() const {
- lock_.AssertAcquired();
- if (!thread_id_.is_null())
- return;
-
- thread_id_ = PlatformThread::CurrentRef();
- task_token_ = TaskToken::GetForCurrentThread();
- sequence_token_ = SequenceToken::GetForCurrentThread();
+void ThreadCheckerImpl::EnsureThreadIdAssigned() const {
+ AutoLock auto_lock(lock_);
+ if (valid_thread_id_.is_null()) {
+ valid_thread_id_ = PlatformThread::CurrentRef();
+ }
}
} // namespace base
diff --git a/base/threading/thread_checker_impl.h b/base/threading/thread_checker_impl.h
index 13193d1299..c92e143db0 100644
--- a/base/threading/thread_checker_impl.h
+++ b/base/threading/thread_checker_impl.h
@@ -7,18 +7,17 @@
#include "base/base_export.h"
#include "base/compiler_specific.h"
-#include "base/sequence_token.h"
#include "base/synchronization/lock.h"
#include "base/threading/platform_thread.h"
namespace base {
-// Real implementation of ThreadChecker, for use in debug mode, or for temporary
-// use in release mode (e.g. to CHECK on a threading issue seen only in the
-// wild).
+// Real implementation of ThreadChecker, for use in debug mode, or
+// for temporary use in release mode (e.g. to CHECK on a threading issue
+// seen only in the wild).
//
-// Note: You should almost always use the ThreadChecker class to get the right
-// version for your build configuration.
+// Note: You should almost always use the ThreadChecker class to get the
+// right version for your build configuration.
class BASE_EXPORT ThreadCheckerImpl {
public:
ThreadCheckerImpl();
@@ -32,29 +31,12 @@ class BASE_EXPORT ThreadCheckerImpl {
void DetachFromThread();
private:
- void EnsureAssigned() const;
+ void EnsureThreadIdAssigned() const;
- // Members are mutable so that CalledOnValidThread() can set them.
-
- // Synchronizes access to all members.
mutable base::Lock lock_;
-
- // Thread on which CalledOnValidThread() may return true.
- mutable PlatformThreadRef thread_id_;
-
- // TaskToken for which CalledOnValidThread() always returns true. This allows
- // CalledOnValidThread() to return true when called multiple times from the
- // same task, even if it's not running in a single-threaded context itself
- // (allowing usage of ThreadChecker/NonThreadSafe objects on the stack in the
- // scope of one-off tasks). Note: CalledOnValidThread() may return true even
- // if the current TaskToken is not equal to this.
- mutable TaskToken task_token_;
-
- // SequenceToken for which CalledOnValidThread() may return true. Used to
- // ensure that CalledOnValidThread() doesn't return true for TaskScheduler
- // tasks that happen to run on the same thread but weren't posted to the same
- // SingleThreadTaskRunner.
- mutable SequenceToken sequence_token_;
+ // This is mutable so that CalledOnValidThread can set it.
+ // It's guarded by |lock_|.
+ mutable PlatformThreadRef valid_thread_id_;
};
} // namespace base
diff --git a/base/threading/thread_checker_unittest.cc b/base/threading/thread_checker_unittest.cc
index 96455e66c7..bc5b1e473a 100644
--- a/base/threading/thread_checker_unittest.cc
+++ b/base/threading/thread_checker_unittest.cc
@@ -2,194 +2,180 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/threading/thread_checker.h"
+
#include <memory>
-#include "base/bind.h"
-#include "base/bind_helpers.h"
+#include "base/logging.h"
#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/sequence_token.h"
-#include "base/test/test_simple_task_runner.h"
#include "base/threading/simple_thread.h"
-#include "base/threading/thread_checker_impl.h"
-#include "base/threading/thread_task_runner_handle.h"
#include "testing/gtest/include/gtest/gtest.h"
+// Duplicated from base/threading/thread_checker.h so that we can be
+// good citizens there and undef the macro.
+#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
+#define ENABLE_THREAD_CHECKER 1
+#else
+#define ENABLE_THREAD_CHECKER 0
+#endif
+
namespace base {
+
namespace {
-// A thread that runs a callback.
-class RunCallbackThread : public SimpleThread {
+// Simple class to exercise the basics of ThreadChecker.
+// Both the destructor and DoStuff should verify that they were
+// called on the same thread as the constructor.
+class ThreadCheckerClass : public ThreadChecker {
public:
- explicit RunCallbackThread(const Closure& callback)
- : SimpleThread("RunCallbackThread"), callback_(callback) {}
+ ThreadCheckerClass() {}
- private:
- // SimpleThread:
- void Run() override { callback_.Run(); }
+ // Verifies that it was called on the same thread as the constructor.
+ void DoStuff() {
+ DCHECK(CalledOnValidThread());
+ }
- const Closure callback_;
+ void DetachFromThread() {
+ ThreadChecker::DetachFromThread();
+ }
- DISALLOW_COPY_AND_ASSIGN(RunCallbackThread);
-};
+ static void MethodOnDifferentThreadImpl();
+ static void DetachThenCallFromDifferentThreadImpl();
-// Runs a callback on a new thread synchronously.
-void RunCallbackOnNewThreadSynchronously(const Closure& callback) {
- RunCallbackThread run_callback_thread(callback);
- run_callback_thread.Start();
- run_callback_thread.Join();
-}
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ThreadCheckerClass);
+};
-void ExpectCalledOnValidThread(ThreadCheckerImpl* thread_checker) {
- ASSERT_TRUE(thread_checker);
+// Calls ThreadCheckerClass::DoStuff on another thread.
+class CallDoStuffOnThread : public base::SimpleThread {
+ public:
+ explicit CallDoStuffOnThread(ThreadCheckerClass* thread_checker_class)
+ : SimpleThread("call_do_stuff_on_thread"),
+ thread_checker_class_(thread_checker_class) {
+ }
- // This should bind |thread_checker| to the current thread if it wasn't
- // already bound to a thread.
- EXPECT_TRUE(thread_checker->CalledOnValidThread());
+ void Run() override { thread_checker_class_->DoStuff(); }
- // Since |thread_checker| is now bound to the current thread, another call to
- // CalledOnValidThread() should return true.
- EXPECT_TRUE(thread_checker->CalledOnValidThread());
-}
+ private:
+ ThreadCheckerClass* thread_checker_class_;
-void ExpectNotCalledOnValidThread(ThreadCheckerImpl* thread_checker) {
- ASSERT_TRUE(thread_checker);
- EXPECT_FALSE(thread_checker->CalledOnValidThread());
-}
+ DISALLOW_COPY_AND_ASSIGN(CallDoStuffOnThread);
+};
-void ExpectNotCalledOnValidThreadWithSequenceTokenAndThreadTaskRunnerHandle(
- ThreadCheckerImpl* thread_checker,
- SequenceToken sequence_token) {
- ThreadTaskRunnerHandle thread_task_runner_handle(
- make_scoped_refptr(new TestSimpleTaskRunner));
- ScopedSetSequenceTokenForCurrentThread
- scoped_set_sequence_token_for_current_thread(sequence_token);
- ExpectNotCalledOnValidThread(thread_checker);
-}
+// Deletes ThreadCheckerClass on a different thread.
+class DeleteThreadCheckerClassOnThread : public base::SimpleThread {
+ public:
+ explicit DeleteThreadCheckerClassOnThread(
+ ThreadCheckerClass* thread_checker_class)
+ : SimpleThread("delete_thread_checker_class_on_thread"),
+ thread_checker_class_(thread_checker_class) {
+ }
-} // namespace
+ void Run() override { thread_checker_class_.reset(); }
-TEST(ThreadCheckerTest, AllowedSameThreadNoSequenceToken) {
- ThreadCheckerImpl thread_checker;
- EXPECT_TRUE(thread_checker.CalledOnValidThread());
-}
+ private:
+ std::unique_ptr<ThreadCheckerClass> thread_checker_class_;
-TEST(ThreadCheckerTest,
- AllowedSameThreadAndSequenceDifferentTasksWithThreadTaskRunnerHandle) {
- ThreadTaskRunnerHandle thread_task_runner_handle(
- make_scoped_refptr(new TestSimpleTaskRunner));
+ DISALLOW_COPY_AND_ASSIGN(DeleteThreadCheckerClassOnThread);
+};
- std::unique_ptr<ThreadCheckerImpl> thread_checker;
- const SequenceToken sequence_token = SequenceToken::Create();
+} // namespace
- {
- ScopedSetSequenceTokenForCurrentThread
- scoped_set_sequence_token_for_current_thread(sequence_token);
- thread_checker.reset(new ThreadCheckerImpl);
- }
+TEST(ThreadCheckerTest, CallsAllowedOnSameThread) {
+ std::unique_ptr<ThreadCheckerClass> thread_checker_class(
+ new ThreadCheckerClass);
- {
- ScopedSetSequenceTokenForCurrentThread
- scoped_set_sequence_token_for_current_thread(sequence_token);
- EXPECT_TRUE(thread_checker->CalledOnValidThread());
- }
-}
+ // Verify that DoStuff doesn't assert.
+ thread_checker_class->DoStuff();
-TEST(ThreadCheckerTest,
- AllowedSameThreadSequenceAndTaskNoThreadTaskRunnerHandle) {
- ScopedSetSequenceTokenForCurrentThread
- scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
- ThreadCheckerImpl thread_checker;
- EXPECT_TRUE(thread_checker.CalledOnValidThread());
+ // Verify that the destructor doesn't assert.
+ thread_checker_class.reset();
}
-TEST(ThreadCheckerTest,
- DisallowedSameThreadAndSequenceDifferentTasksNoThreadTaskRunnerHandle) {
- std::unique_ptr<ThreadCheckerImpl> thread_checker;
-
- {
- ScopedSetSequenceTokenForCurrentThread
- scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
- thread_checker.reset(new ThreadCheckerImpl);
- }
+TEST(ThreadCheckerTest, DestructorAllowedOnDifferentThread) {
+ std::unique_ptr<ThreadCheckerClass> thread_checker_class(
+ new ThreadCheckerClass);
- {
- ScopedSetSequenceTokenForCurrentThread
- scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
- EXPECT_FALSE(thread_checker->CalledOnValidThread());
- }
-}
+ // Verify that the destructor doesn't assert
+ // when called on a different thread.
+ DeleteThreadCheckerClassOnThread delete_on_thread(
+ thread_checker_class.release());
-TEST(ThreadCheckerTest, DisallowedDifferentThreadsNoSequenceToken) {
- ThreadCheckerImpl thread_checker;
- RunCallbackOnNewThreadSynchronously(
- Bind(&ExpectNotCalledOnValidThread, Unretained(&thread_checker)));
+ delete_on_thread.Start();
+ delete_on_thread.Join();
}
-TEST(ThreadCheckerTest, DisallowedDifferentThreadsSameSequence) {
- ThreadTaskRunnerHandle thread_task_runner_handle(
- make_scoped_refptr(new TestSimpleTaskRunner));
- const SequenceToken sequence_token(SequenceToken::Create());
+TEST(ThreadCheckerTest, DetachFromThread) {
+ std::unique_ptr<ThreadCheckerClass> thread_checker_class(
+ new ThreadCheckerClass);
- ScopedSetSequenceTokenForCurrentThread
- scoped_set_sequence_token_for_current_thread(sequence_token);
- ThreadCheckerImpl thread_checker;
- EXPECT_TRUE(thread_checker.CalledOnValidThread());
+ // Verify that DoStuff doesn't assert when called on a different thread after
+ // a call to DetachFromThread.
+ thread_checker_class->DetachFromThread();
+ CallDoStuffOnThread call_on_thread(thread_checker_class.get());
- RunCallbackOnNewThreadSynchronously(Bind(
- &ExpectNotCalledOnValidThreadWithSequenceTokenAndThreadTaskRunnerHandle,
- Unretained(&thread_checker), sequence_token));
+ call_on_thread.Start();
+ call_on_thread.Join();
}
-TEST(ThreadCheckerTest, DisallowedSameThreadDifferentSequence) {
- std::unique_ptr<ThreadCheckerImpl> thread_checker;
+#if GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER
- ThreadTaskRunnerHandle thread_task_runner_handle(
- make_scoped_refptr(new TestSimpleTaskRunner));
+void ThreadCheckerClass::MethodOnDifferentThreadImpl() {
+ std::unique_ptr<ThreadCheckerClass> thread_checker_class(
+ new ThreadCheckerClass);
- {
- ScopedSetSequenceTokenForCurrentThread
- scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
- thread_checker.reset(new ThreadCheckerImpl);
- }
+ // DoStuff should assert in debug builds only when called on a
+ // different thread.
+ CallDoStuffOnThread call_on_thread(thread_checker_class.get());
- {
- // Different SequenceToken.
- ScopedSetSequenceTokenForCurrentThread
- scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
- EXPECT_FALSE(thread_checker->CalledOnValidThread());
- }
+ call_on_thread.Start();
+ call_on_thread.Join();
+}
- // No SequenceToken.
- EXPECT_FALSE(thread_checker->CalledOnValidThread());
+#if ENABLE_THREAD_CHECKER
+TEST(ThreadCheckerDeathTest, MethodNotAllowedOnDifferentThreadInDebug) {
+ ASSERT_DEATH({
+ ThreadCheckerClass::MethodOnDifferentThreadImpl();
+ }, "");
+}
+#else
+TEST(ThreadCheckerTest, MethodAllowedOnDifferentThreadInRelease) {
+ ThreadCheckerClass::MethodOnDifferentThreadImpl();
}
+#endif // ENABLE_THREAD_CHECKER
-TEST(ThreadCheckerTest, DetachFromThread) {
- ThreadCheckerImpl thread_checker;
- thread_checker.DetachFromThread();
+void ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl() {
+ std::unique_ptr<ThreadCheckerClass> thread_checker_class(
+ new ThreadCheckerClass);
+
+ // DoStuff doesn't assert when called on a different thread
+ // after a call to DetachFromThread.
+ thread_checker_class->DetachFromThread();
+ CallDoStuffOnThread call_on_thread(thread_checker_class.get());
- // Verify that CalledOnValidThread() returns true when called on a different
- // thread after a call to DetachFromThread().
- RunCallbackOnNewThreadSynchronously(
- Bind(&ExpectCalledOnValidThread, Unretained(&thread_checker)));
+ call_on_thread.Start();
+ call_on_thread.Join();
- EXPECT_FALSE(thread_checker.CalledOnValidThread());
+ // DoStuff should assert in debug builds only after moving to
+ // another thread.
+ thread_checker_class->DoStuff();
}
-TEST(ThreadCheckerTest, DetachFromThreadWithSequenceToken) {
- ThreadTaskRunnerHandle thread_task_runner_handle(
- make_scoped_refptr(new TestSimpleTaskRunner));
- ScopedSetSequenceTokenForCurrentThread
- scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
- ThreadCheckerImpl thread_checker;
- thread_checker.DetachFromThread();
+#if ENABLE_THREAD_CHECKER
+TEST(ThreadCheckerDeathTest, DetachFromThreadInDebug) {
+ ASSERT_DEATH({
+ ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl();
+ }, "");
+}
+#else
+TEST(ThreadCheckerTest, DetachFromThreadInRelease) {
+ ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl();
+}
+#endif // ENABLE_THREAD_CHECKER
- // Verify that CalledOnValidThread() returns true when called on a different
- // thread after a call to DetachFromThread().
- RunCallbackOnNewThreadSynchronously(
- Bind(&ExpectCalledOnValidThread, Unretained(&thread_checker)));
+#endif // GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER
- EXPECT_FALSE(thread_checker.CalledOnValidThread());
-}
+// Just in case we ever get lumped together with other compilation units.
+#undef ENABLE_THREAD_CHECKER
} // namespace base
diff --git a/base/threading/thread_local.h b/base/threading/thread_local.h
index cad9add3a9..f40420cd2f 100644
--- a/base/threading/thread_local.h
+++ b/base/threading/thread_local.h
@@ -2,34 +2,35 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// WARNING: Thread local storage is a bit tricky to get right. Please make sure
-// that this is really the proper solution for what you're trying to achieve.
-// Don't prematurely optimize, most likely you can just use a Lock.
+// WARNING: Thread local storage is a bit tricky to get right. Please make
+// sure that this is really the proper solution for what you're trying to
+// achieve. Don't prematurely optimize, most likely you can just use a Lock.
//
-// These classes implement a wrapper around ThreadLocalStorage::Slot. On
-// construction, they will allocate a TLS slot, and free the TLS slot on
-// destruction. No memory management (creation or destruction) is handled. This
-// means for uses of ThreadLocalPointer, you must correctly manage the memory
-// yourself, these classes will not destroy the pointer for you. There are no
-// at-thread-exit actions taken by these classes.
+// These classes implement a wrapper around the platform's TLS storage
+// mechanism. On construction, they will allocate a TLS slot, and free the
+// TLS slot on destruction. No memory management (creation or destruction) is
+// handled. This means for uses of ThreadLocalPointer, you must correctly
+// manage the memory yourself, these classes will not destroy the pointer for
+// you. There are no at-thread-exit actions taken by these classes.
//
-// ThreadLocalPointer<Type> wraps a Type*. It performs no creation or
-// destruction, so memory management must be handled elsewhere. The first call
-// to Get() on a thread will return NULL. You can update the pointer with a call
-// to Set().
+// ThreadLocalPointer<Type> wraps a Type*. It performs no creation or
+// destruction, so memory management must be handled elsewhere. The first call
+// to Get() on a thread will return NULL. You can update the pointer with a
+// call to Set().
//
-// ThreadLocalBoolean wraps a bool. It will default to false if it has never
+// ThreadLocalBoolean wraps a bool. It will default to false if it has never
// been set otherwise with Set().
//
-// Thread Safety: An instance of ThreadLocalStorage is completely thread safe
-// once it has been created. If you want to dynamically create an instance, you
-// must of course properly deal with safety and race conditions. This means a
-// function-level static initializer is generally inappropiate.
+// Thread Safety: An instance of ThreadLocalStorage is completely thread safe
+// once it has been created. If you want to dynamically create an instance,
+// you must of course properly deal with safety and race conditions. This
+// means a function-level static initializer is generally inappropiate.
//
-// In Android, the system TLS is limited.
+// In Android, the system TLS is limited, the implementation is backed with
+// ThreadLocalStorage.
//
// Example usage:
-// // My class is logically attached to a single thread. We cache a pointer
+// // My class is logically attached to a single thread. We cache a pointer
// // on the thread it was created on, so we can implement current().
// MyClass::MyClass() {
// DCHECK(Singleton<ThreadLocalPointer<MyClass> >::get()->Get() == NULL);
@@ -50,42 +51,76 @@
#ifndef BASE_THREADING_THREAD_LOCAL_H_
#define BASE_THREADING_THREAD_LOCAL_H_
+#include "base/base_export.h"
#include "base/macros.h"
#include "base/threading/thread_local_storage.h"
+#include "build/build_config.h"
+
+#if defined(OS_POSIX)
+#include <pthread.h>
+#endif
namespace base {
+namespace internal {
+
+// Helper functions that abstract the cross-platform APIs. Do not use directly.
+struct BASE_EXPORT ThreadLocalPlatform {
+#if defined(OS_WIN)
+ typedef unsigned long SlotType;
+#elif defined(OS_ANDROID)
+ typedef ThreadLocalStorage::StaticSlot SlotType;
+#elif defined(OS_POSIX)
+ typedef pthread_key_t SlotType;
+#endif
+
+ static void AllocateSlot(SlotType* slot);
+ static void FreeSlot(SlotType slot);
+ static void* GetValueFromSlot(SlotType slot);
+ static void SetValueInSlot(SlotType slot, void* value);
+};
+
+} // namespace internal
template <typename Type>
class ThreadLocalPointer {
public:
- ThreadLocalPointer() = default;
- ~ThreadLocalPointer() = default;
+ ThreadLocalPointer() : slot_() {
+ internal::ThreadLocalPlatform::AllocateSlot(&slot_);
+ }
+
+ ~ThreadLocalPointer() {
+ internal::ThreadLocalPlatform::FreeSlot(slot_);
+ }
Type* Get() {
- return static_cast<Type*>(slot_.Get());
+ return static_cast<Type*>(
+ internal::ThreadLocalPlatform::GetValueFromSlot(slot_));
}
void Set(Type* ptr) {
- slot_.Set(const_cast<void*>(static_cast<const void*>(ptr)));
+ internal::ThreadLocalPlatform::SetValueInSlot(
+ slot_, const_cast<void*>(static_cast<const void*>(ptr)));
}
private:
- ThreadLocalStorage::Slot slot_;
+ typedef internal::ThreadLocalPlatform::SlotType SlotType;
+
+ SlotType slot_;
DISALLOW_COPY_AND_ASSIGN(ThreadLocalPointer<Type>);
};
class ThreadLocalBoolean {
public:
- ThreadLocalBoolean() = default;
- ~ThreadLocalBoolean() = default;
+ ThreadLocalBoolean() {}
+ ~ThreadLocalBoolean() {}
bool Get() {
- return tlp_.Get() != nullptr;
+ return tlp_.Get() != NULL;
}
void Set(bool val) {
- tlp_.Set(val ? this : nullptr);
+ tlp_.Set(val ? this : NULL);
}
private:
diff --git a/base/threading/thread_local_posix.cc b/base/threading/thread_local_posix.cc
new file mode 100644
index 0000000000..8bc46ad190
--- /dev/null
+++ b/base/threading/thread_local_posix.cc
@@ -0,0 +1,43 @@
+// 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/threading/thread_local.h"
+
+#include <pthread.h>
+
+#include "base/logging.h"
+#include "build/build_config.h"
+
+#if !defined(OS_ANDROID)
+
+namespace base {
+namespace internal {
+
+// static
+void ThreadLocalPlatform::AllocateSlot(SlotType* slot) {
+ int error = pthread_key_create(slot, NULL);
+ CHECK_EQ(error, 0);
+}
+
+// static
+void ThreadLocalPlatform::FreeSlot(SlotType slot) {
+ int error = pthread_key_delete(slot);
+ DCHECK_EQ(0, error);
+}
+
+// static
+void* ThreadLocalPlatform::GetValueFromSlot(SlotType slot) {
+ return pthread_getspecific(slot);
+}
+
+// static
+void ThreadLocalPlatform::SetValueInSlot(SlotType slot, void* value) {
+ int error = pthread_setspecific(slot, value);
+ DCHECK_EQ(error, 0);
+}
+
+} // namespace internal
+} // namespace base
+
+#endif // !defined(OS_ANDROID)
diff --git a/base/threading/thread_local_storage.cc b/base/threading/thread_local_storage.cc
index 48c1dd58c2..a7eb527888 100644
--- a/base/threading/thread_local_storage.cc
+++ b/base/threading/thread_local_storage.cc
@@ -6,57 +6,10 @@
#include "base/atomicops.h"
#include "base/logging.h"
-#include "base/synchronization/lock.h"
#include "build/build_config.h"
using base::internal::PlatformThreadLocalStorage;
-// Chrome Thread Local Storage (TLS)
-//
-// This TLS system allows Chrome to use a single OS level TLS slot process-wide,
-// and allows us to control the slot limits instead of being at the mercy of the
-// platform. To do this, Chrome TLS replicates an array commonly found in the OS
-// thread metadata.
-//
-// Overview:
-//
-// OS TLS Slots Per-Thread Per-Process Global
-// ...
-// [] Chrome TLS Array Chrome TLS Metadata
-// [] ----------> [][][][][ ][][][][] [][][][][ ][][][][]
-// [] | |
-// ... V V
-// Metadata Version Slot Information
-// Your Data!
-//
-// Using a single OS TLS slot, Chrome TLS allocates an array on demand for the
-// lifetime of each thread that requests Chrome TLS data. Each per-thread TLS
-// array matches the length of the per-process global metadata array.
-//
-// A per-process global TLS metadata array tracks information about each item in
-// the per-thread array:
-// * Status: Tracks if the slot is allocated or free to assign.
-// * Destructor: An optional destructor to call on thread destruction for that
-// specific slot.
-// * Version: Tracks the current version of the TLS slot. Each TLS slot
-// allocation is associated with a unique version number.
-//
-// Most OS TLS APIs guarantee that a newly allocated TLS slot is
-// initialized to 0 for all threads. The Chrome TLS system provides
-// this guarantee by tracking the version for each TLS slot here
-// on each per-thread Chrome TLS array entry. Threads that access
-// a slot with a mismatched version will receive 0 as their value.
-// The metadata version is incremented when the client frees a
-// slot. The per-thread metadata version is updated when a client
-// writes to the slot. This scheme allows for constant time
-// invalidation and avoids the need to iterate through each Chrome
-// TLS array to mark the slot as zero.
-//
-// Just like an OS TLS API, clients of the Chrome TLS are responsible for
-// managing any necessary lifetime of the data in their slots. The only
-// convenience provided is automatic destruction when a thread ends. If a client
-// frees a slot, that client is responsible for destroying the data in the slot.
-
namespace {
// In order to make TLS destructors work, we need to keep around a function
// pointer to the destructor for each slot. We keep this array of pointers in a
@@ -65,42 +18,37 @@ namespace {
// hold a pointer to a per-thread array (table) of slots that we allocate to
// Chromium consumers.
-// g_native_tls_key is the one native TLS that we use. It stores our table.
+// g_native_tls_key is the one native TLS that we use. It stores our table.
base::subtle::Atomic32 g_native_tls_key =
PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES;
-// The maximum number of slots in our thread local storage stack.
-constexpr int kThreadLocalStorageSize = 256;
-constexpr int kInvalidSlotValue = -1;
-
-enum TlsStatus {
- FREE,
- IN_USE,
-};
-
-struct TlsMetadata {
- TlsStatus status;
- base::ThreadLocalStorage::TLSDestructorFunc destructor;
- uint32_t version;
-};
+// g_last_used_tls_key is the high-water-mark of allocated thread local storage.
+// Each allocation is an index into our g_tls_destructors[]. Each such index is
+// assigned to the instance variable slot_ in a ThreadLocalStorage::Slot
+// instance. We reserve the value slot_ == 0 to indicate that the corresponding
+// instance of ThreadLocalStorage::Slot has been freed (i.e., destructor called,
+// etc.). This reserved use of 0 is then stated as the initial value of
+// g_last_used_tls_key, so that the first issued index will be 1.
+base::subtle::Atomic32 g_last_used_tls_key = 0;
-struct TlsVectorEntry {
- void* data;
- uint32_t version;
-};
-
-// This lock isn't needed until after we've constructed the per-thread TLS
-// vector, so it's safe to use.
-base::Lock* GetTLSMetadataLock() {
- static auto* lock = new base::Lock();
- return lock;
-}
-TlsMetadata g_tls_metadata[kThreadLocalStorageSize];
-size_t g_last_assigned_slot = 0;
+// The maximum number of 'slots' in our thread local storage stack.
+const int kThreadLocalStorageSize = 256;
// The maximum number of times to try to clear slots by calling destructors.
// Use pthread naming convention for clarity.
-constexpr int kMaxDestructorIterations = kThreadLocalStorageSize;
+const int kMaxDestructorIterations = kThreadLocalStorageSize;
+
+// An array of destructor function pointers for the slots. If a slot has a
+// destructor, it will be stored in its corresponding entry in this array.
+// The elements are volatile to ensure that when the compiler reads the value
+// to potentially call the destructor, it does so once, and that value is tested
+// for null-ness and then used. Yes, that would be a weird de-optimization,
+// but I can imagine some register machines where it was just as easy to
+// re-fetch an array element, and I want to be sure a call to free the key
+// (i.e., null out the destructor entry) that happens on a separate thread can't
+// hurt the racy calls to the destructors on another thread.
+volatile base::ThreadLocalStorage::TLSDestructorFunc
+ g_tls_destructors[kThreadLocalStorageSize];
// This function is called to initialize our entire Chromium TLS system.
// It may be called very early, and we need to complete most all of the setup
@@ -108,7 +56,7 @@ constexpr int kMaxDestructorIterations = kThreadLocalStorageSize;
// recursively depend on this initialization.
// As a result, we use Atomics, and avoid anything (like a singleton) that might
// require memory allocations.
-TlsVectorEntry* ConstructTlsVector() {
+void** ConstructTlsVector() {
PlatformThreadLocalStorage::TLSKey key =
base::subtle::NoBarrier_Load(&g_native_tls_key);
if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES) {
@@ -125,8 +73,8 @@ TlsVectorEntry* ConstructTlsVector() {
key != PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES);
PlatformThreadLocalStorage::FreeTLS(tmp);
}
- // Atomically test-and-set the tls_key. If the key is
- // TLS_KEY_OUT_OF_INDEXES, go ahead and set it. Otherwise, do nothing, as
+ // Atomically test-and-set the tls_key. If the key is
+ // TLS_KEY_OUT_OF_INDEXES, go ahead and set it. Otherwise, do nothing, as
// another thread already did our dirty work.
if (PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES !=
static_cast<PlatformThreadLocalStorage::TLSKey>(
@@ -142,38 +90,39 @@ TlsVectorEntry* ConstructTlsVector() {
}
CHECK(!PlatformThreadLocalStorage::GetTLSValue(key));
- // Some allocators, such as TCMalloc, make use of thread local storage. As a
- // result, any attempt to call new (or malloc) will lazily cause such a system
- // to initialize, which will include registering for a TLS key. If we are not
- // careful here, then that request to create a key will call new back, and
- // we'll have an infinite loop. We avoid that as follows: Use a stack
- // allocated vector, so that we don't have dependence on our allocator until
- // our service is in place. (i.e., don't even call new until after we're
- // setup)
- TlsVectorEntry stack_allocated_tls_data[kThreadLocalStorageSize];
+ // Some allocators, such as TCMalloc, make use of thread local storage.
+ // As a result, any attempt to call new (or malloc) will lazily cause such a
+ // system to initialize, which will include registering for a TLS key. If we
+ // are not careful here, then that request to create a key will call new back,
+ // and we'll have an infinite loop. We avoid that as follows:
+ // Use a stack allocated vector, so that we don't have dependence on our
+ // allocator until our service is in place. (i.e., don't even call new until
+ // after we're setup)
+ void* stack_allocated_tls_data[kThreadLocalStorageSize];
memset(stack_allocated_tls_data, 0, sizeof(stack_allocated_tls_data));
// Ensure that any rentrant calls change the temp version.
PlatformThreadLocalStorage::SetTLSValue(key, stack_allocated_tls_data);
// Allocate an array to store our data.
- TlsVectorEntry* tls_data = new TlsVectorEntry[kThreadLocalStorageSize];
+ void** tls_data = new void*[kThreadLocalStorageSize];
memcpy(tls_data, stack_allocated_tls_data, sizeof(stack_allocated_tls_data));
PlatformThreadLocalStorage::SetTLSValue(key, tls_data);
return tls_data;
}
-void OnThreadExitInternal(TlsVectorEntry* tls_data) {
- DCHECK(tls_data);
- // Some allocators, such as TCMalloc, use TLS. As a result, when a thread
+void OnThreadExitInternal(void* value) {
+ DCHECK(value);
+ void** tls_data = static_cast<void**>(value);
+ // Some allocators, such as TCMalloc, use TLS. As a result, when a thread
// terminates, one of the destructor calls we make may be to shut down an
- // allocator. We have to be careful that after we've shutdown all of the known
- // destructors (perchance including an allocator), that we don't call the
- // allocator and cause it to resurrect itself (with no possibly destructor
- // call to follow). We handle this problem as follows: Switch to using a stack
- // allocated vector, so that we don't have dependence on our allocator after
- // we have called all g_tls_metadata destructors. (i.e., don't even call
- // delete[] after we're done with destructors.)
- TlsVectorEntry stack_allocated_tls_data[kThreadLocalStorageSize];
+ // allocator. We have to be careful that after we've shutdown all of the
+ // known destructors (perchance including an allocator), that we don't call
+ // the allocator and cause it to resurrect itself (with no possibly destructor
+ // call to follow). We handle this problem as follows:
+ // Switch to using a stack allocated vector, so that we don't have dependence
+ // on our allocator after we have called all g_tls_destructors. (i.e., don't
+ // even call delete[] after we're done with destructors.)
+ void* stack_allocated_tls_data[kThreadLocalStorageSize];
memcpy(stack_allocated_tls_data, tls_data, sizeof(stack_allocated_tls_data));
// Ensure that any re-entrant calls change the temp version.
PlatformThreadLocalStorage::TLSKey key =
@@ -181,38 +130,32 @@ void OnThreadExitInternal(TlsVectorEntry* tls_data) {
PlatformThreadLocalStorage::SetTLSValue(key, stack_allocated_tls_data);
delete[] tls_data; // Our last dependence on an allocator.
- // Snapshot the TLS Metadata so we don't have to lock on every access.
- TlsMetadata tls_metadata[kThreadLocalStorageSize];
- {
- base::AutoLock auto_lock(*GetTLSMetadataLock());
- memcpy(tls_metadata, g_tls_metadata, sizeof(g_tls_metadata));
- }
-
int remaining_attempts = kMaxDestructorIterations;
bool need_to_scan_destructors = true;
while (need_to_scan_destructors) {
need_to_scan_destructors = false;
// Try to destroy the first-created-slot (which is slot 1) in our last
- // destructor call. That user was able to function, and define a slot with
+ // destructor call. That user was able to function, and define a slot with
// no other services running, so perhaps it is a basic service (like an
- // allocator) and should also be destroyed last. If we get the order wrong,
- // then we'll iterate several more times, so it is really not that critical
- // (but it might help).
- for (int slot = 0; slot < kThreadLocalStorageSize ; ++slot) {
- void* tls_value = stack_allocated_tls_data[slot].data;
- if (!tls_value || tls_metadata[slot].status == TlsStatus::FREE ||
- stack_allocated_tls_data[slot].version != tls_metadata[slot].version)
+ // allocator) and should also be destroyed last. If we get the order wrong,
+ // then we'll itterate several more times, so it is really not that
+ // critical (but it might help).
+ base::subtle::Atomic32 last_used_tls_key =
+ base::subtle::NoBarrier_Load(&g_last_used_tls_key);
+ for (int slot = last_used_tls_key; slot > 0; --slot) {
+ void* tls_value = stack_allocated_tls_data[slot];
+ if (tls_value == NULL)
continue;
base::ThreadLocalStorage::TLSDestructorFunc destructor =
- tls_metadata[slot].destructor;
- if (!destructor)
+ g_tls_destructors[slot];
+ if (destructor == NULL)
continue;
- stack_allocated_tls_data[slot].data = nullptr; // pre-clear the slot.
+ stack_allocated_tls_data[slot] = NULL; // pre-clear the slot.
destructor(tls_value);
- // Any destructor might have called a different service, which then set a
- // different slot to a non-null value. Hence we need to check the whole
- // vector again. This is a pthread standard.
+ // Any destructor might have called a different service, which then set
+ // a different slot to a non-NULL value. Hence we need to check
+ // the whole vector again. This is a pthread standard.
need_to_scan_destructors = true;
}
if (--remaining_attempts <= 0) {
@@ -222,7 +165,7 @@ void OnThreadExitInternal(TlsVectorEntry* tls_data) {
}
// Remove our stack allocated vector.
- PlatformThreadLocalStorage::SetTLSValue(key, nullptr);
+ PlatformThreadLocalStorage::SetTLSValue(key, NULL);
}
} // namespace
@@ -241,107 +184,69 @@ void PlatformThreadLocalStorage::OnThreadExit() {
// Maybe we have never initialized TLS for this thread.
if (!tls_data)
return;
- OnThreadExitInternal(static_cast<TlsVectorEntry*>(tls_data));
+ OnThreadExitInternal(tls_data);
}
#elif defined(OS_POSIX)
void PlatformThreadLocalStorage::OnThreadExit(void* value) {
- OnThreadExitInternal(static_cast<TlsVectorEntry*>(value));
+ OnThreadExitInternal(value);
}
#endif // defined(OS_WIN)
} // namespace internal
+ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) {
+ slot_ = 0;
+ base::subtle::Release_Store(&initialized_, 0);
+ Initialize(destructor);
+}
+
void ThreadLocalStorage::StaticSlot::Initialize(TLSDestructorFunc destructor) {
PlatformThreadLocalStorage::TLSKey key =
base::subtle::NoBarrier_Load(&g_native_tls_key);
if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES ||
- !PlatformThreadLocalStorage::GetTLSValue(key)) {
+ !PlatformThreadLocalStorage::GetTLSValue(key))
ConstructTlsVector();
- }
// Grab a new slot.
- slot_ = kInvalidSlotValue;
- version_ = 0;
- {
- base::AutoLock auto_lock(*GetTLSMetadataLock());
- for (int i = 0; i < kThreadLocalStorageSize; ++i) {
- // Tracking the last assigned slot is an attempt to find the next
- // available slot within one iteration. Under normal usage, slots remain
- // in use for the lifetime of the process (otherwise before we reclaimed
- // slots, we would have run out of slots). This makes it highly likely the
- // next slot is going to be a free slot.
- size_t slot_candidate =
- (g_last_assigned_slot + 1 + i) % kThreadLocalStorageSize;
- if (g_tls_metadata[slot_candidate].status == TlsStatus::FREE) {
- g_tls_metadata[slot_candidate].status = TlsStatus::IN_USE;
- g_tls_metadata[slot_candidate].destructor = destructor;
- g_last_assigned_slot = slot_candidate;
- slot_ = slot_candidate;
- version_ = g_tls_metadata[slot_candidate].version;
- break;
- }
- }
- }
- CHECK_NE(slot_, kInvalidSlotValue);
+ slot_ = base::subtle::NoBarrier_AtomicIncrement(&g_last_used_tls_key, 1);
+ DCHECK_GT(slot_, 0);
CHECK_LT(slot_, kThreadLocalStorageSize);
// Setup our destructor.
+ g_tls_destructors[slot_] = destructor;
base::subtle::Release_Store(&initialized_, 1);
}
void ThreadLocalStorage::StaticSlot::Free() {
- DCHECK_NE(slot_, kInvalidSlotValue);
+ // At this time, we don't reclaim old indices for TLS slots.
+ // So all we need to do is wipe the destructor.
+ DCHECK_GT(slot_, 0);
DCHECK_LT(slot_, kThreadLocalStorageSize);
- {
- base::AutoLock auto_lock(*GetTLSMetadataLock());
- g_tls_metadata[slot_].status = TlsStatus::FREE;
- g_tls_metadata[slot_].destructor = nullptr;
- ++(g_tls_metadata[slot_].version);
- }
- slot_ = kInvalidSlotValue;
+ g_tls_destructors[slot_] = NULL;
+ slot_ = 0;
base::subtle::Release_Store(&initialized_, 0);
}
void* ThreadLocalStorage::StaticSlot::Get() const {
- TlsVectorEntry* tls_data = static_cast<TlsVectorEntry*>(
+ void** tls_data = static_cast<void**>(
PlatformThreadLocalStorage::GetTLSValue(
base::subtle::NoBarrier_Load(&g_native_tls_key)));
if (!tls_data)
tls_data = ConstructTlsVector();
- DCHECK_NE(slot_, kInvalidSlotValue);
+ DCHECK_GT(slot_, 0);
DCHECK_LT(slot_, kThreadLocalStorageSize);
- // Version mismatches means this slot was previously freed.
- if (tls_data[slot_].version != version_)
- return nullptr;
- return tls_data[slot_].data;
+ return tls_data[slot_];
}
void ThreadLocalStorage::StaticSlot::Set(void* value) {
- TlsVectorEntry* tls_data = static_cast<TlsVectorEntry*>(
+ void** tls_data = static_cast<void**>(
PlatformThreadLocalStorage::GetTLSValue(
base::subtle::NoBarrier_Load(&g_native_tls_key)));
if (!tls_data)
tls_data = ConstructTlsVector();
- DCHECK_NE(slot_, kInvalidSlotValue);
+ DCHECK_GT(slot_, 0);
DCHECK_LT(slot_, kThreadLocalStorageSize);
- tls_data[slot_].data = value;
- tls_data[slot_].version = version_;
-}
-
-ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) {
- tls_slot_.Initialize(destructor);
-}
-
-ThreadLocalStorage::Slot::~Slot() {
- tls_slot_.Free();
-}
-
-void* ThreadLocalStorage::Slot::Get() const {
- return tls_slot_.Get();
-}
-
-void ThreadLocalStorage::Slot::Set(void* value) {
- tls_slot_.Set(value);
+ tls_data[slot_] = value;
}
} // namespace base
diff --git a/base/threading/thread_local_storage.h b/base/threading/thread_local_storage.h
index 5e70410af9..0c7a692a66 100644
--- a/base/threading/thread_local_storage.h
+++ b/base/threading/thread_local_storage.h
@@ -5,8 +5,6 @@
#ifndef BASE_THREADING_THREAD_LOCAL_STORAGE_H_
#define BASE_THREADING_THREAD_LOCAL_STORAGE_H_
-#include <stdint.h>
-
#include "base/atomicops.h"
#include "base/base_export.h"
#include "base/macros.h"
@@ -22,12 +20,9 @@ namespace base {
namespace internal {
-// WARNING: You should *NOT* use this class directly.
-// PlatformThreadLocalStorage is a low-level abstraction of the OS's TLS
-// interface. Instead, you should use one of the following:
-// * ThreadLocalBoolean (from thread_local.h) for booleans.
-// * ThreadLocalPointer (from thread_local.h) for pointers.
-// * ThreadLocalStorage::StaticSlot/Slot for more direct control of the slot.
+// WARNING: You should *NOT* be using this class directly.
+// PlatformThreadLocalStorage is low-level abstraction to the OS's TLS
+// interface, you should instead be using ThreadLocalStorage::StaticSlot/Slot.
class BASE_EXPORT PlatformThreadLocalStorage {
public:
@@ -94,7 +89,7 @@ class BASE_EXPORT ThreadLocalStorage {
// initialization, as base's LINKER_INITIALIZED requires a constructor and on
// some compilers (notably gcc 4.4) this still ends up needing runtime
// initialization.
-#define TLS_INITIALIZER {false, 0, 0}
+ #define TLS_INITIALIZER {false, 0}
// A key representing one value stored in TLS.
// Initialize like
@@ -128,25 +123,18 @@ class BASE_EXPORT ThreadLocalStorage {
// The internals of this struct should be considered private.
base::subtle::Atomic32 initialized_;
int slot_;
- uint32_t version_;
};
// A convenience wrapper around StaticSlot with a constructor. Can be used
// as a member variable.
- class BASE_EXPORT Slot {
+ class BASE_EXPORT Slot : public StaticSlot {
public:
+ // Calls StaticSlot::Initialize().
explicit Slot(TLSDestructorFunc destructor = NULL);
- ~Slot();
-
- // Get the thread-local value stored in this slot.
- // Values are guaranteed to initially be zero.
- void* Get() const;
-
- // Set the slot's thread-local value to |value|.
- void Set(void* value);
private:
- StaticSlot tls_slot_;
+ using StaticSlot::initialized_;
+ using StaticSlot::slot_;
DISALLOW_COPY_AND_ASSIGN(Slot);
};
diff --git a/base/threading/thread_local_storage_unittest.cc b/base/threading/thread_local_storage_unittest.cc
index 335252b18e..322524b10e 100644
--- a/base/threading/thread_local_storage_unittest.cc
+++ b/base/threading/thread_local_storage_unittest.cc
@@ -127,14 +127,4 @@ TEST(ThreadLocalStorageTest, MAYBE_TLSDestructors) {
tls_slot.Free(); // Stop doing callbacks to cleanup threads.
}
-TEST(ThreadLocalStorageTest, TLSReclaim) {
- // Creates and destroys many TLS slots and ensures they all zero-inited.
- for (int i = 0; i < 1000; ++i) {
- ThreadLocalStorage::Slot slot(nullptr);
- EXPECT_EQ(nullptr, slot.Get());
- slot.Set(reinterpret_cast<void*>(0xBAADF00D));
- EXPECT_EQ(reinterpret_cast<void*>(0xBAADF00D), slot.Get());
- }
-}
-
} // namespace base
diff --git a/base/threading/thread_restrictions.cc b/base/threading/thread_restrictions.cc
index 8dd7743332..00306c5ae7 100644
--- a/base/threading/thread_restrictions.cc
+++ b/base/threading/thread_restrictions.cc
@@ -4,7 +4,7 @@
#include "base/threading/thread_restrictions.h"
-#if DCHECK_IS_ON()
+#if ENABLE_THREAD_RESTRICTIONS
#include "base/lazy_instance.h"
#include "base/logging.h"
@@ -35,7 +35,7 @@ bool ThreadRestrictions::SetIOAllowed(bool allowed) {
// static
void ThreadRestrictions::AssertIOAllowed() {
if (g_io_disallowed.Get().Get()) {
- NOTREACHED() <<
+ LOG(FATAL) <<
"Function marked as IO-only was called from a thread that "
"disallows IO! If this thread really should be allowed to "
"make IO calls, adjust the call to "
@@ -54,14 +54,10 @@ bool ThreadRestrictions::SetSingletonAllowed(bool allowed) {
// static
void ThreadRestrictions::AssertSingletonAllowed() {
if (g_singleton_disallowed.Get().Get()) {
- NOTREACHED() << "LazyInstance/Singleton is not allowed to be used on this "
- << "thread. Most likely it's because this thread is not "
- << "joinable (or the current task is running with "
- << "TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN semantics), so "
- << "AtExitManager may have deleted the object on shutdown, "
- << "leading to a potential shutdown crash. If you need to use "
- << "the object from this context, it'll have to be updated to "
- << "use Leaky traits.";
+ LOG(FATAL) << "LazyInstance/Singleton is not allowed to be used on this "
+ << "thread. Most likely it's because this thread is not "
+ << "joinable, so AtExitManager may have deleted the object "
+ << "on shutdown, leading to a potential shutdown crash.";
}
}
@@ -73,8 +69,8 @@ void ThreadRestrictions::DisallowWaiting() {
// static
void ThreadRestrictions::AssertWaitAllowed() {
if (g_wait_disallowed.Get().Get()) {
- NOTREACHED() << "Waiting is not allowed to be used on this thread to "
- << "prevent jank and deadlock.";
+ LOG(FATAL) << "Waiting is not allowed to be used on this thread to prevent "
+ << "jank and deadlock.";
}
}
@@ -86,4 +82,4 @@ bool ThreadRestrictions::SetWaitAllowed(bool allowed) {
} // namespace base
-#endif // DCHECK_IS_ON()
+#endif // ENABLE_THREAD_RESTRICTIONS
diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h
index a86dd452b8..4212a4b6eb 100644
--- a/base/threading/thread_restrictions.h
+++ b/base/threading/thread_restrictions.h
@@ -6,9 +6,15 @@
#define BASE_THREADING_THREAD_RESTRICTIONS_H_
#include "base/base_export.h"
-#include "base/logging.h"
#include "base/macros.h"
+// See comment at top of thread_checker.h
+#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
+#define ENABLE_THREAD_RESTRICTIONS 1
+#else
+#define ENABLE_THREAD_RESTRICTIONS 0
+#endif
+
class BrowserProcessImpl;
class HistogramSynchronizer;
class NativeBackendKWallet;
@@ -51,10 +57,10 @@ namespace gpu {
class GpuChannelHost;
}
namespace mojo {
-class SyncCallRestrictions;
-namespace edk {
-class ScopedIPCSupport;
+namespace common {
+class MessagePumpMojo;
}
+class SyncCallRestrictions;
}
namespace ui {
class CommandBufferClientImpl;
@@ -86,10 +92,6 @@ namespace android {
class JavaHandlerThread;
}
-namespace internal {
-class TaskTracker;
-}
-
class SequencedWorkerPool;
class SimpleThread;
class Thread;
@@ -135,7 +137,21 @@ class BASE_EXPORT ThreadRestrictions {
DISALLOW_COPY_AND_ASSIGN(ScopedAllowIO);
};
-#if DCHECK_IS_ON()
+ // Constructing a ScopedAllowSingleton temporarily allows accessing for the
+ // current thread. Doing this is almost always incorrect.
+ class BASE_EXPORT ScopedAllowSingleton {
+ public:
+ ScopedAllowSingleton() { previous_value_ = SetSingletonAllowed(true); }
+ ~ScopedAllowSingleton() { SetSingletonAllowed(previous_value_); }
+ private:
+ // Whether singleton use is allowed when the ScopedAllowSingleton was
+ // constructed.
+ bool previous_value_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedAllowSingleton);
+ };
+
+#if ENABLE_THREAD_RESTRICTIONS
// Set whether the current thread to make IO calls.
// Threads start out in the *allowed* state.
// Returns the previous value.
@@ -181,7 +197,6 @@ class BASE_EXPORT ThreadRestrictions {
friend class content::ScopedAllowWaitForAndroidLayoutTests;
friend class content::ScopedAllowWaitForDebugURL;
friend class ::HistogramSynchronizer;
- friend class internal::TaskTracker;
friend class ::ScopedAllowWaitForLegacyWebViewApi;
friend class cc::CompletionEvent;
friend class cc::SingleThreadTaskGraphRunner;
@@ -195,8 +210,8 @@ class BASE_EXPORT ThreadRestrictions {
friend class ThreadTestHelper;
friend class PlatformThread;
friend class android::JavaHandlerThread;
+ friend class mojo::common::MessagePumpMojo;
friend class mojo::SyncCallRestrictions;
- friend class mojo::edk::ScopedIPCSupport;
friend class ui::CommandBufferClientImpl;
friend class ui::CommandBufferLocal;
friend class ui::GpuState;
@@ -225,7 +240,7 @@ class BASE_EXPORT ThreadRestrictions {
friend class views::ScreenMus;
// END USAGE THAT NEEDS TO BE FIXED.
-#if DCHECK_IS_ON()
+#if ENABLE_THREAD_RESTRICTIONS
static bool SetWaitAllowed(bool allowed);
#else
static bool SetWaitAllowed(bool) { return true; }
diff --git a/base/threading/thread_task_runner_handle.cc b/base/threading/thread_task_runner_handle.cc
index 00deaa4e20..190e18ffc6 100644
--- a/base/threading/thread_task_runner_handle.cc
+++ b/base/threading/thread_task_runner_handle.cc
@@ -6,10 +6,8 @@
#include <utility>
-#include "base/bind.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
-#include "base/memory/ptr_util.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_local.h"
@@ -34,50 +32,6 @@ bool ThreadTaskRunnerHandle::IsSet() {
return !!lazy_tls_ptr.Pointer()->Get();
}
-// static
-ScopedClosureRunner ThreadTaskRunnerHandle::OverrideForTesting(
- scoped_refptr<SingleThreadTaskRunner> overriding_task_runner) {
- // OverrideForTesting() is not compatible with a SequencedTaskRunnerHandle
- // being set (but SequencedTaskRunnerHandle::IsSet() includes
- // ThreadTaskRunnerHandle::IsSet() so that's discounted as the only valid
- // excuse for it to be true). Sadly this means that tests that merely need a
- // SequencedTaskRunnerHandle on their main thread can be forced to use a
- // ThreadTaskRunnerHandle if they're also using test task runners (that
- // OverrideForTesting() when running their tasks from said main thread). To
- // solve this: sequence_task_runner_handle.cc and thread_task_runner_handle.cc
- // would have to be merged into a single impl file and share TLS state. This
- // was deemed unecessary for now as most tests should use higher level
- // constructs and not have to instantiate task runner handles on their own.
- DCHECK(!SequencedTaskRunnerHandle::IsSet() || IsSet());
-
- if (!IsSet()) {
- std::unique_ptr<ThreadTaskRunnerHandle> top_level_ttrh =
- MakeUnique<ThreadTaskRunnerHandle>(std::move(overriding_task_runner));
- return ScopedClosureRunner(base::Bind(
- [](std::unique_ptr<ThreadTaskRunnerHandle>) {},
- base::Passed(&top_level_ttrh)));
- }
-
- ThreadTaskRunnerHandle* ttrh = lazy_tls_ptr.Pointer()->Get();
- // Swap the two (and below bind |overriding_task_runner|, which is now the
- // previous one, as the |task_runner_to_restore|).
- ttrh->task_runner_.swap(overriding_task_runner);
-
- return ScopedClosureRunner(base::Bind(
- [](scoped_refptr<SingleThreadTaskRunner> task_runner_to_restore,
- SingleThreadTaskRunner* expected_task_runner_before_restore) {
- ThreadTaskRunnerHandle* ttrh = lazy_tls_ptr.Pointer()->Get();
-
- DCHECK_EQ(expected_task_runner_before_restore, ttrh->task_runner_.get())
- << "Nested overrides must expire their ScopedClosureRunners "
- "in LIFO order.";
-
- ttrh->task_runner_.swap(task_runner_to_restore);
- },
- base::Passed(&overriding_task_runner),
- base::Unretained(ttrh->task_runner_.get())));
-}
-
ThreadTaskRunnerHandle::ThreadTaskRunnerHandle(
scoped_refptr<SingleThreadTaskRunner> task_runner)
: task_runner_(std::move(task_runner)) {
diff --git a/base/threading/thread_task_runner_handle.h b/base/threading/thread_task_runner_handle.h
index 7ae85e6dcf..c8e58935f0 100644
--- a/base/threading/thread_task_runner_handle.h
+++ b/base/threading/thread_task_runner_handle.h
@@ -6,7 +6,6 @@
#define BASE_THREADING_THREAD_TASK_RUNNER_HANDLE_H_
#include "base/base_export.h"
-#include "base/callback_helpers.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
@@ -27,17 +26,6 @@ class BASE_EXPORT ThreadTaskRunnerHandle {
// the current thread.
static bool IsSet();
- // Overrides ThreadTaskRunnerHandle::Get()'s |task_runner_| to point at
- // |overriding_task_runner| until the returned ScopedClosureRunner goes out of
- // scope (instantiates a ThreadTaskRunnerHandle for that scope if |!IsSet()|).
- // Nested overrides are allowed but callers must ensure the
- // ScopedClosureRunners expire in LIFO (stack) order. Note: nesting
- // ThreadTaskRunnerHandles isn't generally desired but it's useful in unit
- // tests where multiple task runners can share the main thread for simplicity
- // and determinism.
- static ScopedClosureRunner OverrideForTesting(
- scoped_refptr<SingleThreadTaskRunner> overriding_task_runner);
-
// Binds |task_runner| to the current thread. |task_runner| must belong
// to the current thread for this to succeed.
explicit ThreadTaskRunnerHandle(
diff --git a/base/threading/thread_unittest.cc b/base/threading/thread_unittest.cc
index af8347432b..b0fd26521a 100644
--- a/base/threading/thread_unittest.cc
+++ b/base/threading/thread_unittest.cc
@@ -5,22 +5,13 @@
#include "base/threading/thread.h"
#include <stddef.h>
-#include <stdint.h>
#include <vector>
#include "base/bind.h"
-#include "base/debug/leak_annotations.h"
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
+#include "base/location.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/waitable_event.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/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
@@ -47,11 +38,8 @@ class SleepInsideInitThread : public Thread {
init_called_ = true;
}
bool InitCalled() { return init_called_; }
-
private:
bool init_called_;
-
- DISALLOW_COPY_AND_ASSIGN(SleepInsideInitThread);
};
enum ThreadEvent {
@@ -88,8 +76,6 @@ class CaptureToEventList : public Thread {
private:
EventList* event_list_;
-
- DISALLOW_COPY_AND_ASSIGN(CaptureToEventList);
};
// Observer that writes a value into |event_list| when a message loop has been
@@ -110,8 +96,6 @@ class CapturingDestructionObserver
private:
EventList* event_list_;
-
- DISALLOW_COPY_AND_ASSIGN(CapturingDestructionObserver);
};
// Task that adds a destruction observer to the current message loop.
@@ -131,79 +115,59 @@ void ReturnThreadId(base::Thread* thread,
} // namespace
+TEST_F(ThreadTest, Restart) {
+ Thread a("Restart");
+ a.Stop();
+ EXPECT_FALSE(a.message_loop());
+ EXPECT_FALSE(a.IsRunning());
+ EXPECT_TRUE(a.Start());
+ EXPECT_TRUE(a.message_loop());
+ EXPECT_TRUE(a.IsRunning());
+ a.Stop();
+ EXPECT_FALSE(a.message_loop());
+ EXPECT_FALSE(a.IsRunning());
+ EXPECT_TRUE(a.Start());
+ EXPECT_TRUE(a.message_loop());
+ EXPECT_TRUE(a.IsRunning());
+ a.Stop();
+ EXPECT_FALSE(a.message_loop());
+ EXPECT_FALSE(a.IsRunning());
+ a.Stop();
+ EXPECT_FALSE(a.message_loop());
+ EXPECT_FALSE(a.IsRunning());
+}
+
TEST_F(ThreadTest, StartWithOptions_StackSize) {
Thread a("StartWithStackSize");
// Ensure that the thread can work with only 12 kb and still process a
- // message. At the same time, we should scale with the bitness of the system
- // where 12 kb is definitely not enough.
- // 12 kb = 3072 Slots on a 32-bit system, so we'll scale based off of that.
+ // message.
Thread::Options options;
-#if defined(ADDRESS_SANITIZER) || !defined(NDEBUG)
- // ASan bloats the stack variables and overflows the 3072 slot stack. Some
- // debug builds also grow the stack too much.
- options.stack_size = 2 * 3072 * sizeof(uintptr_t);
+#if defined(ADDRESS_SANITIZER)
+ // ASan bloats the stack variables and overflows the 12 kb stack.
+ options.stack_size = 24*1024;
#else
- options.stack_size = 3072 * sizeof(uintptr_t);
+ options.stack_size = 12*1024;
#endif
EXPECT_TRUE(a.StartWithOptions(options));
EXPECT_TRUE(a.message_loop());
EXPECT_TRUE(a.IsRunning());
- base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
- base::WaitableEvent::InitialState::NOT_SIGNALED);
- a.task_runner()->PostTask(FROM_HERE, base::Bind(&base::WaitableEvent::Signal,
- base::Unretained(&event)));
- event.Wait();
-}
-
-// Intentional test-only race for otherwise untestable code, won't fix.
-// https://crbug.com/634383
-#if !defined(THREAD_SANITIZER)
-TEST_F(ThreadTest, StartWithOptions_NonJoinable) {
- Thread* a = new Thread("StartNonJoinable");
- // Non-joinable threads have to be leaked for now (see
- // Thread::Options::joinable for details).
- ANNOTATE_LEAKING_OBJECT_PTR(a);
+ bool was_invoked = false;
+ a.task_runner()->PostTask(FROM_HERE, base::Bind(&ToggleValue, &was_invoked));
- Thread::Options options;
- options.joinable = false;
- EXPECT_TRUE(a->StartWithOptions(options));
- EXPECT_TRUE(a->message_loop());
- EXPECT_TRUE(a->IsRunning());
-
- // Without this call this test is racy. The above IsRunning() succeeds because
- // of an early-return condition while between Start() and StopSoon(), after
- // invoking StopSoon() below this early-return condition is no longer
- // satisfied and the real |is_running_| bit has to be checked. It could still
- // be false if the message loop hasn't started for real in practice. This is
- // only a requirement for this test because the non-joinable property forces
- // it to use StopSoon() and not wait for a complete Stop().
- EXPECT_TRUE(a->WaitUntilThreadStarted());
-
- // Make the thread block until |block_event| is signaled.
- base::WaitableEvent block_event(
- base::WaitableEvent::ResetPolicy::AUTOMATIC,
- base::WaitableEvent::InitialState::NOT_SIGNALED);
- a->task_runner()->PostTask(
- FROM_HERE,
- base::Bind(&base::WaitableEvent::Wait, base::Unretained(&block_event)));
-
- a->StopSoon();
- EXPECT_TRUE(a->IsRunning());
-
- // Unblock the task and give a bit of extra time to unwind QuitWhenIdle().
- block_event.Signal();
- base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20));
-
- // The thread should now have stopped on its own.
- EXPECT_FALSE(a->IsRunning());
+ // wait for the task to run (we could use a kernel event here
+ // instead to avoid busy waiting, but this is sufficient for
+ // testing purposes).
+ for (int i = 100; i >= 0 && !was_invoked; --i) {
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10));
+ }
+ EXPECT_TRUE(was_invoked);
}
-#endif
-TEST_F(ThreadTest, TwoTasksOnJoinableThread) {
+TEST_F(ThreadTest, TwoTasks) {
bool was_invoked = false;
{
- Thread a("TwoTasksOnJoinableThread");
+ Thread a("TwoTasks");
EXPECT_TRUE(a.Start());
EXPECT_TRUE(a.message_loop());
@@ -220,164 +184,18 @@ TEST_F(ThreadTest, TwoTasksOnJoinableThread) {
EXPECT_TRUE(was_invoked);
}
-TEST_F(ThreadTest, DestroyWhileRunningIsSafe) {
- Thread a("DestroyWhileRunningIsSafe");
- EXPECT_TRUE(a.Start());
- EXPECT_TRUE(a.WaitUntilThreadStarted());
-}
-
-// TODO(gab): Enable this test when destroying a non-joinable Thread instance
-// is supported (proposal @ https://crbug.com/629139#c14).
-TEST_F(ThreadTest, DISABLED_DestroyWhileRunningNonJoinableIsSafe) {
- {
- Thread a("DestroyWhileRunningNonJoinableIsSafe");
- Thread::Options options;
- options.joinable = false;
- EXPECT_TRUE(a.StartWithOptions(options));
- EXPECT_TRUE(a.WaitUntilThreadStarted());
- }
-
- // Attempt to catch use-after-frees from the non-joinable thread in the
- // scope of this test if any.
- base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20));
-}
-
TEST_F(ThreadTest, StopSoon) {
Thread a("StopSoon");
EXPECT_TRUE(a.Start());
EXPECT_TRUE(a.message_loop());
EXPECT_TRUE(a.IsRunning());
a.StopSoon();
- a.Stop();
- EXPECT_FALSE(a.message_loop());
- EXPECT_FALSE(a.IsRunning());
-}
-
-TEST_F(ThreadTest, StopTwiceNop) {
- Thread a("StopTwiceNop");
- EXPECT_TRUE(a.Start());
- EXPECT_TRUE(a.message_loop());
- EXPECT_TRUE(a.IsRunning());
- a.StopSoon();
- // Calling StopSoon() a second time should be a nop.
a.StopSoon();
a.Stop();
- // Same with Stop().
- a.Stop();
- EXPECT_FALSE(a.message_loop());
- EXPECT_FALSE(a.IsRunning());
- // Calling them when not running should also nop.
- a.StopSoon();
- a.Stop();
-}
-
-// TODO(gab): Enable this test in conjunction with re-enabling the sequence
-// check in Thread::Stop() as part of http://crbug.com/629139.
-TEST_F(ThreadTest, DISABLED_StopOnNonOwningThreadIsDeath) {
- Thread a("StopOnNonOwningThreadDeath");
- EXPECT_TRUE(a.StartAndWaitForTesting());
-
- Thread b("NonOwningThread");
- b.Start();
- EXPECT_DCHECK_DEATH({
- // Stopping |a| on |b| isn't allowed.
- b.task_runner()->PostTask(FROM_HERE,
- base::Bind(&Thread::Stop, base::Unretained(&a)));
- // Block here so the DCHECK on |b| always happens in this scope.
- base::PlatformThread::Sleep(base::TimeDelta::Max());
- });
-}
-
-TEST_F(ThreadTest, TransferOwnershipAndStop) {
- std::unique_ptr<Thread> a =
- base::MakeUnique<Thread>("TransferOwnershipAndStop");
- EXPECT_TRUE(a->StartAndWaitForTesting());
- EXPECT_TRUE(a->IsRunning());
-
- Thread b("TakingOwnershipThread");
- b.Start();
-
- base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
- base::WaitableEvent::InitialState::NOT_SIGNALED);
-
- // a->DetachFromSequence() should allow |b| to use |a|'s Thread API.
- a->DetachFromSequence();
- b.task_runner()->PostTask(
- FROM_HERE, base::Bind(
- [](std::unique_ptr<Thread> thread_to_stop,
- base::WaitableEvent* event_to_signal) -> void {
- thread_to_stop->Stop();
- event_to_signal->Signal();
- },
- base::Passed(&a), base::Unretained(&event)));
-
- event.Wait();
-}
-
-TEST_F(ThreadTest, StartTwice) {
- Thread a("StartTwice");
-
- EXPECT_FALSE(a.message_loop());
- EXPECT_FALSE(a.IsRunning());
-
- EXPECT_TRUE(a.Start());
- EXPECT_TRUE(a.message_loop());
- EXPECT_TRUE(a.IsRunning());
-
- a.Stop();
- EXPECT_FALSE(a.message_loop());
- EXPECT_FALSE(a.IsRunning());
-
- EXPECT_TRUE(a.Start());
- EXPECT_TRUE(a.message_loop());
- EXPECT_TRUE(a.IsRunning());
-
- a.Stop();
EXPECT_FALSE(a.message_loop());
EXPECT_FALSE(a.IsRunning());
}
-// Intentional test-only race for otherwise untestable code, won't fix.
-// https://crbug.com/634383
-#if !defined(THREAD_SANITIZER)
-TEST_F(ThreadTest, StartTwiceNonJoinableNotAllowed) {
- LOG(ERROR) << __FUNCTION__;
- Thread* a = new Thread("StartTwiceNonJoinable");
- // Non-joinable threads have to be leaked for now (see
- // Thread::Options::joinable for details).
- ANNOTATE_LEAKING_OBJECT_PTR(a);
-
- Thread::Options options;
- options.joinable = false;
- EXPECT_TRUE(a->StartWithOptions(options));
- EXPECT_TRUE(a->message_loop());
- EXPECT_TRUE(a->IsRunning());
-
- // Signaled when last task on |a| is processed.
- base::WaitableEvent last_task_event(
- base::WaitableEvent::ResetPolicy::AUTOMATIC,
- base::WaitableEvent::InitialState::NOT_SIGNALED);
- a->task_runner()->PostTask(FROM_HERE,
- base::Bind(&base::WaitableEvent::Signal,
- base::Unretained(&last_task_event)));
-
- // StopSoon() is non-blocking, Yield() to |a|, wait for last task to be
- // processed and a little more for QuitWhenIdle() to unwind before considering
- // the thread "stopped".
- a->StopSoon();
- base::PlatformThread::YieldCurrentThread();
- last_task_event.Wait();
- base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20));
-
- // This test assumes that the above was sufficient to let the thread fully
- // stop.
- ASSERT_FALSE(a->IsRunning());
-
- // Restarting it should not be allowed.
- EXPECT_DCHECK_DEATH(a->Start());
-}
-#endif
-
TEST_F(ThreadTest, ThreadName) {
Thread a("ThreadName");
EXPECT_TRUE(a.Start());
@@ -479,91 +297,3 @@ TEST_F(ThreadTest, MultipleWaitUntilThreadStarted) {
EXPECT_TRUE(a.WaitUntilThreadStarted());
EXPECT_TRUE(a.WaitUntilThreadStarted());
}
-
-TEST_F(ThreadTest, FlushForTesting) {
- Thread a("FlushForTesting");
-
- // Flushing a non-running thread should be a no-op.
- a.FlushForTesting();
-
- ASSERT_TRUE(a.Start());
-
- // Flushing a thread with no tasks shouldn't block.
- a.FlushForTesting();
-
- constexpr base::TimeDelta kSleepPerTestTask =
- base::TimeDelta::FromMilliseconds(50);
- constexpr size_t kNumSleepTasks = 5;
-
- const base::TimeTicks ticks_before_post = base::TimeTicks::Now();
-
- for (size_t i = 0; i < kNumSleepTasks; ++i) {
- a.task_runner()->PostTask(
- FROM_HERE, base::Bind(&base::PlatformThread::Sleep, kSleepPerTestTask));
- }
-
- // All tasks should have executed, as reflected by the elapsed time.
- a.FlushForTesting();
- EXPECT_GE(base::TimeTicks::Now() - ticks_before_post,
- kNumSleepTasks * kSleepPerTestTask);
-
- a.Stop();
-
- // Flushing a stopped thread should be a no-op.
- a.FlushForTesting();
-}
-
-namespace {
-
-// A Thread which uses a MessageLoop on the stack. It won't start a real
-// underlying thread (instead its messages can be processed by a RunLoop on the
-// stack).
-class ExternalMessageLoopThread : public Thread {
- public:
- ExternalMessageLoopThread() : Thread("ExternalMessageLoopThread") {}
-
- ~ExternalMessageLoopThread() override { Stop(); }
-
- void InstallMessageLoop() { SetMessageLoop(&external_message_loop_); }
-
- void VerifyUsingExternalMessageLoop(
- bool expected_using_external_message_loop) {
- EXPECT_EQ(expected_using_external_message_loop,
- using_external_message_loop());
- }
-
- private:
- base::MessageLoop external_message_loop_;
-
- DISALLOW_COPY_AND_ASSIGN(ExternalMessageLoopThread);
-};
-
-} // namespace
-
-TEST_F(ThreadTest, ExternalMessageLoop) {
- ExternalMessageLoopThread a;
- EXPECT_FALSE(a.message_loop());
- EXPECT_FALSE(a.IsRunning());
- a.VerifyUsingExternalMessageLoop(false);
-
- a.InstallMessageLoop();
- EXPECT_TRUE(a.message_loop());
- EXPECT_TRUE(a.IsRunning());
- a.VerifyUsingExternalMessageLoop(true);
-
- bool ran = false;
- a.task_runner()->PostTask(
- FROM_HERE, base::Bind([](bool* toggled) { *toggled = true; }, &ran));
- base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(ran);
-
- a.Stop();
- EXPECT_FALSE(a.message_loop());
- EXPECT_FALSE(a.IsRunning());
- a.VerifyUsingExternalMessageLoop(true);
-
- // Confirm that running any remaining tasks posted from Stop() goes smoothly
- // (e.g. https://codereview.chromium.org/2135413003/#ps300001 crashed if
- // StopSoon() posted Thread::ThreadQuitHelper() while |run_loop_| was null).
- base::RunLoop().RunUntilIdle();
-}
diff --git a/base/threading/worker_pool.cc b/base/threading/worker_pool.cc
index d47037d79a..0b7bf8eca1 100644
--- a/base/threading/worker_pool.cc
+++ b/base/threading/worker_pool.cc
@@ -4,11 +4,9 @@
#include "base/threading/worker_pool.h"
-#include <utility>
-
#include "base/bind.h"
#include "base/compiler_specific.h"
-#include "base/debug/leak_annotations.h"
+#include "base/lazy_instance.h"
#include "base/macros.h"
#include "base/task_runner.h"
#include "base/threading/post_task_and_reply_impl.h"
@@ -99,27 +97,27 @@ struct TaskRunnerHolder {
scoped_refptr<TaskRunner> taskrunners_[2];
};
+base::LazyInstance<TaskRunnerHolder>::Leaky
+ g_taskrunners = LAZY_INSTANCE_INITIALIZER;
+
} // namespace
bool WorkerPool::PostTaskAndReply(const tracked_objects::Location& from_here,
- Closure task,
- Closure reply,
+ const Closure& task,
+ const Closure& reply,
bool task_is_slow) {
// Do not report PostTaskAndReplyRelay leaks in tests. There's nothing we can
// do about them because WorkerPool doesn't have a flushing API.
// http://crbug.com/248513
// http://crbug.com/290897
- // Note: this annotation does not cover tasks posted through a TaskRunner.
- ANNOTATE_SCOPED_MEMORY_LEAK;
- return PostTaskAndReplyWorkerPool(task_is_slow)
- .PostTaskAndReply(from_here, std::move(task), std::move(reply));
+ return PostTaskAndReplyWorkerPool(task_is_slow).PostTaskAndReply(
+ from_here, task, reply);
}
// static
const scoped_refptr<TaskRunner>&
WorkerPool::GetTaskRunner(bool tasks_are_slow) {
- static auto* task_runner_holder = new TaskRunnerHolder();
- return task_runner_holder->taskrunners_[tasks_are_slow];
+ return g_taskrunners.Get().taskrunners_[tasks_are_slow];
}
} // namespace base
diff --git a/base/threading/worker_pool.h b/base/threading/worker_pool.h
index 865948e437..a52a41428b 100644
--- a/base/threading/worker_pool.h
+++ b/base/threading/worker_pool.h
@@ -6,9 +6,11 @@
#define BASE_THREADING_WORKER_POOL_H_
#include "base/base_export.h"
-#include "base/callback.h"
+#include "base/callback_forward.h"
#include "base/memory/ref_counted.h"
+class Task;
+
namespace tracked_objects {
class Location;
} // namespace tracked_objects
@@ -38,8 +40,8 @@ class BASE_EXPORT WorkerPool {
// for |task| is a worker thread and you can specify |task_is_slow| just
// like you can for PostTask above.
static bool PostTaskAndReply(const tracked_objects::Location& from_here,
- Closure task,
- Closure reply,
+ const Closure& task,
+ const Closure& reply,
bool task_is_slow);
// Return true if the current thread is one that this WorkerPool runs tasks
diff --git a/base/threading/worker_pool_posix.cc b/base/threading/worker_pool_posix.cc
index 0e19a1a0fe..6b4c42f601 100644
--- a/base/threading/worker_pool_posix.cc
+++ b/base/threading/worker_pool_posix.cc
@@ -30,21 +30,10 @@ base::LazyInstance<ThreadLocalBoolean>::Leaky
const int kIdleSecondsBeforeExit = 10 * 60;
-#if defined(OS_MACOSX)
-// On Mac OS X a background thread's default stack size is 512Kb. We need at
-// least 1MB for compilation tasks in V8, so increase this default.
-const int kStackSize = 1 * 1024 * 1024;
-#else
-const int kStackSize = 0;
-#endif
-
class WorkerPoolImpl {
public:
WorkerPoolImpl();
-
- // WorkerPoolImpl is only instantiated as a leaky LazyInstance, so the
- // destructor is never called.
- ~WorkerPoolImpl() = delete;
+ ~WorkerPoolImpl();
void PostTask(const tracked_objects::Location& from_here,
const base::Closure& task,
@@ -58,13 +47,17 @@ WorkerPoolImpl::WorkerPoolImpl()
: pool_(new base::PosixDynamicThreadPool("WorkerPool",
kIdleSecondsBeforeExit)) {}
+WorkerPoolImpl::~WorkerPoolImpl() {
+ pool_->Terminate();
+}
+
void WorkerPoolImpl::PostTask(const tracked_objects::Location& from_here,
const base::Closure& task,
bool /*task_is_slow*/) {
pool_->PostTask(from_here, task);
}
-base::LazyInstance<WorkerPoolImpl>::Leaky g_lazy_worker_pool =
+base::LazyInstance<WorkerPoolImpl> g_lazy_worker_pool =
LAZY_INSTANCE_INITIALIZER;
class WorkerThread : public PlatformThread::Delegate {
@@ -97,7 +90,7 @@ void WorkerThread::ThreadMain() {
tracked_objects::TaskStopwatch stopwatch;
stopwatch.Start();
- std::move(pending_task.task).Run();
+ pending_task.task.Run();
stopwatch.Stop();
tracked_objects::ThreadData::TallyRunOnWorkerThreadIfTracking(
@@ -128,13 +121,23 @@ PosixDynamicThreadPool::PosixDynamicThreadPool(const std::string& name_prefix,
: name_prefix_(name_prefix),
idle_seconds_before_exit_(idle_seconds_before_exit),
pending_tasks_available_cv_(&lock_),
- num_idle_threads_(0) {}
+ num_idle_threads_(0),
+ terminated_(false) {}
PosixDynamicThreadPool::~PosixDynamicThreadPool() {
while (!pending_tasks_.empty())
pending_tasks_.pop();
}
+void PosixDynamicThreadPool::Terminate() {
+ {
+ AutoLock locked(lock_);
+ DCHECK(!terminated_) << "Thread pool is already terminated.";
+ terminated_ = true;
+ }
+ pending_tasks_available_cv_.Broadcast();
+}
+
void PosixDynamicThreadPool::PostTask(
const tracked_objects::Location& from_here,
const base::Closure& task) {
@@ -144,6 +147,8 @@ void PosixDynamicThreadPool::PostTask(
void PosixDynamicThreadPool::AddTask(PendingTask* pending_task) {
AutoLock locked(lock_);
+ DCHECK(!terminated_)
+ << "This thread pool is already terminated. Do not post new tasks.";
pending_tasks_.push(std::move(*pending_task));
@@ -154,13 +159,16 @@ void PosixDynamicThreadPool::AddTask(PendingTask* pending_task) {
// The new PlatformThread will take ownership of the WorkerThread object,
// which will delete itself on exit.
WorkerThread* worker = new WorkerThread(name_prefix_, this);
- PlatformThread::CreateNonJoinable(kStackSize, worker);
+ PlatformThread::CreateNonJoinable(0, worker);
}
}
PendingTask PosixDynamicThreadPool::WaitForTask() {
AutoLock locked(lock_);
+ if (terminated_)
+ return PendingTask(FROM_HERE, base::Closure());
+
if (pending_tasks_.empty()) { // No work available, wait for work.
num_idle_threads_++;
if (num_idle_threads_cv_.get())
diff --git a/base/threading/worker_pool_posix.h b/base/threading/worker_pool_posix.h
index d65ae8f8cf..628e2b6420 100644
--- a/base/threading/worker_pool_posix.h
+++ b/base/threading/worker_pool_posix.h
@@ -38,6 +38,8 @@
#include "base/threading/platform_thread.h"
#include "base/tracked_objects.h"
+class Task;
+
namespace base {
class BASE_EXPORT PosixDynamicThreadPool
@@ -50,6 +52,10 @@ class BASE_EXPORT PosixDynamicThreadPool
PosixDynamicThreadPool(const std::string& name_prefix,
int idle_seconds_before_exit);
+ // Indicates that the thread pool is going away. Stops handing out tasks to
+ // worker threads. Wakes up all the idle threads to let them exit.
+ void Terminate();
+
// Adds |task| to the thread pool.
void PostTask(const tracked_objects::Location& from_here,
const Closure& task);
@@ -79,6 +85,7 @@ class BASE_EXPORT PosixDynamicThreadPool
ConditionVariable pending_tasks_available_cv_;
int num_idle_threads_;
TaskQueue pending_tasks_;
+ bool terminated_;
// Only used for tests to ensure correct thread ordering. It will always be
// NULL in non-test code.
std::unique_ptr<ConditionVariable> num_idle_threads_cv_;
diff --git a/base/threading/worker_pool_posix_unittest.cc b/base/threading/worker_pool_posix_unittest.cc
index b4e8b58520..6cefeed34e 100644
--- a/base/threading/worker_pool_posix_unittest.cc
+++ b/base/threading/worker_pool_posix_unittest.cc
@@ -103,6 +103,12 @@ class PosixDynamicThreadPoolTest : public testing::Test {
peer_.set_num_idle_threads_cv(new ConditionVariable(peer_.lock()));
}
+ void TearDown() override {
+ // Wake up the idle threads so they can terminate.
+ if (pool_.get())
+ pool_->Terminate();
+ }
+
void WaitForTasksToStart(int num_tasks) {
base::AutoLock num_waiting_to_start_locked(num_waiting_to_start_lock_);
while (num_waiting_to_start_ < num_tasks) {
diff --git a/base/time/time.cc b/base/time/time.cc
index d1c6a4783c..3670f55758 100644
--- a/base/time/time.cc
+++ b/base/time/time.cc
@@ -21,6 +21,11 @@ namespace base {
// TimeDelta ------------------------------------------------------------------
+// static
+TimeDelta TimeDelta::Max() {
+ return TimeDelta(std::numeric_limits<int64_t>::max());
+}
+
int TimeDelta::InDays() const {
if (is_max()) {
// Preserve max to prevent overflow.
@@ -99,29 +104,33 @@ namespace time_internal {
int64_t SaturatedAdd(TimeDelta delta, int64_t value) {
CheckedNumeric<int64_t> rv(delta.delta_);
rv += value;
- if (rv.IsValid())
- return rv.ValueOrDie();
- // Positive RHS overflows. Negative RHS underflows.
- if (value < 0)
- return -std::numeric_limits<int64_t>::max();
- return std::numeric_limits<int64_t>::max();
+ return FromCheckedNumeric(rv);
}
int64_t SaturatedSub(TimeDelta delta, int64_t value) {
CheckedNumeric<int64_t> rv(delta.delta_);
rv -= value;
- if (rv.IsValid())
- return rv.ValueOrDie();
- // Negative RHS overflows. Positive RHS underflows.
- if (value < 0)
- return std::numeric_limits<int64_t>::max();
- return -std::numeric_limits<int64_t>::max();
+ return FromCheckedNumeric(rv);
+}
+
+int64_t FromCheckedNumeric(const CheckedNumeric<int64_t> value) {
+ if (value.IsValid())
+ return value.ValueUnsafe();
+
+ // We could return max/min but we don't really expose what the maximum delta
+ // is. Instead, return max/(-max), which is something that clients can reason
+ // about.
+ // TODO(rvargas) crbug.com/332611: don't use internal values.
+ int64_t limit = std::numeric_limits<int64_t>::max();
+ if (value.validity() == internal::RANGE_UNDERFLOW)
+ limit = -limit;
+ return value.ValueOrDefault(limit);
}
} // namespace time_internal
std::ostream& operator<<(std::ostream& os, TimeDelta time_delta) {
- return os << time_delta.InSecondsF() << " s";
+ return os << time_delta.InSecondsF() << "s";
}
// Time -----------------------------------------------------------------------
@@ -198,11 +207,6 @@ double Time::ToJsTime() const {
kMicrosecondsPerMillisecond);
}
-Time Time::FromJavaTime(int64_t ms_since_epoch) {
- return base::Time::UnixEpoch() +
- base::TimeDelta::FromMilliseconds(ms_since_epoch);
-}
-
int64_t Time::ToJavaTime() const {
if (is_null()) {
// Preserve 0 so the invalid result doesn't depend on the platform.
@@ -230,12 +234,7 @@ Time Time::LocalMidnight() const {
exploded.minute = 0;
exploded.second = 0;
exploded.millisecond = 0;
- Time out_time;
- if (FromLocalExploded(exploded, &out_time))
- return out_time;
- // This function must not fail.
- NOTREACHED();
- return Time();
+ return FromLocalExploded(exploded);
}
// static
diff --git a/base/time/time.h b/base/time/time.h
index ff8bdde3dc..efece969b0 100644
--- a/base/time/time.h
+++ b/base/time/time.h
@@ -21,11 +21,9 @@
// ThreadTicks will "stand still" whenever the thread has been de-scheduled by
// the operating system.
//
-// All time classes are copyable, assignable, and occupy 64-bits per instance.
-// As a result, prefer passing them by value:
-// void MyFunction(TimeDelta arg);
-// If circumstances require, you may also pass by const reference:
-// void MyFunction(const TimeDelta& arg); // Not preferred.
+// All time classes are copyable, assignable, and occupy 64-bits per
+// instance. Thus, they can be efficiently passed by-value (as opposed to
+// by-reference).
//
// Definitions of operator<< are provided to make these types work with
// DCHECK_EQ() and other log macros. For human-readable formatting, see
@@ -59,7 +57,6 @@
#include "base/base_export.h"
#include "base/compiler_specific.h"
-#include "base/logging.h"
#include "base/numerics/safe_math.h"
#include "build/build_config.h"
@@ -96,6 +93,10 @@ namespace time_internal {
BASE_EXPORT int64_t SaturatedAdd(TimeDelta delta, int64_t value);
BASE_EXPORT int64_t SaturatedSub(TimeDelta delta, int64_t value);
+// Clamp |value| on overflow and underflow conditions. The int64_t argument and
+// return value are in terms of a microsecond timebase.
+BASE_EXPORT int64_t FromCheckedNumeric(const CheckedNumeric<int64_t> value);
+
} // namespace time_internal
// TimeDelta ------------------------------------------------------------------
@@ -114,9 +115,6 @@ class BASE_EXPORT TimeDelta {
static constexpr TimeDelta FromSecondsD(double secs);
static constexpr TimeDelta FromMillisecondsD(double ms);
static constexpr TimeDelta FromMicroseconds(int64_t us);
-#if defined(OS_POSIX)
- static TimeDelta FromTimeSpec(const timespec& ts);
-#endif
#if defined(OS_WIN)
static TimeDelta FromQPCValue(LONGLONG qpc_value);
#endif
@@ -130,7 +128,7 @@ class BASE_EXPORT TimeDelta {
// Returns the maximum time delta, which should be greater than any reasonable
// time delta we might compare it to. Adding or subtracting the maximum time
// delta to a time or another time delta has an undefined result.
- static constexpr TimeDelta Max();
+ static TimeDelta Max();
// Returns the internal numeric value of the TimeDelta object. Please don't
// use this and do arithmetic on it, as it is more error prone than using the
@@ -202,24 +200,13 @@ class BASE_EXPORT TimeDelta {
TimeDelta operator*(T a) const {
CheckedNumeric<int64_t> rv(delta_);
rv *= a;
- if (rv.IsValid())
- return TimeDelta(rv.ValueOrDie());
- // Matched sign overflows. Mismatched sign underflows.
- if ((delta_ < 0) ^ (a < 0))
- return TimeDelta(-std::numeric_limits<int64_t>::max());
- return TimeDelta(std::numeric_limits<int64_t>::max());
+ return TimeDelta(time_internal::FromCheckedNumeric(rv));
}
template<typename T>
TimeDelta operator/(T a) const {
CheckedNumeric<int64_t> rv(delta_);
rv /= a;
- if (rv.IsValid())
- return TimeDelta(rv.ValueOrDie());
- // Matched sign overflows. Mismatched sign underflows.
- // Special case to catch divide by zero.
- if ((delta_ < 0) ^ (a <= 0))
- return TimeDelta(-std::numeric_limits<int64_t>::max());
- return TimeDelta(std::numeric_limits<int64_t>::max());
+ return TimeDelta(time_internal::FromCheckedNumeric(rv));
}
template<typename T>
TimeDelta& operator*=(T a) {
@@ -255,11 +242,6 @@ class BASE_EXPORT TimeDelta {
return delta_ >= other.delta_;
}
-#if defined(OS_WIN)
- // This works around crbug.com/635974
- constexpr TimeDelta(const TimeDelta& other) : delta_(other.delta_) {}
-#endif
-
private:
friend int64_t time_internal::SaturatedAdd(TimeDelta delta, int64_t value);
friend int64_t time_internal::SaturatedSub(TimeDelta delta, int64_t value);
@@ -470,6 +452,8 @@ class BASE_EXPORT Time : public time_internal::TimeBase<Time> {
static Time NowFromSystemTime();
// Converts to/from time_t in UTC and a Time class.
+ // TODO(brettw) this should be removed once everybody starts using the |Time|
+ // class.
static Time FromTimeT(time_t tt);
time_t ToTimeT() const;
@@ -495,9 +479,8 @@ class BASE_EXPORT Time : public time_internal::TimeBase<Time> {
static Time FromJsTime(double ms_since_epoch);
double ToJsTime() const;
- // Converts to/from Java convention for times, a number of
+ // Converts to Java convention for times, a number of
// milliseconds since the epoch.
- static Time FromJavaTime(int64_t ms_since_epoch);
int64_t ToJavaTime() const;
#if defined(OS_POSIX)
@@ -538,8 +521,23 @@ class BASE_EXPORT Time : public time_internal::TimeBase<Time> {
#endif
// Converts an exploded structure representing either the local time or UTC
+ // into a Time class.
+ // TODO(maksims): Get rid of these in favor of the methods below when
+ // all the callers stop using these ones.
+ static Time FromUTCExploded(const Exploded& exploded) {
+ base::Time time;
+ ignore_result(FromUTCExploded(exploded, &time));
+ return time;
+ }
+ static Time FromLocalExploded(const Exploded& exploded) {
+ base::Time time;
+ ignore_result(FromLocalExploded(exploded, &time));
+ return time;
+ }
+
+ // Converts an exploded structure representing either the local time or UTC
// into a Time class. Returns false on a failure when, for example, a day of
- // month is set to 31 on a 28-30 day month. Returns Time(0) on overflow.
+ // month is set to 31 on a 28-30 day month.
static bool FromUTCExploded(const Exploded& exploded,
Time* time) WARN_UNUSED_RESULT {
return FromExploded(false, exploded, time);
@@ -557,12 +555,10 @@ class BASE_EXPORT Time : public time_internal::TimeBase<Time> {
// specified in RFC822) is treated as if the timezone is not specified.
// TODO(iyengar) Move the FromString/FromTimeT/ToTimeT/FromFileTime to
// a new time converter class.
- static bool FromString(const char* time_string,
- Time* parsed_time) WARN_UNUSED_RESULT {
+ static bool FromString(const char* time_string, Time* parsed_time) {
return FromStringInternal(time_string, true, parsed_time);
}
- static bool FromUTCString(const char* time_string,
- Time* parsed_time) WARN_UNUSED_RESULT {
+ static bool FromUTCString(const char* time_string, Time* parsed_time) {
return FromStringInternal(time_string, false, parsed_time);
}
@@ -605,11 +601,10 @@ class BASE_EXPORT Time : public time_internal::TimeBase<Time> {
// timezone is not specified.
static bool FromStringInternal(const char* time_string,
bool is_local,
- Time* parsed_time) WARN_UNUSED_RESULT;
+ Time* parsed_time);
// Comparison does not consider |day_of_week| when doing the operation.
- static bool ExplodedMostlyEquals(const Exploded& lhs,
- const Exploded& rhs) WARN_UNUSED_RESULT;
+ static bool ExplodedMostlyEquals(const Exploded& lhs, const Exploded& rhs);
};
// static
@@ -659,11 +654,6 @@ constexpr TimeDelta TimeDelta::FromMicroseconds(int64_t us) {
}
// static
-constexpr TimeDelta TimeDelta::Max() {
- return TimeDelta(std::numeric_limits<int64_t>::max());
-}
-
-// static
constexpr TimeDelta TimeDelta::FromDouble(double value) {
// TODO(crbug.com/612601): Use saturated_cast<int64_t>(value) once we sort out
// the Min() behavior.
@@ -721,14 +711,7 @@ class BASE_EXPORT TimeTicks : public time_internal::TimeBase<TimeTicks> {
// Now() will return high resolution values. Note that, on systems where the
// high resolution clock works but is deemed inefficient, the low resolution
// clock will be used instead.
- static bool IsHighResolution() WARN_UNUSED_RESULT;
-
- // Returns true if TimeTicks is consistent across processes, meaning that
- // timestamps taken on different processes can be safely compared with one
- // another. (Note that, even on platforms where this returns true, time values
- // from different threads that are within one tick of each other must be
- // considered to have an ambiguous ordering.)
- static bool IsConsistentAcrossProcesses() WARN_UNUSED_RESULT;
+ static bool IsHighResolution();
#if defined(OS_WIN)
// Translates an absolute QPC timestamp into a TimeTicks value. The returned
@@ -737,10 +720,6 @@ class BASE_EXPORT TimeTicks : public time_internal::TimeBase<TimeTicks> {
static TimeTicks FromQPCValue(LONGLONG qpc_value);
#endif
-#if defined(OS_MACOSX) && !defined(OS_IOS)
- static TimeTicks FromMachAbsoluteTime(uint64_t mach_absolute_time);
-#endif // defined(OS_MACOSX) && !defined(OS_IOS)
-
// Get an estimate of the TimeTick value at the time of the UnixEpoch. Because
// Time and TimeTicks respond differently to user-set time and NTP
// adjustments, this number is only an estimate. Nevertheless, this can be
@@ -789,7 +768,7 @@ class BASE_EXPORT ThreadTicks : public time_internal::TimeBase<ThreadTicks> {
}
// Returns true if ThreadTicks::Now() is supported on this system.
- static bool IsSupported() WARN_UNUSED_RESULT {
+ static bool IsSupported() {
#if (defined(_POSIX_THREAD_CPUTIME) && (_POSIX_THREAD_CPUTIME >= 0)) || \
(defined(OS_MACOSX) && !defined(OS_IOS)) || defined(OS_ANDROID)
return true;
@@ -840,7 +819,7 @@ class BASE_EXPORT ThreadTicks : public time_internal::TimeBase<ThreadTicks> {
// allow testing.
static double TSCTicksPerSecond();
- static bool IsSupportedWin() WARN_UNUSED_RESULT;
+ static bool IsSupportedWin();
static void WaitUntilInitializedWin();
#endif
};
diff --git a/base/time/time_mac.cc b/base/time/time_mac.cc
index c75423df9c..373ec3a3bc 100644
--- a/base/time/time_mac.cc
+++ b/base/time/time_mac.cc
@@ -25,8 +25,22 @@
namespace {
-#if defined(OS_MACOSX) && !defined(OS_IOS)
-int64_t MachAbsoluteTimeToTicks(uint64_t mach_absolute_time) {
+int64_t ComputeCurrentTicks() {
+#if defined(OS_IOS)
+ // On iOS mach_absolute_time stops while the device is sleeping. Instead use
+ // now - KERN_BOOTTIME to get a time difference that is not impacted by clock
+ // changes. KERN_BOOTTIME will be updated by the system whenever the system
+ // clock change.
+ struct timeval boottime;
+ int mib[2] = {CTL_KERN, KERN_BOOTTIME};
+ size_t size = sizeof(boottime);
+ int kr = sysctl(mib, arraysize(mib), &boottime, &size, nullptr, 0);
+ DCHECK_EQ(KERN_SUCCESS, kr);
+ base::TimeDelta time_difference = base::Time::Now() -
+ (base::Time::FromTimeT(boottime.tv_sec) +
+ base::TimeDelta::FromMicroseconds(boottime.tv_usec));
+ return time_difference.InMicroseconds();
+#else
static mach_timebase_info_data_t timebase_info;
if (timebase_info.denom == 0) {
// Zero-initialization of statics guarantees that denom will be 0 before
@@ -38,10 +52,14 @@ int64_t MachAbsoluteTimeToTicks(uint64_t mach_absolute_time) {
MACH_DCHECK(kr == KERN_SUCCESS, kr) << "mach_timebase_info";
}
+ // mach_absolute_time is it when it comes to ticks on the Mac. Other calls
+ // with less precision (such as TickCount) just call through to
+ // mach_absolute_time.
+
// timebase_info converts absolute time tick units into nanoseconds. Convert
// to microseconds up front to stave off overflows.
- base::CheckedNumeric<uint64_t> result(mach_absolute_time /
- base::Time::kNanosecondsPerMicrosecond);
+ base::CheckedNumeric<uint64_t> result(
+ mach_absolute_time() / base::Time::kNanosecondsPerMicrosecond);
result *= timebase_info.numer;
result /= timebase_info.denom;
@@ -49,29 +67,6 @@ int64_t MachAbsoluteTimeToTicks(uint64_t mach_absolute_time) {
// With numer and denom = 1 (the expected case), the 64-bit absolute time
// reported in nanoseconds is enough to last nearly 585 years.
return base::checked_cast<int64_t>(result.ValueOrDie());
-}
-#endif // defined(OS_MACOSX) && !defined(OS_IOS)
-
-int64_t ComputeCurrentTicks() {
-#if defined(OS_IOS)
- // On iOS mach_absolute_time stops while the device is sleeping. Instead use
- // now - KERN_BOOTTIME to get a time difference that is not impacted by clock
- // changes. KERN_BOOTTIME will be updated by the system whenever the system
- // clock change.
- struct timeval boottime;
- int mib[2] = {CTL_KERN, KERN_BOOTTIME};
- size_t size = sizeof(boottime);
- int kr = sysctl(mib, arraysize(mib), &boottime, &size, nullptr, 0);
- DCHECK_EQ(KERN_SUCCESS, kr);
- base::TimeDelta time_difference =
- base::Time::Now() - (base::Time::FromTimeT(boottime.tv_sec) +
- base::TimeDelta::FromMicroseconds(boottime.tv_usec));
- return time_difference.InMicroseconds();
-#else
- // mach_absolute_time is it when it comes to ticks on the Mac. Other calls
- // with less precision (such as TickCount) just call through to
- // mach_absolute_time.
- return MachAbsoluteTimeToTicks(mach_absolute_time());
#endif // defined(OS_IOS)
}
@@ -190,18 +185,9 @@ bool Time::FromExploded(bool is_local, const Exploded& exploded, Time* time) {
exploded.millisecond);
CFAbsoluteTime seconds = absolute_time + kCFAbsoluteTimeIntervalSince1970;
- // CFAbsolutTime is typedef of double. Convert seconds to
- // microseconds and then cast to int64. If
- // it cannot be suited to int64, then fail to avoid overflows.
- double microseconds =
- (seconds * kMicrosecondsPerSecond) + kWindowsEpochDeltaMicroseconds;
- if (microseconds > std::numeric_limits<int64_t>::max() ||
- microseconds < std::numeric_limits<int64_t>::min()) {
- *time = Time(0);
- return false;
- }
-
- base::Time converted_time = Time(static_cast<int64_t>(microseconds));
+ base::Time converted_time =
+ Time(static_cast<int64_t>(seconds * kMicrosecondsPerSecond) +
+ kWindowsEpochDeltaMicroseconds);
// If |exploded.day_of_month| is set to 31
// on a 28-30 day month, it will return the first day of the next month.
@@ -273,18 +259,6 @@ bool TimeTicks::IsHighResolution() {
}
// static
-bool TimeTicks::IsConsistentAcrossProcesses() {
- return true;
-}
-
-#if defined(OS_MACOSX) && !defined(OS_IOS)
-// static
-TimeTicks TimeTicks::FromMachAbsoluteTime(uint64_t mach_absolute_time) {
- return TimeTicks(MachAbsoluteTimeToTicks(mach_absolute_time));
-}
-#endif // defined(OS_MACOSX) && !defined(OS_IOS)
-
-// static
TimeTicks::Clock TimeTicks::GetClock() {
#if defined(OS_IOS)
return Clock::IOS_CF_ABSOLUTE_TIME_MINUS_KERN_BOOTTIME;
diff --git a/base/time/time_posix.cc b/base/time/time_posix.cc
index 2cceb0c610..495e249f00 100644
--- a/base/time/time_posix.cc
+++ b/base/time/time_posix.cc
@@ -16,7 +16,6 @@
#include <ostream>
#include "base/logging.h"
-#include "base/numerics/safe_math.h"
#include "build/build_config.h"
#if defined(OS_ANDROID)
@@ -26,6 +25,7 @@
#endif
#if !defined(OS_MACOSX)
+#include "base/lazy_instance.h"
#include "base/synchronization/lock.h"
#endif
@@ -34,10 +34,8 @@ namespace {
#if !defined(OS_MACOSX)
// This prevents a crash on traversing the environment global and looking up
// the 'TZ' variable in libc. See: crbug.com/390567.
-base::Lock* GetSysTimeToTimeStructLock() {
- static auto* lock = new base::Lock();
- return lock;
-}
+base::LazyInstance<base::Lock>::Leaky
+ g_sys_time_to_time_struct_lock = LAZY_INSTANCE_INITIALIZER;
// Define a system-specific SysTime that wraps either to a time_t or
// a time64_t depending on the host system, and associated convertion.
@@ -46,7 +44,7 @@ base::Lock* GetSysTimeToTimeStructLock() {
typedef time64_t SysTime;
SysTime SysTimeFromTimeStruct(struct tm* timestruct, bool is_local) {
- base::AutoLock locked(*GetSysTimeToTimeStructLock());
+ base::AutoLock locked(g_sys_time_to_time_struct_lock.Get());
if (is_local)
return mktime64(timestruct);
else
@@ -54,7 +52,7 @@ SysTime SysTimeFromTimeStruct(struct tm* timestruct, bool is_local) {
}
void SysTimeToTimeStruct(SysTime t, struct tm* timestruct, bool is_local) {
- base::AutoLock locked(*GetSysTimeToTimeStructLock());
+ base::AutoLock locked(g_sys_time_to_time_struct_lock.Get());
if (is_local)
localtime64_r(&t, timestruct);
else
@@ -65,7 +63,7 @@ void SysTimeToTimeStruct(SysTime t, struct tm* timestruct, bool is_local) {
typedef time_t SysTime;
SysTime SysTimeFromTimeStruct(struct tm* timestruct, bool is_local) {
- base::AutoLock locked(*GetSysTimeToTimeStructLock());
+ base::AutoLock locked(g_sys_time_to_time_struct_lock.Get());
if (is_local)
return mktime(timestruct);
else
@@ -73,7 +71,7 @@ SysTime SysTimeFromTimeStruct(struct tm* timestruct, bool is_local) {
}
void SysTimeToTimeStruct(SysTime t, struct tm* timestruct, bool is_local) {
- base::AutoLock locked(*GetSysTimeToTimeStructLock());
+ base::AutoLock locked(g_sys_time_to_time_struct_lock.Get());
if (is_local)
localtime_r(&t, timestruct);
else
@@ -82,19 +80,10 @@ void SysTimeToTimeStruct(SysTime t, struct tm* timestruct, bool is_local) {
#endif // OS_ANDROID
int64_t ConvertTimespecToMicros(const struct timespec& ts) {
- // On 32-bit systems, the calculation cannot overflow int64_t.
- // 2**32 * 1000000 + 2**64 / 1000 < 2**63
- if (sizeof(ts.tv_sec) <= 4 && sizeof(ts.tv_nsec) <= 8) {
- int64_t result = ts.tv_sec;
- result *= base::Time::kMicrosecondsPerSecond;
- result += (ts.tv_nsec / base::Time::kNanosecondsPerMicrosecond);
- return result;
- } else {
- base::CheckedNumeric<int64_t> result(ts.tv_sec);
- result *= base::Time::kMicrosecondsPerSecond;
- result += (ts.tv_nsec / base::Time::kNanosecondsPerMicrosecond);
- return result.ValueOrDie();
- }
+ base::CheckedNumeric<int64_t> result(ts.tv_sec);
+ result *= base::Time::kMicrosecondsPerSecond;
+ result += (ts.tv_nsec / base::Time::kNanosecondsPerMicrosecond);
+ return result.ValueOrDie();
}
// Helper function to get results from clock_gettime() and convert to a
@@ -121,12 +110,6 @@ int64_t ClockNow(clockid_t clk_id) {
namespace base {
-// static
-TimeDelta TimeDelta::FromTimeSpec(const timespec& ts) {
- return TimeDelta(ts.tv_sec * Time::kMicrosecondsPerSecond +
- ts.tv_nsec / Time::kNanosecondsPerMicrosecond);
-}
-
struct timespec TimeDelta::ToTimeSpec() const {
int64_t microseconds = InMicroseconds();
time_t seconds = 0;
@@ -229,30 +212,22 @@ void Time::Explode(bool is_local, Exploded* exploded) const {
// static
bool Time::FromExploded(bool is_local, const Exploded& exploded, Time* time) {
- CheckedNumeric<int> month = exploded.month;
- month--;
- CheckedNumeric<int> year = exploded.year;
- year -= 1900;
- if (!month.IsValid() || !year.IsValid()) {
- *time = Time(0);
- return false;
- }
-
struct tm timestruct;
- timestruct.tm_sec = exploded.second;
- timestruct.tm_min = exploded.minute;
- timestruct.tm_hour = exploded.hour;
- timestruct.tm_mday = exploded.day_of_month;
- timestruct.tm_mon = month.ValueOrDie();
- timestruct.tm_year = year.ValueOrDie();
- timestruct.tm_wday = exploded.day_of_week; // mktime/timegm ignore this
- timestruct.tm_yday = 0; // mktime/timegm ignore this
- timestruct.tm_isdst = -1; // attempt to figure it out
+ timestruct.tm_sec = exploded.second;
+ timestruct.tm_min = exploded.minute;
+ timestruct.tm_hour = exploded.hour;
+ timestruct.tm_mday = exploded.day_of_month;
+ timestruct.tm_mon = exploded.month - 1;
+ timestruct.tm_year = exploded.year - 1900;
+ timestruct.tm_wday = exploded.day_of_week; // mktime/timegm ignore this
+ timestruct.tm_yday = 0; // mktime/timegm ignore this
+ timestruct.tm_isdst = -1; // attempt to figure it out
#if !defined(OS_NACL) && !defined(OS_SOLARIS)
- timestruct.tm_gmtoff = 0; // not a POSIX field, so mktime/timegm ignore
- timestruct.tm_zone = NULL; // not a POSIX field, so mktime/timegm ignore
+ timestruct.tm_gmtoff = 0; // not a POSIX field, so mktime/timegm ignore
+ timestruct.tm_zone = NULL; // not a POSIX field, so mktime/timegm ignore
#endif
+ int64_t milliseconds;
SysTime seconds;
// Certain exploded dates do not really exist due to daylight saving times,
@@ -290,7 +265,6 @@ bool Time::FromExploded(bool is_local, const Exploded& exploded, Time* time) {
// return is the best that can be done here. It's not ideal, but it's better
// than failing here or ignoring the overflow case and treating each time
// overflow as one second prior to the epoch.
- int64_t milliseconds = 0;
if (seconds == -1 &&
(exploded.year < 1969 || exploded.year > 1970)) {
// If exploded.year is 1969 or 1970, take -1 as correct, with the
@@ -323,25 +297,13 @@ bool Time::FromExploded(bool is_local, const Exploded& exploded, Time* time) {
milliseconds += (kMillisecondsPerSecond - 1);
}
} else {
- base::CheckedNumeric<int64_t> checked_millis = seconds;
- checked_millis *= kMillisecondsPerSecond;
- checked_millis += exploded.millisecond;
- if (!checked_millis.IsValid()) {
- *time = base::Time(0);
- return false;
- }
- milliseconds = checked_millis.ValueOrDie();
+ milliseconds = seconds * kMillisecondsPerSecond + exploded.millisecond;
}
- // Adjust from Unix (1970) to Windows (1601) epoch avoiding overflows.
- base::CheckedNumeric<int64_t> checked_microseconds_win_epoch = milliseconds;
- checked_microseconds_win_epoch *= kMicrosecondsPerMillisecond;
- checked_microseconds_win_epoch += kWindowsEpochDeltaMicroseconds;
- if (!checked_microseconds_win_epoch.IsValid()) {
- *time = base::Time(0);
- return false;
- }
- base::Time converted_time(checked_microseconds_win_epoch.ValueOrDie());
+ // Adjust from Unix (1970) to Windows (1601) epoch.
+ base::Time converted_time =
+ Time((milliseconds * kMicrosecondsPerMillisecond) +
+ kWindowsEpochDeltaMicroseconds);
// If |exploded.day_of_month| is set to 31 on a 28-30 day month, it will
// return the first day of the next month. Thus round-trip the time and
@@ -378,11 +340,6 @@ bool TimeTicks::IsHighResolution() {
}
// static
-bool TimeTicks::IsConsistentAcrossProcesses() {
- return true;
-}
-
-// static
ThreadTicks ThreadTicks::Now() {
#if (defined(_POSIX_THREAD_CPUTIME) && (_POSIX_THREAD_CPUTIME >= 0)) || \
defined(OS_ANDROID)
diff --git a/base/time/time_unittest.cc b/base/time/time_unittest.cc
index 8906c3bee1..4f47d56522 100644
--- a/base/time/time_unittest.cc
+++ b/base/time/time_unittest.cc
@@ -54,16 +54,6 @@ TEST(TimeTestOutOfBounds, FromExplodedOutOfBoundsTime) {
{{2016, 10, 0, 25, 7, 47, 234, 0}, false},
// Milliseconds are too large
{{2016, 10, 0, 25, 6, 31, 23, 1643}, false},
- // Test overflow. Time is valid, but overflow case
- // results in Time(0).
- {{9840633, 1, 0, 1, 1, 1, 0, 0}, true},
- // Underflow will fail as well.
- {{-9840633, 1, 0, 1, 1, 1, 0, 0}, true},
- // Test integer overflow and underflow cases for the values themselves.
- {{std::numeric_limits<int>::min(), 1, 0, 1, 1, 1, 0, 0}, true},
- {{std::numeric_limits<int>::max(), 1, 0, 1, 1, 1, 0, 0}, true},
- {{2016, std::numeric_limits<int>::min(), 0, 1, 1, 1, 0, 0}, false},
- {{2016, std::numeric_limits<int>::max(), 0, 1, 1, 1, 0, 0}, false},
};
for (const auto& test : kDateTestData) {
@@ -816,29 +806,22 @@ TEST(TimeDelta, FromAndIn) {
#if defined(OS_POSIX)
TEST(TimeDelta, TimeSpecConversion) {
- TimeDelta delta = TimeDelta::FromSeconds(0);
- struct timespec result = delta.ToTimeSpec();
+ struct timespec result = TimeDelta::FromSeconds(0).ToTimeSpec();
EXPECT_EQ(result.tv_sec, 0);
EXPECT_EQ(result.tv_nsec, 0);
- EXPECT_EQ(delta, TimeDelta::FromTimeSpec(result));
- delta = TimeDelta::FromSeconds(1);
- result = delta.ToTimeSpec();
+ result = TimeDelta::FromSeconds(1).ToTimeSpec();
EXPECT_EQ(result.tv_sec, 1);
EXPECT_EQ(result.tv_nsec, 0);
- EXPECT_EQ(delta, TimeDelta::FromTimeSpec(result));
- delta = TimeDelta::FromMicroseconds(1);
- result = delta.ToTimeSpec();
+ result = TimeDelta::FromMicroseconds(1).ToTimeSpec();
EXPECT_EQ(result.tv_sec, 0);
EXPECT_EQ(result.tv_nsec, 1000);
- EXPECT_EQ(delta, TimeDelta::FromTimeSpec(result));
- delta = TimeDelta::FromMicroseconds(Time::kMicrosecondsPerSecond + 1);
- result = delta.ToTimeSpec();
+ result = TimeDelta::FromMicroseconds(
+ Time::kMicrosecondsPerSecond + 1).ToTimeSpec();
EXPECT_EQ(result.tv_sec, 1);
EXPECT_EQ(result.tv_nsec, 1000);
- EXPECT_EQ(delta, TimeDelta::FromTimeSpec(result));
}
#endif // OS_POSIX
@@ -1116,17 +1099,17 @@ TEST(TimeDeltaLogging, DCheckEqCompiles) {
TEST(TimeDeltaLogging, EmptyIsZero) {
TimeDelta zero;
- EXPECT_EQ("0 s", AnyToString(zero));
+ EXPECT_EQ("0s", AnyToString(zero));
}
TEST(TimeDeltaLogging, FiveHundredMs) {
TimeDelta five_hundred_ms = TimeDelta::FromMilliseconds(500);
- EXPECT_EQ("0.5 s", AnyToString(five_hundred_ms));
+ EXPECT_EQ("0.5s", AnyToString(five_hundred_ms));
}
TEST(TimeDeltaLogging, MinusTenSeconds) {
TimeDelta minus_ten_seconds = TimeDelta::FromSeconds(-10);
- EXPECT_EQ("-10 s", AnyToString(minus_ten_seconds));
+ EXPECT_EQ("-10s", AnyToString(minus_ten_seconds));
}
TEST(TimeDeltaLogging, DoesNotMessUpFormattingFlags) {
diff --git a/base/timer/timer.cc b/base/timer/timer.cc
index 6ec18f1814..e554905fff 100644
--- a/base/timer/timer.cc
+++ b/base/timer/timer.cc
@@ -6,15 +6,11 @@
#include <stddef.h>
-#include <utility>
-
#include "base/logging.h"
-#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/tick_clock.h"
namespace base {
@@ -64,36 +60,26 @@ class BaseTimerTaskInternal {
};
Timer::Timer(bool retain_user_task, bool is_repeating)
- : Timer(retain_user_task, is_repeating, nullptr) {}
-
-Timer::Timer(bool retain_user_task, bool is_repeating, TickClock* tick_clock)
- : scheduled_task_(nullptr),
+ : scheduled_task_(NULL),
thread_id_(0),
is_repeating_(is_repeating),
retain_user_task_(retain_user_task),
- tick_clock_(tick_clock),
- is_running_(false) {}
+ is_running_(false) {
+}
Timer::Timer(const tracked_objects::Location& posted_from,
TimeDelta delay,
const base::Closure& user_task,
bool is_repeating)
- : Timer(posted_from, delay, user_task, is_repeating, nullptr) {}
-
-Timer::Timer(const tracked_objects::Location& posted_from,
- TimeDelta delay,
- const base::Closure& user_task,
- bool is_repeating,
- TickClock* tick_clock)
- : scheduled_task_(nullptr),
+ : scheduled_task_(NULL),
posted_from_(posted_from),
delay_(delay),
user_task_(user_task),
thread_id_(0),
is_repeating_(is_repeating),
retain_user_task_(true),
- tick_clock_(tick_clock),
- is_running_(false) {}
+ is_running_(false) {
+}
Timer::~Timer() {
StopAndAbandon();
@@ -137,7 +123,7 @@ void Timer::Reset() {
// Set the new desired_run_time_.
if (delay_ > TimeDelta::FromMicroseconds(0))
- desired_run_time_ = Now() + delay_;
+ desired_run_time_ = TimeTicks::Now() + delay_;
else
desired_run_time_ = TimeTicks();
@@ -153,10 +139,6 @@ void Timer::Reset() {
PostNewScheduledTask(delay_);
}
-TimeTicks Timer::Now() const {
- return tick_clock_ ? tick_clock_->NowTicks() : TimeTicks::Now();
-}
-
void Timer::SetTaskInfo(const tracked_objects::Location& posted_from,
TimeDelta delay,
const base::Closure& user_task) {
@@ -173,7 +155,7 @@ void Timer::PostNewScheduledTask(TimeDelta delay) {
GetTaskRunner()->PostDelayedTask(posted_from_,
base::Bind(&BaseTimerTaskInternal::Run, base::Owned(scheduled_task_)),
delay);
- scheduled_run_time_ = desired_run_time_ = Now() + delay;
+ scheduled_run_time_ = desired_run_time_ = TimeTicks::Now() + delay;
} else {
GetTaskRunner()->PostTask(posted_from_,
base::Bind(&BaseTimerTaskInternal::Run, base::Owned(scheduled_task_)));
@@ -181,8 +163,10 @@ void Timer::PostNewScheduledTask(TimeDelta delay) {
}
// Remember the thread ID that posts the first task -- this will be verified
// later when the task is abandoned to detect misuse from multiple threads.
- if (!thread_id_)
+ if (!thread_id_) {
+ DCHECK(GetTaskRunner()->BelongsToCurrentThread());
thread_id_ = static_cast<int>(PlatformThread::CurrentId());
+ }
}
scoped_refptr<SingleThreadTaskRunner> Timer::GetTaskRunner() {
@@ -205,9 +189,9 @@ void Timer::RunScheduledTask() {
// First check if we need to delay the task because of a new target time.
if (desired_run_time_ > scheduled_run_time_) {
- // Now() can be expensive, so only call it if we know the user has changed
- // the desired_run_time_.
- TimeTicks now = Now();
+ // TimeTicks::Now() can be expensive, so only call it if we know the user
+ // has changed the desired_run_time_.
+ TimeTicks now = TimeTicks::Now();
// Task runner may have called us late anyway, so only post a continuation
// task if the desired_run_time_ is in the future.
if (desired_run_time_ > now) {
diff --git a/base/timer/timer.h b/base/timer/timer.h
index 8aac279def..661829b513 100644
--- a/base/timer/timer.h
+++ b/base/timer/timer.h
@@ -49,8 +49,6 @@
// because they're flaky on the buildbot, but when you run them locally you
// should be able to tell the difference.
-#include <memory>
-
#include "base/base_export.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
@@ -63,7 +61,6 @@ namespace base {
class BaseTimerTaskInternal;
class SingleThreadTaskRunner;
-class TickClock;
//-----------------------------------------------------------------------------
// This class wraps MessageLoop::PostDelayedTask to manage delayed and repeating
@@ -74,23 +71,14 @@ class BASE_EXPORT Timer {
public:
// Construct a timer in repeating or one-shot mode. Start or SetTaskInfo must
// be called later to set task info. |retain_user_task| determines whether the
- // user_task is retained or reset when it runs or stops. If |tick_clock| is
- // provided, it is used instead of TimeTicks::Now() to get TimeTicks when
- // scheduling tasks.
+ // user_task is retained or reset when it runs or stops.
Timer(bool retain_user_task, bool is_repeating);
- Timer(bool retain_user_task, bool is_repeating, TickClock* tick_clock);
- // Construct a timer with retained task info. If |tick_clock| is provided, it
- // is used instead of TimeTicks::Now() to get TimeTicks when scheduling tasks.
+ // Construct a timer with retained task info.
Timer(const tracked_objects::Location& posted_from,
TimeDelta delay,
const base::Closure& user_task,
bool is_repeating);
- Timer(const tracked_objects::Location& posted_from,
- TimeDelta delay,
- const base::Closure& user_task,
- bool is_repeating,
- TickClock* tick_clock);
virtual ~Timer();
@@ -123,9 +111,6 @@ class BASE_EXPORT Timer {
const TimeTicks& desired_run_time() const { return desired_run_time_; }
protected:
- // Returns the current tick count.
- TimeTicks Now() const;
-
// Used to initiate a new delayed task. This has the side-effect of disabling
// scheduled_task_ if it is non-null.
void SetTaskInfo(const tracked_objects::Location& posted_from,
@@ -163,10 +148,8 @@ class BASE_EXPORT Timer {
// Stop running task (if any) and abandon scheduled task (if any).
void StopAndAbandon() {
- AbandonScheduledTask();
-
Stop();
- // No more member accesses here: |this| could be deleted at this point.
+ AbandonScheduledTask();
}
// When non-NULL, the scheduled_task_ is waiting in the MessageLoop to call
@@ -208,9 +191,6 @@ class BASE_EXPORT Timer {
// If true, hold on to the user_task_ closure object for reuse.
const bool retain_user_task_;
- // The tick clock used to calculate the run time for scheduled tasks.
- TickClock* const tick_clock_;
-
// If true, user_task_ is scheduled to run sometime in the future.
bool is_running_;
@@ -230,8 +210,8 @@ class BaseTimerMethodPointer : public Timer {
using Timer::Start;
enum RepeatMode { ONE_SHOT, REPEATING };
- BaseTimerMethodPointer(RepeatMode mode, TickClock* tick_clock)
- : Timer(mode == REPEATING, mode == REPEATING, tick_clock) {}
+ BaseTimerMethodPointer(RepeatMode mode)
+ : Timer(mode == REPEATING, mode == REPEATING) {}
// Start the timer to run at the given |delay| from now. If the timer is
// already running, it will be replaced to call a task formed from
@@ -250,18 +230,14 @@ class BaseTimerMethodPointer : public Timer {
// A simple, one-shot timer. See usage notes at the top of the file.
class OneShotTimer : public BaseTimerMethodPointer {
public:
- OneShotTimer() : OneShotTimer(nullptr) {}
- explicit OneShotTimer(TickClock* tick_clock)
- : BaseTimerMethodPointer(ONE_SHOT, tick_clock) {}
+ OneShotTimer() : BaseTimerMethodPointer(ONE_SHOT) {}
};
//-----------------------------------------------------------------------------
// A simple, repeating timer. See usage notes at the top of the file.
class RepeatingTimer : public BaseTimerMethodPointer {
public:
- RepeatingTimer() : RepeatingTimer(nullptr) {}
- explicit RepeatingTimer(TickClock* tick_clock)
- : BaseTimerMethodPointer(REPEATING, tick_clock) {}
+ RepeatingTimer() : BaseTimerMethodPointer(REPEATING) {}
};
//-----------------------------------------------------------------------------
@@ -282,19 +258,10 @@ class DelayTimer : protected Timer {
TimeDelta delay,
Receiver* receiver,
void (Receiver::*method)())
- : DelayTimer(posted_from, delay, receiver, method, nullptr) {}
-
- template <class Receiver>
- DelayTimer(const tracked_objects::Location& posted_from,
- TimeDelta delay,
- Receiver* receiver,
- void (Receiver::*method)(),
- TickClock* tick_clock)
: Timer(posted_from,
delay,
base::Bind(method, base::Unretained(receiver)),
- false,
- tick_clock) {}
+ false) {}
void Reset() override;
};
diff --git a/base/timer/timer_unittest.cc b/base/timer/timer_unittest.cc
index 69338eb211..6fcd25b93a 100644
--- a/base/timer/timer_unittest.cc
+++ b/base/timer/timer_unittest.cc
@@ -8,274 +8,189 @@
#include <memory>
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/callback.h"
#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
-#include "base/sequenced_task_runner.h"
-#include "base/single_thread_task_runner.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/test/sequenced_worker_pool_owner.h"
-#include "base/test/test_mock_time_task_runner.h"
-#include "base/threading/platform_thread.h"
-#include "base/threading/sequenced_task_runner_handle.h"
-#include "base/threading/thread.h"
+#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/tick_clock.h"
-#include "base/time/time.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
-namespace base {
+using base::TimeDelta;
+using base::SingleThreadTaskRunner;
namespace {
// The message loops on which each timer should be tested.
-const MessageLoop::Type testing_message_loops[] = {
- MessageLoop::TYPE_DEFAULT, MessageLoop::TYPE_IO,
+const base::MessageLoop::Type testing_message_loops[] = {
+ base::MessageLoop::TYPE_DEFAULT,
+ base::MessageLoop::TYPE_IO,
#if !defined(OS_IOS) // iOS does not allow direct running of the UI loop.
- MessageLoop::TYPE_UI,
+ base::MessageLoop::TYPE_UI,
#endif
};
const int kNumTestingMessageLoops = arraysize(testing_message_loops);
-class Receiver {
+class OneShotTimerTester {
public:
- Receiver() : count_(0) {}
- void OnCalled() { count_++; }
- bool WasCalled() { return count_ > 0; }
- int TimesCalled() { return count_; }
-
- private:
- int count_;
-};
-
-// A basic helper class that can start a one-shot timer and signal a
-// WaitableEvent when this timer fires.
-class OneShotTimerTesterBase {
- public:
- // |did_run|, if provided, will be signaled when Run() fires.
- explicit OneShotTimerTesterBase(
- WaitableEvent* did_run = nullptr,
- const TimeDelta& delay = TimeDelta::FromMilliseconds(10))
- : did_run_(did_run), delay_(delay) {}
-
- virtual ~OneShotTimerTesterBase() = default;
+ explicit OneShotTimerTester(bool* did_run, unsigned milliseconds = 10)
+ : did_run_(did_run),
+ delay_ms_(milliseconds),
+ quit_message_loop_(true) {
+ }
void Start() {
- started_time_ = TimeTicks::Now();
- timer_->Start(FROM_HERE, delay_, this, &OneShotTimerTesterBase::Run);
+ timer_.Start(FROM_HERE, TimeDelta::FromMilliseconds(delay_ms_), this,
+ &OneShotTimerTester::Run);
}
- bool IsRunning() { return timer_->IsRunning(); }
-
- TimeTicks started_time() const { return started_time_; }
- TimeDelta delay() const { return delay_; }
-
- protected:
- virtual void Run() {
- if (did_run_) {
- EXPECT_FALSE(did_run_->IsSignaled());
- did_run_->Signal();
- }
+ void SetTaskRunner(scoped_refptr<SingleThreadTaskRunner> task_runner) {
+ quit_message_loop_ = false;
+ timer_.SetTaskRunner(task_runner);
}
- std::unique_ptr<OneShotTimer> timer_ = MakeUnique<OneShotTimer>();
-
private:
- WaitableEvent* const did_run_;
- const TimeDelta delay_;
- TimeTicks started_time_;
+ void Run() {
+ *did_run_ = true;
+ if (quit_message_loop_) {
+ base::MessageLoop::current()->QuitWhenIdle();
+ }
+ }
- DISALLOW_COPY_AND_ASSIGN(OneShotTimerTesterBase);
+ bool* did_run_;
+ base::OneShotTimer timer_;
+ const unsigned delay_ms_;
+ bool quit_message_loop_;
};
-// Extends functionality of OneShotTimerTesterBase with the abilities to wait
-// until the timer fires and to change task runner for the timer.
-class OneShotTimerTester : public OneShotTimerTesterBase {
+class OneShotSelfDeletingTimerTester {
public:
- // |did_run|, if provided, will be signaled when Run() fires.
- explicit OneShotTimerTester(
- WaitableEvent* did_run = nullptr,
- const TimeDelta& delay = TimeDelta::FromMilliseconds(10))
- : OneShotTimerTesterBase(did_run, delay),
- quit_closure_(run_loop_.QuitClosure()) {}
-
- ~OneShotTimerTester() override = default;
-
- void SetTaskRunner(scoped_refptr<SingleThreadTaskRunner> task_runner) {
- timer_->SetTaskRunner(std::move(task_runner));
-
- // Run() will be invoked on |task_runner| but |run_loop_|'s QuitClosure
- // needs to run on this thread (where the MessageLoop lives).
- quit_closure_ =
- Bind(IgnoreResult(&SingleThreadTaskRunner::PostTask),
- ThreadTaskRunnerHandle::Get(), FROM_HERE, run_loop_.QuitClosure());
- }
-
- // Blocks until Run() executes and confirms that Run() didn't fire before
- // |delay_| expired.
- void WaitAndConfirmTimerFiredAfterDelay() {
- run_loop_.Run();
+ explicit OneShotSelfDeletingTimerTester(bool* did_run)
+ : did_run_(did_run), timer_(new base::OneShotTimer()) {}
- EXPECT_NE(TimeTicks(), started_time());
- EXPECT_GE(TimeTicks::Now() - started_time(), delay());
+ void Start() {
+ timer_->Start(FROM_HERE, TimeDelta::FromMilliseconds(10), this,
+ &OneShotSelfDeletingTimerTester::Run);
}
- protected:
- // Overridable method to do things on Run() before signaling events/closures
- // managed by this helper.
- virtual void OnRun() {}
-
private:
- void Run() override {
- OnRun();
- OneShotTimerTesterBase::Run();
- quit_closure_.Run();
+ void Run() {
+ *did_run_ = true;
+ timer_.reset();
+ base::MessageLoop::current()->QuitWhenIdle();
}
- RunLoop run_loop_;
- Closure quit_closure_;
-
- DISALLOW_COPY_AND_ASSIGN(OneShotTimerTester);
-};
-
-class OneShotSelfDeletingTimerTester : public OneShotTimerTester {
- protected:
- void OnRun() override { timer_.reset(); }
+ bool* did_run_;
+ std::unique_ptr<base::OneShotTimer> timer_;
};
-constexpr int kNumRepeats = 10;
-
class RepeatingTimerTester {
public:
- explicit RepeatingTimerTester(WaitableEvent* did_run, const TimeDelta& delay)
- : counter_(kNumRepeats),
- quit_closure_(run_loop_.QuitClosure()),
- did_run_(did_run),
- delay_(delay) {}
+ explicit RepeatingTimerTester(bool* did_run, const TimeDelta& delay)
+ : did_run_(did_run), counter_(10), delay_(delay) {
+ }
void Start() {
- started_time_ = TimeTicks::Now();
timer_.Start(FROM_HERE, delay_, this, &RepeatingTimerTester::Run);
}
- void WaitAndConfirmTimerFiredRepeatedlyAfterDelay() {
- run_loop_.Run();
-
- EXPECT_NE(TimeTicks(), started_time_);
- EXPECT_GE(TimeTicks::Now() - started_time_, kNumRepeats * delay_);
- }
-
private:
void Run() {
if (--counter_ == 0) {
- if (did_run_) {
- EXPECT_FALSE(did_run_->IsSignaled());
- did_run_->Signal();
- }
+ *did_run_ = true;
timer_.Stop();
- quit_closure_.Run();
+ base::MessageLoop::current()->QuitWhenIdle();
}
}
- RepeatingTimer timer_;
+ bool* did_run_;
int counter_;
-
- RunLoop run_loop_;
- Closure quit_closure_;
- WaitableEvent* const did_run_;
-
- const TimeDelta delay_;
- TimeTicks started_time_;
-
- DISALLOW_COPY_AND_ASSIGN(RepeatingTimerTester);
+ TimeDelta delay_;
+ base::RepeatingTimer timer_;
};
-// Basic test with same setup as RunTest_OneShotTimers_Cancel below to confirm
-// that |did_run_a| would be signaled in that test if it wasn't for the
-// deletion.
-void RunTest_OneShotTimers(MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
-
- WaitableEvent did_run_a(WaitableEvent::ResetPolicy::MANUAL,
- WaitableEvent::InitialState::NOT_SIGNALED);
- OneShotTimerTester a(&did_run_a);
- a.Start();
+void RunTest_OneShotTimer(base::MessageLoop::Type message_loop_type) {
+ base::MessageLoop loop(message_loop_type);
- OneShotTimerTester b;
- b.Start();
+ bool did_run = false;
+ OneShotTimerTester f(&did_run);
+ f.Start();
- b.WaitAndConfirmTimerFiredAfterDelay();
+ base::RunLoop().Run();
- EXPECT_TRUE(did_run_a.IsSignaled());
+ EXPECT_TRUE(did_run);
}
-void RunTest_OneShotTimers_Cancel(MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
+void RunTest_OneShotTimer_Cancel(base::MessageLoop::Type message_loop_type) {
+ base::MessageLoop loop(message_loop_type);
- WaitableEvent did_run_a(WaitableEvent::ResetPolicy::MANUAL,
- WaitableEvent::InitialState::NOT_SIGNALED);
+ bool did_run_a = false;
OneShotTimerTester* a = new OneShotTimerTester(&did_run_a);
// This should run before the timer expires.
- SequencedTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, a);
+ base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, a);
// Now start the timer.
a->Start();
- OneShotTimerTester b;
+ bool did_run_b = false;
+ OneShotTimerTester b(&did_run_b);
b.Start();
- b.WaitAndConfirmTimerFiredAfterDelay();
+ base::RunLoop().Run();
- EXPECT_FALSE(did_run_a.IsSignaled());
+ EXPECT_FALSE(did_run_a);
+ EXPECT_TRUE(did_run_b);
}
-void RunTest_OneShotSelfDeletingTimer(MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
+void RunTest_OneShotSelfDeletingTimer(
+ base::MessageLoop::Type message_loop_type) {
+ base::MessageLoop loop(message_loop_type);
- OneShotSelfDeletingTimerTester f;
+ bool did_run = false;
+ OneShotSelfDeletingTimerTester f(&did_run);
f.Start();
- f.WaitAndConfirmTimerFiredAfterDelay();
+
+ base::RunLoop().Run();
+
+ EXPECT_TRUE(did_run);
}
-void RunTest_RepeatingTimer(MessageLoop::Type message_loop_type,
+void RunTest_RepeatingTimer(base::MessageLoop::Type message_loop_type,
const TimeDelta& delay) {
- MessageLoop loop(message_loop_type);
+ base::MessageLoop loop(message_loop_type);
- RepeatingTimerTester f(nullptr, delay);
+ bool did_run = false;
+ RepeatingTimerTester f(&did_run, delay);
f.Start();
- f.WaitAndConfirmTimerFiredRepeatedlyAfterDelay();
+
+ base::RunLoop().Run();
+
+ EXPECT_TRUE(did_run);
}
-void RunTest_RepeatingTimer_Cancel(MessageLoop::Type message_loop_type,
+void RunTest_RepeatingTimer_Cancel(base::MessageLoop::Type message_loop_type,
const TimeDelta& delay) {
- MessageLoop loop(message_loop_type);
+ base::MessageLoop loop(message_loop_type);
- WaitableEvent did_run_a(WaitableEvent::ResetPolicy::MANUAL,
- WaitableEvent::InitialState::NOT_SIGNALED);
+ bool did_run_a = false;
RepeatingTimerTester* a = new RepeatingTimerTester(&did_run_a, delay);
// This should run before the timer expires.
- SequencedTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, a);
+ base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, a);
// Now start the timer.
a->Start();
- RepeatingTimerTester b(nullptr, delay);
+ bool did_run_b = false;
+ RepeatingTimerTester b(&did_run_b, delay);
b.Start();
- b.WaitAndConfirmTimerFiredRepeatedlyAfterDelay();
+ base::RunLoop().Run();
- // |a| should not have fired despite |b| starting after it on the same
- // sequence and being complete by now.
- EXPECT_FALSE(did_run_a.IsSignaled());
+ EXPECT_FALSE(did_run_a);
+ EXPECT_TRUE(did_run_b);
}
class DelayTimerTarget {
@@ -291,38 +206,40 @@ class DelayTimerTarget {
bool signaled_ = false;
};
-void RunTest_DelayTimer_NoCall(MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
+void RunTest_DelayTimer_NoCall(base::MessageLoop::Type message_loop_type) {
+ base::MessageLoop loop(message_loop_type);
// If Delay is never called, the timer shouldn't go off.
DelayTimerTarget target;
- DelayTimer timer(FROM_HERE, TimeDelta::FromMilliseconds(1), &target,
- &DelayTimerTarget::Signal);
+ base::DelayTimer timer(FROM_HERE, TimeDelta::FromMilliseconds(1), &target,
+ &DelayTimerTarget::Signal);
- OneShotTimerTester tester;
+ bool did_run = false;
+ OneShotTimerTester tester(&did_run);
tester.Start();
- tester.WaitAndConfirmTimerFiredAfterDelay();
+ base::RunLoop().Run();
ASSERT_FALSE(target.signaled());
}
-void RunTest_DelayTimer_OneCall(MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
+void RunTest_DelayTimer_OneCall(base::MessageLoop::Type message_loop_type) {
+ base::MessageLoop loop(message_loop_type);
DelayTimerTarget target;
- DelayTimer timer(FROM_HERE, TimeDelta::FromMilliseconds(1), &target,
- &DelayTimerTarget::Signal);
+ base::DelayTimer timer(FROM_HERE, TimeDelta::FromMilliseconds(1), &target,
+ &DelayTimerTarget::Signal);
timer.Reset();
- OneShotTimerTester tester(nullptr, TimeDelta::FromMilliseconds(100));
+ bool did_run = false;
+ OneShotTimerTester tester(&did_run, 100 /* milliseconds */);
tester.Start();
- tester.WaitAndConfirmTimerFiredAfterDelay();
+ base::RunLoop().Run();
ASSERT_TRUE(target.signaled());
}
struct ResetHelper {
- ResetHelper(DelayTimer* timer, DelayTimerTarget* target)
+ ResetHelper(base::DelayTimer* timer, DelayTimerTarget* target)
: timer_(timer), target_(target) {}
void Reset() {
@@ -331,30 +248,31 @@ struct ResetHelper {
}
private:
- DelayTimer* const timer_;
+ base::DelayTimer* const timer_;
DelayTimerTarget* const target_;
};
-void RunTest_DelayTimer_Reset(MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
+void RunTest_DelayTimer_Reset(base::MessageLoop::Type message_loop_type) {
+ base::MessageLoop loop(message_loop_type);
// If Delay is never called, the timer shouldn't go off.
DelayTimerTarget target;
- DelayTimer timer(FROM_HERE, TimeDelta::FromMilliseconds(50), &target,
- &DelayTimerTarget::Signal);
+ base::DelayTimer timer(FROM_HERE, TimeDelta::FromMilliseconds(50), &target,
+ &DelayTimerTarget::Signal);
timer.Reset();
ResetHelper reset_helper(&timer, &target);
- OneShotTimer timers[20];
+ base::OneShotTimer timers[20];
for (size_t i = 0; i < arraysize(timers); ++i) {
timers[i].Start(FROM_HERE, TimeDelta::FromMilliseconds(i * 10),
&reset_helper, &ResetHelper::Reset);
}
- OneShotTimerTester tester(nullptr, TimeDelta::FromMilliseconds(300));
+ bool did_run = false;
+ OneShotTimerTester tester(&did_run, 300);
tester.Start();
- tester.WaitAndConfirmTimerFiredAfterDelay();
+ base::RunLoop().Run();
ASSERT_TRUE(target.signaled());
}
@@ -366,20 +284,21 @@ class DelayTimerFatalTarget {
}
};
-void RunTest_DelayTimer_Deleted(MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
+
+void RunTest_DelayTimer_Deleted(base::MessageLoop::Type message_loop_type) {
+ base::MessageLoop loop(message_loop_type);
DelayTimerFatalTarget target;
{
- DelayTimer timer(FROM_HERE, TimeDelta::FromMilliseconds(50), &target,
- &DelayTimerFatalTarget::Signal);
+ base::DelayTimer timer(FROM_HERE, TimeDelta::FromMilliseconds(50), &target,
+ &DelayTimerFatalTarget::Signal);
timer.Reset();
}
// When the timer is deleted, the DelayTimerFatalTarget should never be
// called.
- PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
}
} // namespace
@@ -388,15 +307,15 @@ void RunTest_DelayTimer_Deleted(MessageLoop::Type message_loop_type) {
// Each test is run against each type of MessageLoop. That way we are sure
// that timers work properly in all configurations.
-TEST(TimerTest, OneShotTimers) {
+TEST(TimerTest, OneShotTimer) {
for (int i = 0; i < kNumTestingMessageLoops; i++) {
- RunTest_OneShotTimers(testing_message_loops[i]);
+ RunTest_OneShotTimer(testing_message_loops[i]);
}
}
-TEST(TimerTest, OneShotTimers_Cancel) {
+TEST(TimerTest, OneShotTimer_Cancel) {
for (int i = 0; i < kNumTestingMessageLoops; i++) {
- RunTest_OneShotTimers_Cancel(testing_message_loops[i]);
+ RunTest_OneShotTimer_Cancel(testing_message_loops[i]);
}
}
@@ -409,42 +328,17 @@ TEST(TimerTest, OneShotSelfDeletingTimer) {
}
TEST(TimerTest, OneShotTimer_CustomTaskRunner) {
- // A MessageLoop is required for the timer events on the other thread to
- // communicate back to the Timer under test.
- MessageLoop loop;
-
- Thread other_thread("OneShotTimer_CustomTaskRunner");
- other_thread.Start();
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner =
+ new base::TestSimpleTaskRunner();
- WaitableEvent did_run(WaitableEvent::ResetPolicy::MANUAL,
- WaitableEvent::InitialState::NOT_SIGNALED);
+ bool did_run = false;
OneShotTimerTester f(&did_run);
- f.SetTaskRunner(other_thread.task_runner());
+ f.SetTaskRunner(task_runner);
f.Start();
- EXPECT_TRUE(f.IsRunning());
-
- f.WaitAndConfirmTimerFiredAfterDelay();
- EXPECT_TRUE(did_run.IsSignaled());
-
- // |f| should already have communicated back to this |loop| before invoking
- // Run() and as such this thread should already be aware that |f| is no longer
- // running.
- EXPECT_TRUE(loop.IsIdleForTesting());
- EXPECT_FALSE(f.IsRunning());
-}
-TEST(TimerTest, OneShotTimerWithTickClock) {
- scoped_refptr<TestMockTimeTaskRunner> task_runner(
- new TestMockTimeTaskRunner(Time::Now(), TimeTicks::Now()));
- std::unique_ptr<TickClock> tick_clock(task_runner->GetMockTickClock());
- MessageLoop message_loop;
- message_loop.SetTaskRunner(task_runner);
- Receiver receiver;
- OneShotTimer timer(tick_clock.get());
- timer.Start(FROM_HERE, TimeDelta::FromSeconds(1),
- Bind(&Receiver::OnCalled, Unretained(&receiver)));
- task_runner->FastForwardBy(TimeDelta::FromSeconds(1));
- EXPECT_TRUE(receiver.WasCalled());
+ EXPECT_FALSE(did_run);
+ task_runner->RunUntilIdle();
+ EXPECT_TRUE(did_run);
}
TEST(TimerTest, RepeatingTimer) {
@@ -475,22 +369,6 @@ TEST(TimerTest, RepeatingTimerZeroDelay_Cancel) {
}
}
-TEST(TimerTest, RepeatingTimerWithTickClock) {
- scoped_refptr<TestMockTimeTaskRunner> task_runner(
- new TestMockTimeTaskRunner(Time::Now(), TimeTicks::Now()));
- std::unique_ptr<TickClock> tick_clock(task_runner->GetMockTickClock());
- MessageLoop message_loop;
- message_loop.SetTaskRunner(task_runner);
- Receiver receiver;
- const int expected_times_called = 10;
- RepeatingTimer timer(tick_clock.get());
- timer.Start(FROM_HERE, TimeDelta::FromSeconds(1),
- Bind(&Receiver::OnCalled, Unretained(&receiver)));
- task_runner->FastForwardBy(TimeDelta::FromSeconds(expected_times_called));
- timer.Stop();
- EXPECT_EQ(expected_times_called, receiver.TimesCalled());
-}
-
TEST(TimerTest, DelayTimer_NoCall) {
for (int i = 0; i < kNumTestingMessageLoops; i++) {
RunTest_DelayTimer_NoCall(testing_message_loops[i]);
@@ -516,89 +394,25 @@ TEST(TimerTest, DelayTimer_Deleted) {
}
}
-TEST(TimerTest, DelayTimerWithTickClock) {
- scoped_refptr<TestMockTimeTaskRunner> task_runner(
- new TestMockTimeTaskRunner(Time::Now(), TimeTicks::Now()));
- std::unique_ptr<TickClock> tick_clock(task_runner->GetMockTickClock());
- MessageLoop message_loop;
- message_loop.SetTaskRunner(task_runner);
- Receiver receiver;
- DelayTimer timer(FROM_HERE, TimeDelta::FromSeconds(1), &receiver,
- &Receiver::OnCalled, tick_clock.get());
- task_runner->FastForwardBy(TimeDelta::FromMilliseconds(999));
- EXPECT_FALSE(receiver.WasCalled());
- timer.Reset();
- task_runner->FastForwardBy(TimeDelta::FromMilliseconds(999));
- EXPECT_FALSE(receiver.WasCalled());
- timer.Reset();
- task_runner->FastForwardBy(TimeDelta::FromSeconds(1));
- EXPECT_TRUE(receiver.WasCalled());
-}
-
TEST(TimerTest, MessageLoopShutdown) {
// This test is designed to verify that shutdown of the
// message loop does not cause crashes if there were pending
// timers not yet fired. It may only trigger exceptions
// if debug heap checking is enabled.
- WaitableEvent did_run(WaitableEvent::ResetPolicy::MANUAL,
- WaitableEvent::InitialState::NOT_SIGNALED);
+ bool did_run = false;
{
- OneShotTimerTesterBase a(&did_run);
- OneShotTimerTesterBase b(&did_run);
- OneShotTimerTesterBase c(&did_run);
- OneShotTimerTesterBase d(&did_run);
+ OneShotTimerTester a(&did_run);
+ OneShotTimerTester b(&did_run);
+ OneShotTimerTester c(&did_run);
+ OneShotTimerTester d(&did_run);
{
- MessageLoop loop;
+ base::MessageLoop loop;
a.Start();
b.Start();
} // MessageLoop destructs by falling out of scope.
} // OneShotTimers destruct. SHOULD NOT CRASH, of course.
- EXPECT_FALSE(did_run.IsSignaled());
-}
-
-// Ref counted class which owns a Timer. The class passes a reference to itself
-// via the |user_task| parameter in Timer::Start(). |Timer::user_task_| might
-// end up holding the last reference to the class.
-class OneShotSelfOwningTimerTester
- : public RefCounted<OneShotSelfOwningTimerTester> {
- public:
- OneShotSelfOwningTimerTester() = default;
-
- void StartTimer() {
- // Start timer with long delay in order to test the timer getting destroyed
- // while a timer task is still pending.
- timer_.Start(FROM_HERE, TimeDelta::FromDays(1),
- base::Bind(&OneShotSelfOwningTimerTester::Run, this));
- }
-
- private:
- friend class RefCounted<OneShotSelfOwningTimerTester>;
- ~OneShotSelfOwningTimerTester() = default;
-
- void Run() {
- ADD_FAILURE() << "Timer unexpectedly fired.";
- }
-
- OneShotTimer timer_;
-
- DISALLOW_COPY_AND_ASSIGN(OneShotSelfOwningTimerTester);
-};
-
-TEST(TimerTest, MessageLoopShutdownSelfOwningTimer) {
- // This test verifies that shutdown of the message loop does not cause crashes
- // if there is a pending timer not yet fired and |Timer::user_task_| owns the
- // timer. The test may only trigger exceptions if debug heap checking is
- // enabled.
-
- MessageLoop loop;
- scoped_refptr<OneShotSelfOwningTimerTester> tester =
- new OneShotSelfOwningTimerTester();
-
- std::move(tester)->StartTimer();
- // |Timer::user_task_| owns sole reference to |tester|.
-
- // MessageLoop destructs by falling out of scope. SHOULD NOT CRASH.
+ EXPECT_FALSE(did_run);
}
void TimerTestCallback() {
@@ -606,10 +420,11 @@ void TimerTestCallback() {
TEST(TimerTest, NonRepeatIsRunning) {
{
- MessageLoop loop;
- Timer timer(false, false);
+ base::MessageLoop loop;
+ base::Timer timer(false, false);
EXPECT_FALSE(timer.IsRunning());
- timer.Start(FROM_HERE, TimeDelta::FromDays(1), Bind(&TimerTestCallback));
+ timer.Start(FROM_HERE, TimeDelta::FromDays(1),
+ base::Bind(&TimerTestCallback));
EXPECT_TRUE(timer.IsRunning());
timer.Stop();
EXPECT_FALSE(timer.IsRunning());
@@ -617,10 +432,11 @@ TEST(TimerTest, NonRepeatIsRunning) {
}
{
- Timer timer(true, false);
- MessageLoop loop;
+ base::Timer timer(true, false);
+ base::MessageLoop loop;
EXPECT_FALSE(timer.IsRunning());
- timer.Start(FROM_HERE, TimeDelta::FromDays(1), Bind(&TimerTestCallback));
+ timer.Start(FROM_HERE, TimeDelta::FromDays(1),
+ base::Bind(&TimerTestCallback));
EXPECT_TRUE(timer.IsRunning());
timer.Stop();
EXPECT_FALSE(timer.IsRunning());
@@ -631,11 +447,12 @@ TEST(TimerTest, NonRepeatIsRunning) {
}
TEST(TimerTest, NonRepeatMessageLoopDeath) {
- Timer timer(false, false);
+ base::Timer timer(false, false);
{
- MessageLoop loop;
+ base::MessageLoop loop;
EXPECT_FALSE(timer.IsRunning());
- timer.Start(FROM_HERE, TimeDelta::FromDays(1), Bind(&TimerTestCallback));
+ timer.Start(FROM_HERE, TimeDelta::FromDays(1),
+ base::Bind(&TimerTestCallback));
EXPECT_TRUE(timer.IsRunning());
}
EXPECT_FALSE(timer.IsRunning());
@@ -643,9 +460,9 @@ TEST(TimerTest, NonRepeatMessageLoopDeath) {
}
TEST(TimerTest, RetainRepeatIsRunning) {
- MessageLoop loop;
- Timer timer(FROM_HERE, TimeDelta::FromDays(1), Bind(&TimerTestCallback),
- true);
+ base::MessageLoop loop;
+ base::Timer timer(FROM_HERE, TimeDelta::FromDays(1),
+ base::Bind(&TimerTestCallback), true);
EXPECT_FALSE(timer.IsRunning());
timer.Reset();
EXPECT_TRUE(timer.IsRunning());
@@ -656,9 +473,9 @@ TEST(TimerTest, RetainRepeatIsRunning) {
}
TEST(TimerTest, RetainNonRepeatIsRunning) {
- MessageLoop loop;
- Timer timer(FROM_HERE, TimeDelta::FromDays(1), Bind(&TimerTestCallback),
- false);
+ base::MessageLoop loop;
+ base::Timer timer(FROM_HERE, TimeDelta::FromDays(1),
+ base::Bind(&TimerTestCallback), false);
EXPECT_FALSE(timer.IsRunning());
timer.Reset();
EXPECT_TRUE(timer.IsRunning());
@@ -680,27 +497,25 @@ void ClearAllCallbackHappened() {
void SetCallbackHappened1() {
g_callback_happened1 = true;
- MessageLoop::current()->QuitWhenIdle();
+ base::MessageLoop::current()->QuitWhenIdle();
}
void SetCallbackHappened2() {
g_callback_happened2 = true;
- MessageLoop::current()->QuitWhenIdle();
+ base::MessageLoop::current()->QuitWhenIdle();
}
-} // namespace
-
TEST(TimerTest, ContinuationStopStart) {
{
ClearAllCallbackHappened();
- MessageLoop loop;
- Timer timer(false, false);
+ base::MessageLoop loop;
+ base::Timer timer(false, false);
timer.Start(FROM_HERE, TimeDelta::FromMilliseconds(10),
- Bind(&SetCallbackHappened1));
+ base::Bind(&SetCallbackHappened1));
timer.Stop();
timer.Start(FROM_HERE, TimeDelta::FromMilliseconds(40),
- Bind(&SetCallbackHappened2));
- RunLoop().Run();
+ base::Bind(&SetCallbackHappened2));
+ base::RunLoop().Run();
EXPECT_FALSE(g_callback_happened1);
EXPECT_TRUE(g_callback_happened2);
}
@@ -709,16 +524,16 @@ TEST(TimerTest, ContinuationStopStart) {
TEST(TimerTest, ContinuationReset) {
{
ClearAllCallbackHappened();
- MessageLoop loop;
- Timer timer(false, false);
+ base::MessageLoop loop;
+ base::Timer timer(false, false);
timer.Start(FROM_HERE, TimeDelta::FromMilliseconds(10),
- Bind(&SetCallbackHappened1));
+ base::Bind(&SetCallbackHappened1));
timer.Reset();
// Since Reset happened before task ran, the user_task must not be cleared:
ASSERT_FALSE(timer.user_task().is_null());
- RunLoop().Run();
+ base::RunLoop().Run();
EXPECT_TRUE(g_callback_happened1);
}
}
-} // namespace base
+} // namespace
diff --git a/base/trace_event/category_registry.cc b/base/trace_event/category_registry.cc
deleted file mode 100644
index e7c14606d6..0000000000
--- a/base/trace_event/category_registry.cc
+++ /dev/null
@@ -1,156 +0,0 @@
-// Copyright 2016 The Chromium 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/trace_event/category_registry.h"
-
-#include <string.h>
-
-#include <type_traits>
-
-#include "base/atomicops.h"
-#include "base/debug/leak_annotations.h"
-#include "base/logging.h"
-#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
-#include "base/trace_event/trace_category.h"
-
-namespace base {
-namespace trace_event {
-
-namespace {
-
-constexpr size_t kMaxCategories = 200;
-const int kNumBuiltinCategories = 4;
-
-// |g_categories| might end up causing creating dynamic initializers if not POD.
-static_assert(std::is_pod<TraceCategory>::value, "TraceCategory must be POD");
-
-// These entries must be kept consistent with the kCategory* consts below.
-TraceCategory g_categories[kMaxCategories] = {
- {0, 0, "tracing categories exhausted; must increase kMaxCategories"},
- {0, 0, "tracing already shutdown"}, // See kCategoryAlreadyShutdown below.
- {0, 0, "__metadata"}, // See kCategoryMetadata below.
- {0, 0, "toplevel"}, // Warmup the toplevel category.
-};
-
-base::subtle::AtomicWord g_category_index = kNumBuiltinCategories;
-
-bool IsValidCategoryPtr(const TraceCategory* category) {
- // If any of these are hit, something has cached a corrupt category pointer.
- uintptr_t ptr = reinterpret_cast<uintptr_t>(category);
- return ptr % sizeof(void*) == 0 &&
- ptr >= reinterpret_cast<uintptr_t>(&g_categories[0]) &&
- ptr <= reinterpret_cast<uintptr_t>(&g_categories[kMaxCategories - 1]);
-}
-
-} // namespace
-
-// static
-TraceCategory* const CategoryRegistry::kCategoryExhausted = &g_categories[0];
-TraceCategory* const CategoryRegistry::kCategoryAlreadyShutdown =
- &g_categories[1];
-TraceCategory* const CategoryRegistry::kCategoryMetadata = &g_categories[2];
-
-// static
-void CategoryRegistry::Initialize() {
- // Trace is enabled or disabled on one thread while other threads are
- // accessing the enabled flag. We don't care whether edge-case events are
- // traced or not, so we allow races on the enabled flag to keep the trace
- // macros fast.
- for (size_t i = 0; i < kMaxCategories; ++i) {
- ANNOTATE_BENIGN_RACE(g_categories[i].state_ptr(),
- "trace_event category enabled");
- // If this DCHECK is hit in a test it means that ResetForTesting() is not
- // called and the categories state leaks between test fixtures.
- DCHECK(!g_categories[i].is_enabled());
- }
-}
-
-// static
-void CategoryRegistry::ResetForTesting() {
- // reset_for_testing clears up only the enabled state and filters. The
- // categories themselves cannot be cleared up because the static pointers
- // injected by the macros still point to them and cannot be reset.
- for (size_t i = 0; i < kMaxCategories; ++i)
- g_categories[i].reset_for_testing();
-}
-
-// static
-TraceCategory* CategoryRegistry::GetCategoryByName(const char* category_name) {
- DCHECK(!strchr(category_name, '"'))
- << "Category names may not contain double quote";
-
- // The g_categories is append only, avoid using a lock for the fast path.
- size_t category_index = base::subtle::Acquire_Load(&g_category_index);
-
- // Search for pre-existing category group.
- for (size_t i = 0; i < category_index; ++i) {
- if (strcmp(g_categories[i].name(), category_name) == 0) {
- return &g_categories[i];
- }
- }
- return nullptr;
-}
-
-bool CategoryRegistry::GetOrCreateCategoryLocked(
- const char* category_name,
- CategoryInitializerFn category_initializer_fn,
- TraceCategory** category) {
- // This is the slow path: the lock is not held in the fastpath
- // (GetCategoryByName), so more than one thread could have reached here trying
- // to add the same category.
- *category = GetCategoryByName(category_name);
- if (*category)
- return false;
-
- // Create a new category.
- size_t category_index = base::subtle::Acquire_Load(&g_category_index);
- if (category_index >= kMaxCategories) {
- NOTREACHED() << "must increase kMaxCategories";
- *category = kCategoryExhausted;
- return false;
- }
-
- // TODO(primiano): this strdup should be removed. The only documented reason
- // for it was TraceWatchEvent, which is gone. However, something might have
- // ended up relying on this. Needs some auditing before removal.
- const char* category_name_copy = strdup(category_name);
- ANNOTATE_LEAKING_OBJECT_PTR(category_name_copy);
-
- *category = &g_categories[category_index];
- DCHECK(!(*category)->is_valid());
- DCHECK(!(*category)->is_enabled());
- (*category)->set_name(category_name_copy);
- category_initializer_fn(*category);
-
- // Update the max index now.
- base::subtle::Release_Store(&g_category_index, category_index + 1);
- return true;
-}
-
-// static
-const TraceCategory* CategoryRegistry::GetCategoryByStatePtr(
- const uint8_t* category_state) {
- const TraceCategory* category = TraceCategory::FromStatePtr(category_state);
- DCHECK(IsValidCategoryPtr(category));
- return category;
-}
-
-// static
-bool CategoryRegistry::IsBuiltinCategory(const TraceCategory* category) {
- DCHECK(IsValidCategoryPtr(category));
- return category < &g_categories[kNumBuiltinCategories];
-}
-
-// static
-CategoryRegistry::Range CategoryRegistry::GetAllCategories() {
- // The |g_categories| array is append only. We have to only guarantee to
- // not return an index to a category which is being initialized by
- // GetOrCreateCategoryByName().
- size_t category_index = base::subtle::Acquire_Load(&g_category_index);
- return CategoryRegistry::Range(&g_categories[0],
- &g_categories[category_index]);
-}
-
-} // namespace trace_event
-} // namespace base
diff --git a/base/trace_event/category_registry.h b/base/trace_event/category_registry.h
deleted file mode 100644
index 9c08efa3e1..0000000000
--- a/base/trace_event/category_registry.h
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2016 The Chromium 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_TRACE_EVENT_CATEGORY_REGISTRY_H_
-#define BASE_TRACE_EVENT_CATEGORY_REGISTRY_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include "base/base_export.h"
-#include "base/logging.h"
-
-namespace base {
-namespace trace_event {
-
-struct TraceCategory;
-class TraceCategoryTest;
-class TraceLog;
-
-// Allows fast and thread-safe acces to the state of all tracing categories.
-// All the methods in this class can be concurrently called on multiple threads,
-// unless otherwise noted (e.g., GetOrCreateCategoryLocked).
-// The reason why this is a fully static class with global state is to allow to
-// statically define known categories as global linker-initialized structs,
-// without requiring static initializers.
-class BASE_EXPORT CategoryRegistry {
- public:
- // Allows for-each iterations over a slice of the categories array.
- class Range {
- public:
- Range(TraceCategory* begin, TraceCategory* end) : begin_(begin), end_(end) {
- DCHECK_LE(begin, end);
- }
- TraceCategory* begin() const { return begin_; }
- TraceCategory* end() const { return end_; }
-
- private:
- TraceCategory* const begin_;
- TraceCategory* const end_;
- };
-
- // Known categories.
- static TraceCategory* const kCategoryExhausted;
- static TraceCategory* const kCategoryMetadata;
- static TraceCategory* const kCategoryAlreadyShutdown;
-
- // Returns a category entry from the Category.state_ptr() pointer.
- // TODO(primiano): trace macros should just keep a pointer to the entire
- // TraceCategory, not just the enabled state pointer. That would remove the
- // need for this function and make everything cleaner at no extra cost (as
- // long as the |state_| is the first field of the struct, which can be
- // guaranteed via static_assert, see TraceCategory ctor).
- static const TraceCategory* GetCategoryByStatePtr(
- const uint8_t* category_state);
-
- // Returns a category from its name or nullptr if not found.
- // The output |category| argument is an undefinitely lived pointer to the
- // TraceCategory owned by the registry. TRACE_EVENTx macros will cache this
- // pointer and use it for checks in their fast-paths.
- static TraceCategory* GetCategoryByName(const char* category_name);
-
- static bool IsBuiltinCategory(const TraceCategory*);
-
- private:
- friend class TraceCategoryTest;
- friend class TraceLog;
- using CategoryInitializerFn = void (*)(TraceCategory*);
-
- // Only for debugging/testing purposes, is a no-op on release builds.
- static void Initialize();
-
- // Resets the state of all categories, to clear up the state between tests.
- static void ResetForTesting();
-
- // Used to get/create a category in the slow-path. If the category exists
- // already, this has the same effect of GetCategoryByName and returns false.
- // If not, a new category is created and the CategoryInitializerFn is invoked
- // before retuning true. The caller must guarantee serialization: either call
- // this method from a single thread or hold a lock when calling this.
- static bool GetOrCreateCategoryLocked(const char* category_name,
- CategoryInitializerFn,
- TraceCategory**);
-
- // Allows to iterate over the valid categories in a for-each loop.
- // This includes builtin categories such as __metadata.
- static Range GetAllCategories();
-};
-
-} // namespace trace_event
-} // namespace base
-
-#endif // BASE_TRACE_EVENT_CATEGORY_REGISTRY_H_
diff --git a/base/trace_event/common/trace_event_common.h b/base/trace_event/common/trace_event_common.h
index bb6fa1b82b..0a04d62710 100644
--- a/base/trace_event/common/trace_event_common.h
+++ b/base/trace_event/common/trace_event_common.h
@@ -223,6 +223,49 @@
flow_flags, arg1_name, arg1_val, \
arg2_name, arg2_val)
+// UNSHIPPED_TRACE_EVENT* are like TRACE_EVENT* except that they are not
+// included in official builds.
+
+#if OFFICIAL_BUILD
+#undef TRACING_IS_OFFICIAL_BUILD
+#define TRACING_IS_OFFICIAL_BUILD 1
+#elif !defined(TRACING_IS_OFFICIAL_BUILD)
+#define TRACING_IS_OFFICIAL_BUILD 0
+#endif
+
+#if TRACING_IS_OFFICIAL_BUILD
+#define UNSHIPPED_TRACE_EVENT0(category_group, name) (void)0
+#define UNSHIPPED_TRACE_EVENT1(category_group, name, arg1_name, arg1_val) \
+ (void)0
+#define UNSHIPPED_TRACE_EVENT2(category_group, name, arg1_name, arg1_val, \
+ arg2_name, arg2_val) \
+ (void)0
+#define UNSHIPPED_TRACE_EVENT_INSTANT0(category_group, name, scope) (void)0
+#define UNSHIPPED_TRACE_EVENT_INSTANT1(category_group, name, scope, arg1_name, \
+ arg1_val) \
+ (void)0
+#define UNSHIPPED_TRACE_EVENT_INSTANT2(category_group, name, scope, arg1_name, \
+ arg1_val, arg2_name, arg2_val) \
+ (void)0
+#else
+#define UNSHIPPED_TRACE_EVENT0(category_group, name) \
+ TRACE_EVENT0(category_group, name)
+#define UNSHIPPED_TRACE_EVENT1(category_group, name, arg1_name, arg1_val) \
+ TRACE_EVENT1(category_group, name, arg1_name, arg1_val)
+#define UNSHIPPED_TRACE_EVENT2(category_group, name, arg1_name, arg1_val, \
+ arg2_name, arg2_val) \
+ TRACE_EVENT2(category_group, name, arg1_name, arg1_val, arg2_name, arg2_val)
+#define UNSHIPPED_TRACE_EVENT_INSTANT0(category_group, name, scope) \
+ TRACE_EVENT_INSTANT0(category_group, name, scope)
+#define UNSHIPPED_TRACE_EVENT_INSTANT1(category_group, name, scope, arg1_name, \
+ arg1_val) \
+ TRACE_EVENT_INSTANT1(category_group, name, scope, arg1_name, arg1_val)
+#define UNSHIPPED_TRACE_EVENT_INSTANT2(category_group, name, scope, arg1_name, \
+ arg1_val, arg2_name, arg2_val) \
+ TRACE_EVENT_INSTANT2(category_group, name, scope, arg1_name, arg1_val, \
+ arg2_name, arg2_val)
+#endif
+
// Records a single event called "name" immediately, with 0, 1 or 2
// associated arguments. If the category is not enabled, then this
// does nothing.
@@ -254,10 +297,20 @@
#define TRACE_EVENT_INSTANT_WITH_TIMESTAMP0(category_group, name, scope, \
timestamp) \
- INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP( \
- TRACE_EVENT_PHASE_INSTANT, category_group, name, timestamp, \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \
+ TRACE_EVENT_PHASE_INSTANT, category_group, name, 0, 0, timestamp, \
TRACE_EVENT_FLAG_NONE | scope)
+// Syntactic sugars for the sampling tracing in the main thread.
+#define TRACE_EVENT_SCOPED_SAMPLING_STATE(category, name) \
+ TRACE_EVENT_SCOPED_SAMPLING_STATE_FOR_BUCKET(0, category, name)
+#define TRACE_EVENT_GET_SAMPLING_STATE() \
+ TRACE_EVENT_GET_SAMPLING_STATE_FOR_BUCKET(0)
+#define TRACE_EVENT_SET_SAMPLING_STATE(category, name) \
+ TRACE_EVENT_SET_SAMPLING_STATE_FOR_BUCKET(0, category, name)
+#define TRACE_EVENT_SET_NONCONST_SAMPLING_STATE(categoryAndName) \
+ TRACE_EVENT_SET_NONCONST_SAMPLING_STATE_FOR_BUCKET(0, categoryAndName)
+
// Records a single BEGIN event called "name" immediately, with 0, 1 or 2
// associated arguments. If the category is not enabled, then this
// does nothing.
@@ -342,15 +395,10 @@
TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, \
arg2_name, arg2_val)
-#define TRACE_EVENT_MARK_WITH_TIMESTAMP0(category_group, name, timestamp) \
- INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP( \
- TRACE_EVENT_PHASE_MARK, category_group, name, timestamp, \
- TRACE_EVENT_FLAG_NONE)
-
#define TRACE_EVENT_MARK_WITH_TIMESTAMP1(category_group, name, timestamp, \
arg1_name, arg1_val) \
- INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP( \
- TRACE_EVENT_PHASE_MARK, category_group, name, timestamp, \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \
+ TRACE_EVENT_PHASE_MARK, category_group, name, 0, 0, timestamp, \
TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
#define TRACE_EVENT_COPY_MARK(category_group, name) \
@@ -358,8 +406,8 @@
TRACE_EVENT_FLAG_COPY)
#define TRACE_EVENT_COPY_MARK_WITH_TIMESTAMP(category_group, name, timestamp) \
- INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP( \
- TRACE_EVENT_PHASE_MARK, category_group, name, timestamp, \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \
+ TRACE_EVENT_PHASE_MARK, category_group, name, 0, 0, timestamp, \
TRACE_EVENT_FLAG_COPY)
// Similar to TRACE_EVENT_ENDx but with a custom |at| timestamp provided.
@@ -496,12 +544,6 @@
TRACE_EVENT_PHASE_SAMPLE, category_group, name, 0, thread_id, timestamp, \
TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, arg2_name, arg2_val)
-#define TRACE_EVENT_SAMPLE_WITH_ID1(category_group, name, id, arg1_name, \
- arg1_val) \
- INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_SAMPLE, category_group, \
- name, id, TRACE_EVENT_FLAG_NONE, arg1_name, \
- arg1_val)
-
// ASYNC_STEP_* APIs should be only used by legacy code. New code should
// consider using NESTABLE_ASYNC_* APIs to describe substeps within an async
// event.
@@ -570,13 +612,6 @@
TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id, \
TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE, \
arg1_name, arg1_val)
-#define TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP2(category_group, name, id, \
- timestamp, arg1_name, \
- arg1_val, arg2_name, arg2_val) \
- INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \
- TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id, \
- TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE, \
- arg1_name, arg1_val, arg2_name, arg2_val)
#define TRACE_EVENT_COPY_ASYNC_BEGIN_WITH_TIMESTAMP0(category_group, name, id, \
timestamp) \
INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \
@@ -666,13 +701,6 @@
TRACE_EVENT_PHASE_ASYNC_END, category_group, name, id, \
TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE, \
arg1_name, arg1_val)
-#define TRACE_EVENT_ASYNC_END_WITH_TIMESTAMP2(category_group, name, id, \
- timestamp, arg1_name, arg1_val, \
- arg2_name, arg2_val) \
- INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \
- TRACE_EVENT_PHASE_ASYNC_END, category_group, name, id, \
- TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE, \
- arg1_name, arg1_val, arg2_name, arg2_val)
// NESTABLE_ASYNC_* APIs are used to describe an async operation, which can
// be nested within a NESTABLE_ASYNC event and/or have inner NESTABLE_ASYNC
@@ -732,19 +760,16 @@
TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, arg2_name, arg2_val)
// Records a single NESTABLE_ASYNC_INSTANT event called "name" immediately,
-// with none, one or two associated argument. If the category is not enabled,
-// then this does nothing.
-#define TRACE_EVENT_NESTABLE_ASYNC_INSTANT0(category_group, name, id) \
- INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT, \
- category_group, name, id, \
- TRACE_EVENT_FLAG_NONE)
-
+// with one associated argument. If the category is not enabled, then this
+// does nothing.
#define TRACE_EVENT_NESTABLE_ASYNC_INSTANT1(category_group, name, id, \
arg1_name, arg1_val) \
INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT, \
category_group, name, id, \
TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
-
+// Records a single NESTABLE_ASYNC_INSTANT event called "name" immediately,
+// with 2 associated arguments. If the category is not enabled, then this
+// does nothing.
#define TRACE_EVENT_NESTABLE_ASYNC_INSTANT2( \
category_group, name, id, arg1_name, arg1_val, arg2_name, arg2_val) \
INTERNAL_TRACE_EVENT_ADD_WITH_ID( \
@@ -919,58 +944,48 @@
#define TRACE_EVENT_CLOCK_SYNC_ISSUER(sync_id, issue_ts, issue_end_ts) \
INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP( \
TRACE_EVENT_PHASE_CLOCK_SYNC, "__metadata", "clock_sync", \
- issue_end_ts, TRACE_EVENT_FLAG_NONE, \
- "sync_id", sync_id, "issue_ts", issue_ts)
+ issue_end_ts.ToInternalValue(), TRACE_EVENT_FLAG_NONE, \
+ "sync_id", sync_id, "issue_ts", issue_ts.ToInternalValue())
// Macros to track the life time and value of arbitrary client objects.
// See also TraceTrackableObject.
#define TRACE_EVENT_OBJECT_CREATED_WITH_ID(category_group, name, id) \
INTERNAL_TRACE_EVENT_ADD_WITH_ID( \
- TRACE_EVENT_PHASE_CREATE_OBJECT, category_group, name, id, \
- TRACE_EVENT_FLAG_NONE)
+ TRACE_EVENT_PHASE_CREATE_OBJECT, category_group, name, \
+ TRACE_ID_DONT_MANGLE(id), TRACE_EVENT_FLAG_NONE)
#define TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(category_group, name, id, \
snapshot) \
INTERNAL_TRACE_EVENT_ADD_WITH_ID( \
TRACE_EVENT_PHASE_SNAPSHOT_OBJECT, category_group, name, \
- id, TRACE_EVENT_FLAG_NONE, "snapshot", snapshot)
+ TRACE_ID_DONT_MANGLE(id), TRACE_EVENT_FLAG_NONE, "snapshot", snapshot)
-#define TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID_AND_TIMESTAMP( \
- category_group, name, id, timestamp, snapshot) \
- INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \
- TRACE_EVENT_PHASE_SNAPSHOT_OBJECT, category_group, name, \
- id, TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE, \
- "snapshot", snapshot)
+#define TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID_AND_TIMESTAMP( \
+ category_group, name, id, timestamp, snapshot) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \
+ TRACE_EVENT_PHASE_SNAPSHOT_OBJECT, category_group, name, \
+ TRACE_ID_DONT_MANGLE(id), TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, \
+ TRACE_EVENT_FLAG_NONE, "snapshot", snapshot)
#define TRACE_EVENT_OBJECT_DELETED_WITH_ID(category_group, name, id) \
INTERNAL_TRACE_EVENT_ADD_WITH_ID( \
- TRACE_EVENT_PHASE_DELETE_OBJECT, category_group, name, id, \
- TRACE_EVENT_FLAG_NONE)
+ TRACE_EVENT_PHASE_DELETE_OBJECT, category_group, name, \
+ TRACE_ID_DONT_MANGLE(id), TRACE_EVENT_FLAG_NONE)
// Records entering and leaving trace event contexts. |category_group| and
// |name| specify the context category and type. |context| is a
// snapshotted context object id.
-#define TRACE_EVENT_ENTER_CONTEXT(category_group, name, context) \
- INTERNAL_TRACE_EVENT_ADD_WITH_ID( \
- TRACE_EVENT_PHASE_ENTER_CONTEXT, category_group, name, context, \
- TRACE_EVENT_FLAG_NONE)
-#define TRACE_EVENT_LEAVE_CONTEXT(category_group, name, context) \
- INTERNAL_TRACE_EVENT_ADD_WITH_ID( \
- TRACE_EVENT_PHASE_LEAVE_CONTEXT, category_group, name, context, \
- TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_ENTER_CONTEXT(category_group, name, context) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID( \
+ TRACE_EVENT_PHASE_ENTER_CONTEXT, category_group, name, \
+ TRACE_ID_DONT_MANGLE(context), TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_LEAVE_CONTEXT(category_group, name, context) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID( \
+ TRACE_EVENT_PHASE_LEAVE_CONTEXT, category_group, name, \
+ TRACE_ID_DONT_MANGLE(context), TRACE_EVENT_FLAG_NONE)
#define TRACE_EVENT_SCOPED_CONTEXT(category_group, name, context) \
- INTERNAL_TRACE_EVENT_SCOPED_CONTEXT(category_group, name, context)
-
-// Macro to specify that two trace IDs are identical. For example,
-// TRACE_LINK_IDS(
-// "category", "name",
-// TRACE_ID_WITH_SCOPE("net::URLRequest", 0x1000),
-// TRACE_ID_WITH_SCOPE("blink::ResourceFetcher::FetchRequest", 0x2000))
-// tells the trace consumer that events with ID ("net::URLRequest", 0x1000) from
-// the current process have the same ID as events with ID
-// ("blink::ResourceFetcher::FetchRequest", 0x2000).
-#define TRACE_LINK_IDS(category_group, name, id, linked_id) \
- INTERNAL_TRACE_EVENT_ADD_LINK_IDS(category_group, name, id, linked_id);
+ INTERNAL_TRACE_EVENT_SCOPED_CONTEXT(category_group, name, \
+ TRACE_ID_DONT_MANGLE(context))
// Macro to efficiently determine if a given category group is enabled.
#define TRACE_EVENT_CATEGORY_GROUP_ENABLED(category_group, ret) \
@@ -1037,13 +1052,11 @@
#define TRACE_EVENT_PHASE_CLOCK_SYNC ('c')
#define TRACE_EVENT_PHASE_ENTER_CONTEXT ('(')
#define TRACE_EVENT_PHASE_LEAVE_CONTEXT (')')
-#define TRACE_EVENT_PHASE_LINK_IDS ('=')
// Flags for changing the behavior of TRACE_EVENT_API_ADD_TRACE_EVENT.
#define TRACE_EVENT_FLAG_NONE (static_cast<unsigned int>(0))
#define TRACE_EVENT_FLAG_COPY (static_cast<unsigned int>(1 << 0))
#define TRACE_EVENT_FLAG_HAS_ID (static_cast<unsigned int>(1 << 1))
-// TODO(crbug.com/639003): Free this bit after ID mangling is deprecated.
#define TRACE_EVENT_FLAG_MANGLE_ID (static_cast<unsigned int>(1 << 2))
#define TRACE_EVENT_FLAG_SCOPE_OFFSET (static_cast<unsigned int>(1 << 3))
#define TRACE_EVENT_FLAG_SCOPE_EXTRA (static_cast<unsigned int>(1 << 4))
@@ -1054,8 +1067,6 @@
#define TRACE_EVENT_FLAG_FLOW_OUT (static_cast<unsigned int>(1 << 9))
#define TRACE_EVENT_FLAG_HAS_CONTEXT_ID (static_cast<unsigned int>(1 << 10))
#define TRACE_EVENT_FLAG_HAS_PROCESS_ID (static_cast<unsigned int>(1 << 11))
-#define TRACE_EVENT_FLAG_HAS_LOCAL_ID (static_cast<unsigned int>(1 << 12))
-#define TRACE_EVENT_FLAG_HAS_GLOBAL_ID (static_cast<unsigned int>(1 << 13))
#define TRACE_EVENT_FLAG_SCOPE_MASK \
(static_cast<unsigned int>(TRACE_EVENT_FLAG_SCOPE_OFFSET | \
diff --git a/base/trace_event/etw_manifest/etw_manifest.gyp b/base/trace_event/etw_manifest/etw_manifest.gyp
new file mode 100644
index 0000000000..b2f0eb8ea1
--- /dev/null
+++ b/base/trace_event/etw_manifest/etw_manifest.gyp
@@ -0,0 +1,41 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+{
+ 'targets': [
+ {
+ # GN version: //base/trace_event/etw_manifest/BUILD.gn
+ 'target_name': 'etw_manifest',
+ 'type': 'none',
+ 'toolsets': ['host', 'target'],
+ 'hard_dependency': 1,
+ 'conditions': [
+ ['OS=="win"', {
+ 'sources': [
+ 'chrome_events_win.man',
+ ],
+ 'variables': {
+ 'man_output_dir': '<(SHARED_INTERMEDIATE_DIR)/base/trace_event/etw_manifest',
+ },
+ 'rules': [{
+ # Rule to run the message compiler.
+ 'rule_name': 'message_compiler',
+ 'extension': 'man',
+ 'outputs': [
+ '<(man_output_dir)/chrome_events_win.h',
+ '<(man_output_dir)/chrome_events_win.rc',
+ ],
+ 'action': [
+ 'mc.exe',
+ '-h', '<(man_output_dir)',
+ '-r', '<(man_output_dir)/.',
+ '-um',
+ '<(RULE_INPUT_PATH)',
+ ],
+ 'message': 'Running message compiler on <(RULE_INPUT_PATH)',
+ }],
+ }],
+ ],
+ }
+ ]
+}
diff --git a/base/trace_event/event_name_filter.cc b/base/trace_event/event_name_filter.cc
deleted file mode 100644
index 8d0058c147..0000000000
--- a/base/trace_event/event_name_filter.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2016 The Chromium 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/trace_event/event_name_filter.h"
-
-#include "base/trace_event/trace_event_impl.h"
-
-namespace base {
-namespace trace_event {
-
-// static
-const char EventNameFilter::kName[] = "event_whitelist_predicate";
-
-EventNameFilter::EventNameFilter(
- std::unique_ptr<EventNamesWhitelist> event_names_whitelist)
- : event_names_whitelist_(std::move(event_names_whitelist)) {}
-
-EventNameFilter::~EventNameFilter() {}
-
-bool EventNameFilter::FilterTraceEvent(const TraceEvent& trace_event) const {
- return event_names_whitelist_->count(trace_event.name()) != 0;
-}
-
-} // namespace trace_event
-} // namespace base
diff --git a/base/trace_event/event_name_filter.h b/base/trace_event/event_name_filter.h
deleted file mode 100644
index 19333b3e03..0000000000
--- a/base/trace_event/event_name_filter.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2016 The Chromium 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_TRACE_EVENT_EVENT_NAME_FILTER_H_
-#define BASE_TRACE_EVENT_EVENT_NAME_FILTER_H_
-
-#include <memory>
-#include <string>
-#include <unordered_set>
-
-#include "base/base_export.h"
-#include "base/macros.h"
-#include "base/trace_event/trace_event_filter.h"
-
-namespace base {
-namespace trace_event {
-
-class TraceEvent;
-
-// Filters trace events by checking the full name against a whitelist.
-// The current implementation is quite simple and dumb and just uses a
-// hashtable which requires char* to std::string conversion. It could be smarter
-// and use a bloom filter trie. However, today this is used too rarely to
-// justify that cost.
-class BASE_EXPORT EventNameFilter : public TraceEventFilter {
- public:
- using EventNamesWhitelist = std::unordered_set<std::string>;
- static const char kName[];
-
- EventNameFilter(std::unique_ptr<EventNamesWhitelist>);
- ~EventNameFilter() override;
-
- // TraceEventFilter implementation.
- bool FilterTraceEvent(const TraceEvent&) const override;
-
- private:
- std::unique_ptr<const EventNamesWhitelist> event_names_whitelist_;
-
- DISALLOW_COPY_AND_ASSIGN(EventNameFilter);
-};
-
-} // namespace trace_event
-} // namespace base
-
-#endif // BASE_TRACE_EVENT_EVENT_NAME_FILTER_H_
diff --git a/base/trace_event/event_name_filter_unittest.cc b/base/trace_event/event_name_filter_unittest.cc
deleted file mode 100644
index 0bc2a4dafc..0000000000
--- a/base/trace_event/event_name_filter_unittest.cc
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2015 The Chromium 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/trace_event/event_name_filter.h"
-
-#include "base/memory/ptr_util.h"
-#include "base/trace_event/trace_event_impl.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace base {
-namespace trace_event {
-
-const TraceEvent& MakeTraceEvent(const char* name) {
- static TraceEvent event;
- event.Reset();
- event.Initialize(0, TimeTicks(), ThreadTicks(), 'b', nullptr, name, "", 0, 0,
- 0, nullptr, nullptr, nullptr, nullptr, 0);
- return event;
-}
-
-TEST(TraceEventNameFilterTest, Whitelist) {
- auto empty_whitelist = MakeUnique<EventNameFilter::EventNamesWhitelist>();
- auto filter = MakeUnique<EventNameFilter>(std::move(empty_whitelist));
-
- // No events should be filtered if the whitelist is empty.
- EXPECT_FALSE(filter->FilterTraceEvent(MakeTraceEvent("foo")));
-
- auto whitelist = MakeUnique<EventNameFilter::EventNamesWhitelist>();
- whitelist->insert("foo");
- whitelist->insert("bar");
- filter = MakeUnique<EventNameFilter>(std::move(whitelist));
- EXPECT_TRUE(filter->FilterTraceEvent(MakeTraceEvent("foo")));
- EXPECT_FALSE(filter->FilterTraceEvent(MakeTraceEvent("fooz")));
- EXPECT_FALSE(filter->FilterTraceEvent(MakeTraceEvent("afoo")));
- EXPECT_TRUE(filter->FilterTraceEvent(MakeTraceEvent("bar")));
- EXPECT_FALSE(filter->FilterTraceEvent(MakeTraceEvent("foobar")));
-}
-
-} // namespace trace_event
-} // namespace base
diff --git a/base/trace_event/heap_profiler_allocation_context_tracker.cc b/base/trace_event/heap_profiler_allocation_context_tracker.cc
index b47dc16edd..31f311a918 100644
--- a/base/trace_event/heap_profiler_allocation_context_tracker.cc
+++ b/base/trace_event/heap_profiler_allocation_context_tracker.cc
@@ -29,6 +29,7 @@ const size_t kMaxStackDepth = 128u;
const size_t kMaxTaskDepth = 16u;
AllocationContextTracker* const kInitializingSentinel =
reinterpret_cast<AllocationContextTracker*>(-1);
+const char kTracingOverhead[] = "tracing_overhead";
ThreadLocalStorage::StaticSlot g_tls_alloc_ctx_tracker = TLS_INITIALIZER;
@@ -107,17 +108,17 @@ void AllocationContextTracker::SetCaptureMode(CaptureMode mode) {
}
void AllocationContextTracker::PushPseudoStackFrame(
- AllocationContextTracker::PseudoStackFrame stack_frame) {
+ const char* trace_event_name) {
// Impose a limit on the height to verify that every push is popped, because
// in practice the pseudo stack never grows higher than ~20 frames.
if (pseudo_stack_.size() < kMaxStackDepth)
- pseudo_stack_.push_back(stack_frame);
+ pseudo_stack_.push_back(trace_event_name);
else
NOTREACHED();
}
void AllocationContextTracker::PopPseudoStackFrame(
- AllocationContextTracker::PseudoStackFrame stack_frame) {
+ const char* trace_event_name) {
// Guard for stack underflow. If tracing was started with a TRACE_EVENT in
// scope, the frame was never pushed, so it is possible that pop is called
// on an empty stack.
@@ -127,10 +128,8 @@ void AllocationContextTracker::PopPseudoStackFrame(
// Assert that pushes and pops are nested correctly. This DCHECK can be
// hit if some TRACE_EVENT macro is unbalanced (a TRACE_EVENT_END* call
// without a corresponding TRACE_EVENT_BEGIN).
- DCHECK(stack_frame == pseudo_stack_.back())
- << "Encountered an unmatched TRACE_EVENT_END: "
- << stack_frame.trace_event_name
- << " vs event in stack: " << pseudo_stack_.back().trace_event_name;
+ DCHECK_EQ(trace_event_name, pseudo_stack_.back())
+ << "Encountered an unmatched TRACE_EVENT_END";
pseudo_stack_.pop_back();
}
@@ -156,15 +155,21 @@ void AllocationContextTracker::PopCurrentTaskContext(const char* context) {
}
// static
-bool AllocationContextTracker::GetContextSnapshot(AllocationContext* ctx) {
- if (ignore_scope_depth_)
- return false;
+AllocationContext AllocationContextTracker::GetContextSnapshot() {
+ AllocationContext ctx;
+
+ if (ignore_scope_depth_) {
+ ctx.backtrace.frames[0] = StackFrame::FromTraceEventName(kTracingOverhead);
+ ctx.type_name = kTracingOverhead;
+ ctx.backtrace.frame_count = 1;
+ return ctx;
+ }
CaptureMode mode = static_cast<CaptureMode>(
subtle::NoBarrier_Load(&capture_mode_));
- auto* backtrace = std::begin(ctx->backtrace.frames);
- auto* backtrace_end = std::end(ctx->backtrace.frames);
+ auto* backtrace = std::begin(ctx.backtrace.frames);
+ auto* backtrace_end = std::end(ctx.backtrace.frames);
if (!thread_name_) {
// Ignore the string allocation made by GetAndLeakThreadName to avoid
@@ -188,12 +193,11 @@ bool AllocationContextTracker::GetContextSnapshot(AllocationContext* ctx) {
}
case CaptureMode::PSEUDO_STACK:
{
- for (const PseudoStackFrame& stack_frame : pseudo_stack_) {
+ for (const char* event_name: pseudo_stack_) {
if (backtrace == backtrace_end) {
break;
}
- *backtrace++ =
- StackFrame::FromTraceEventName(stack_frame.trace_event_name);
+ *backtrace++ = StackFrame::FromTraceEventName(event_name);
}
break;
}
@@ -218,32 +222,24 @@ bool AllocationContextTracker::GetContextSnapshot(AllocationContext* ctx) {
// Copy frames backwards
size_t backtrace_capacity = backtrace_end - backtrace;
- int32_t top_frame_index = (backtrace_capacity >= frame_count)
- ? 0
- : frame_count - backtrace_capacity;
- for (int32_t i = frame_count - 1; i >= top_frame_index; --i) {
- const void* frame = frames[i];
+ size_t top_frame_index = (backtrace_capacity >= frame_count) ?
+ 0 :
+ frame_count - backtrace_capacity;
+ for (size_t i = frame_count; i > top_frame_index;) {
+ const void* frame = frames[--i];
*backtrace++ = StackFrame::FromProgramCounter(frame);
}
break;
}
}
- ctx->backtrace.frame_count = backtrace - std::begin(ctx->backtrace.frames);
+ ctx.backtrace.frame_count = backtrace - std::begin(ctx.backtrace.frames);
// TODO(ssid): Fix crbug.com/594803 to add file name as 3rd dimension
// (component name) in the heap profiler and not piggy back on the type name.
- if (!task_contexts_.empty()) {
- ctx->type_name = task_contexts_.back();
- } else if (!pseudo_stack_.empty()) {
- // If task context was unavailable, then the category names are taken from
- // trace events.
- ctx->type_name = pseudo_stack_.back().trace_event_category;
- } else {
- ctx->type_name = nullptr;
- }
+ ctx.type_name = task_contexts_.empty() ? nullptr : task_contexts_.back();
- return true;
+ return ctx;
}
} // namespace trace_event
diff --git a/base/trace_event/heap_profiler_allocation_context_tracker.h b/base/trace_event/heap_profiler_allocation_context_tracker.h
index 4f2a8c9502..454200c474 100644
--- a/base/trace_event/heap_profiler_allocation_context_tracker.h
+++ b/base/trace_event/heap_profiler_allocation_context_tracker.h
@@ -10,6 +10,7 @@
#include "base/atomicops.h"
#include "base/base_export.h"
#include "base/debug/stack_trace.h"
+#include "base/logging.h"
#include "base/macros.h"
#include "base/trace_event/heap_profiler_allocation_context.h"
@@ -29,17 +30,6 @@ class BASE_EXPORT AllocationContextTracker {
NATIVE_STACK // GetContextSnapshot() returns native (real) stack trace
};
- // Stack frame constructed from trace events in codebase.
- struct BASE_EXPORT PseudoStackFrame {
- const char* trace_event_category;
- const char* trace_event_name;
-
- bool operator==(const PseudoStackFrame& other) const {
- return trace_event_category == other.trace_event_category &&
- trace_event_name == other.trace_event_name;
- }
- };
-
// Globally sets capturing mode.
// TODO(primiano): How to guard against *_STACK -> DISABLED -> *_STACK?
static void SetCaptureMode(CaptureMode mode);
@@ -70,8 +60,8 @@ class BASE_EXPORT AllocationContextTracker {
static void SetCurrentThreadName(const char* name);
// Starts and ends a new ignore scope between which the allocations are
- // ignored by the heap profiler. GetContextSnapshot() returns false when
- // allocations are ignored.
+ // ignored in the heap profiler. A dummy context that short circuits to
+ // "tracing_overhead" is returned for these allocations.
void begin_ignore_scope() { ignore_scope_depth_++; }
void end_ignore_scope() {
if (ignore_scope_depth_)
@@ -79,19 +69,18 @@ class BASE_EXPORT AllocationContextTracker {
}
// Pushes a frame onto the thread-local pseudo stack.
- void PushPseudoStackFrame(PseudoStackFrame stack_frame);
+ void PushPseudoStackFrame(const char* trace_event_name);
// Pops a frame from the thread-local pseudo stack.
- void PopPseudoStackFrame(PseudoStackFrame stack_frame);
+ void PopPseudoStackFrame(const char* trace_event_name);
// Push and pop current task's context. A stack is used to support nested
// tasks and the top of the stack will be used in allocation context.
void PushCurrentTaskContext(const char* context);
void PopCurrentTaskContext(const char* context);
- // Fills a snapshot of the current thread-local context. Doesn't fill and
- // returns false if allocations are being ignored.
- bool GetContextSnapshot(AllocationContext* snapshot);
+ // Returns a snapshot of the current thread-local context.
+ AllocationContext GetContextSnapshot();
~AllocationContextTracker();
@@ -101,7 +90,7 @@ class BASE_EXPORT AllocationContextTracker {
static subtle::Atomic32 capture_mode_;
// The pseudo stack where frames are |TRACE_EVENT| names.
- std::vector<PseudoStackFrame> pseudo_stack_;
+ std::vector<const char*> pseudo_stack_;
// The thread name is used as the first entry in the pseudo stack.
const char* thread_name_;
diff --git a/base/trace_event/heap_profiler_allocation_context_tracker_unittest.cc b/base/trace_event/heap_profiler_allocation_context_tracker_unittest.cc
index 577f50043d..3064a6a711 100644
--- a/base/trace_event/heap_profiler_allocation_context_tracker_unittest.cc
+++ b/base/trace_event/heap_profiler_allocation_context_tracker_unittest.cc
@@ -11,7 +11,6 @@
#include "base/trace_event/heap_profiler.h"
#include "base/trace_event/heap_profiler_allocation_context.h"
#include "base/trace_event/heap_profiler_allocation_context_tracker.h"
-#include "base/trace_event/memory_dump_manager.h"
#include "base/trace_event/trace_event.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -27,25 +26,13 @@ const char kEclair[] = "Eclair";
const char kFroyo[] = "Froyo";
const char kGingerbread[] = "Gingerbread";
-const char kFilteringTraceConfig[] =
- "{"
- " \"event_filters\": ["
- " {"
- " \"excluded_categories\": [],"
- " \"filter_args\": {},"
- " \"filter_predicate\": \"heap_profiler_predicate\","
- " \"included_categories\": [\"*\"]"
- " }"
- " ]"
- "}";
-
// Asserts that the fixed-size array |expected_backtrace| matches the backtrace
// in |AllocationContextTracker::GetContextSnapshot|.
template <size_t N>
void AssertBacktraceEquals(const StackFrame(&expected_backtrace)[N]) {
- AllocationContext ctx;
- ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread()
- ->GetContextSnapshot(&ctx));
+ AllocationContext ctx =
+ AllocationContextTracker::GetInstanceForCurrentThread()
+ ->GetContextSnapshot();
auto* actual = std::begin(ctx.backtrace.frames);
auto* actual_bottom = actual + ctx.backtrace.frame_count;
@@ -65,9 +52,9 @@ void AssertBacktraceEquals(const StackFrame(&expected_backtrace)[N]) {
void AssertBacktraceContainsOnlyThreadName() {
StackFrame t = StackFrame::FromThreadName(kThreadName);
- AllocationContext ctx;
- ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread()
- ->GetContextSnapshot(&ctx));
+ AllocationContext ctx =
+ AllocationContextTracker::GetInstanceForCurrentThread()
+ ->GetContextSnapshot();
ASSERT_EQ(1u, ctx.backtrace.frame_count);
ASSERT_EQ(t, ctx.backtrace.frames[0]);
@@ -76,19 +63,17 @@ void AssertBacktraceContainsOnlyThreadName() {
class AllocationContextTrackerTest : public testing::Test {
public:
void SetUp() override {
+ TraceConfig config("");
+ TraceLog::GetInstance()->SetEnabled(config, TraceLog::RECORDING_MODE);
AllocationContextTracker::SetCaptureMode(
AllocationContextTracker::CaptureMode::PSEUDO_STACK);
- // Enabling memory-infra category sets default memory dump config which
- // includes filters for capturing pseudo stack.
- TraceConfig config(kFilteringTraceConfig);
- TraceLog::GetInstance()->SetEnabled(config, TraceLog::FILTERING_MODE);
AllocationContextTracker::SetCurrentThreadName(kThreadName);
}
void TearDown() override {
AllocationContextTracker::SetCaptureMode(
AllocationContextTracker::CaptureMode::DISABLED);
- TraceLog::GetInstance()->SetDisabled(TraceLog::FILTERING_MODE);
+ TraceLog::GetInstance()->SetDisabled();
}
};
@@ -121,12 +106,6 @@ TEST_F(AllocationContextTrackerTest, PseudoStackScopedTrace) {
AssertBacktraceEquals(frame_ce);
}
- {
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("Testing"), kCupcake);
- StackFrame frame_cc[] = {t, c, c};
- AssertBacktraceEquals(frame_cc);
- }
-
AssertBacktraceEquals(frame_c);
}
@@ -243,9 +222,9 @@ TEST_F(AllocationContextTrackerTest, BacktraceTakesTop) {
{
TRACE_EVENT0("Testing", kGingerbread);
- AllocationContext ctx;
- ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread()
- ->GetContextSnapshot(&ctx));
+ AllocationContext ctx =
+ AllocationContextTracker::GetInstanceForCurrentThread()
+ ->GetContextSnapshot();
// The pseudo stack relies on pointer equality, not deep string comparisons.
ASSERT_EQ(t, ctx.backtrace.frames[0]);
@@ -254,54 +233,38 @@ TEST_F(AllocationContextTrackerTest, BacktraceTakesTop) {
}
{
- AllocationContext ctx;
- ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread()
- ->GetContextSnapshot(&ctx));
+ AllocationContext ctx =
+ AllocationContextTracker::GetInstanceForCurrentThread()
+ ->GetContextSnapshot();
ASSERT_EQ(t, ctx.backtrace.frames[0]);
ASSERT_EQ(c, ctx.backtrace.frames[1]);
ASSERT_EQ(f, ctx.backtrace.frames[11]);
}
}
-TEST_F(AllocationContextTrackerTest, TrackCategoryName) {
+TEST_F(AllocationContextTrackerTest, TrackTaskContext) {
const char kContext1[] = "context1";
const char kContext2[] = "context2";
{
// The context from the scoped task event should be used as type name.
TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION event1(kContext1);
- AllocationContext ctx1;
- ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread()
- ->GetContextSnapshot(&ctx1));
+ AllocationContext ctx1 =
+ AllocationContextTracker::GetInstanceForCurrentThread()
+ ->GetContextSnapshot();
ASSERT_EQ(kContext1, ctx1.type_name);
// In case of nested events, the last event's context should be used.
TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION event2(kContext2);
- AllocationContext ctx2;
- ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread()
- ->GetContextSnapshot(&ctx2));
+ AllocationContext ctx2 =
+ AllocationContextTracker::GetInstanceForCurrentThread()
+ ->GetContextSnapshot();
ASSERT_EQ(kContext2, ctx2.type_name);
}
- {
- // Type should be category name of the last seen trace event.
- TRACE_EVENT0("Testing", kCupcake);
- AllocationContext ctx1;
- ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread()
- ->GetContextSnapshot(&ctx1));
- ASSERT_EQ("Testing", std::string(ctx1.type_name));
-
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("Testing"), kCupcake);
- AllocationContext ctx2;
- ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread()
- ->GetContextSnapshot(&ctx2));
- ASSERT_EQ(TRACE_DISABLED_BY_DEFAULT("Testing"),
- std::string(ctx2.type_name));
- }
-
// Type should be nullptr without task event.
- AllocationContext ctx;
- ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread()
- ->GetContextSnapshot(&ctx));
+ AllocationContext ctx =
+ AllocationContextTracker::GetInstanceForCurrentThread()
+ ->GetContextSnapshot();
ASSERT_FALSE(ctx.type_name);
}
@@ -309,9 +272,13 @@ TEST_F(AllocationContextTrackerTest, IgnoreAllocationTest) {
TRACE_EVENT0("Testing", kCupcake);
TRACE_EVENT0("Testing", kDonut);
HEAP_PROFILER_SCOPED_IGNORE;
- AllocationContext ctx;
- ASSERT_FALSE(AllocationContextTracker::GetInstanceForCurrentThread()
- ->GetContextSnapshot(&ctx));
+ AllocationContext ctx =
+ AllocationContextTracker::GetInstanceForCurrentThread()
+ ->GetContextSnapshot();
+ const StringPiece kTracingOverhead("tracing_overhead");
+ ASSERT_EQ(kTracingOverhead,
+ static_cast<const char*>(ctx.backtrace.frames[0].value));
+ ASSERT_EQ(1u, ctx.backtrace.frame_count);
}
} // namespace trace_event
diff --git a/base/trace_event/heap_profiler_allocation_register.cc b/base/trace_event/heap_profiler_allocation_register.cc
index 63d40611a6..2c2cd378bb 100644
--- a/base/trace_event/heap_profiler_allocation_register.cc
+++ b/base/trace_event/heap_profiler_allocation_register.cc
@@ -60,12 +60,12 @@ size_t AllocationRegister::AddressHasher::operator () (
// The multiplicative hashing scheme from [Knuth 1998]. The value of |a| has
// been chosen carefully based on measurements with real-word data (addresses
// recorded from a Chrome trace run). It is the first prime after 2^17. For
- // |shift|, 15 yield good results for both 2^18 and 2^19 bucket sizes.
- // Microbenchmarks show that this simple scheme outperforms fancy hashes like
- // Murmur3 by 20 to 40 percent.
+ // |shift|, 13, 14 and 15 yield good results. These values are tuned to 2^18
+ // buckets. Microbenchmarks show that this simple scheme outperforms fancy
+ // hashes like Murmur3 by 20 to 40 percent.
const uintptr_t key = reinterpret_cast<uintptr_t>(address);
const uintptr_t a = 131101;
- const uintptr_t shift = 15;
+ const uintptr_t shift = 14;
const uintptr_t h = (key * a) >> shift;
return h;
}
diff --git a/base/trace_event/heap_profiler_allocation_register.h b/base/trace_event/heap_profiler_allocation_register.h
index d6a02faeae..86e2721c56 100644
--- a/base/trace_event/heap_profiler_allocation_register.h
+++ b/base/trace_event/heap_profiler_allocation_register.h
@@ -16,7 +16,6 @@
#include "base/process/process_metrics.h"
#include "base/template_util.h"
#include "base/trace_event/heap_profiler_allocation_context.h"
-#include "build/build_config.h"
namespace base {
namespace trace_event {
@@ -46,7 +45,8 @@ class FixedHashMap {
using KVPair = std::pair<const Key, Value>;
// For implementation simplicity API uses integer index instead
- // of iterators. Most operations (except Find) on KVIndex are O(1).
+ // of iterators. Most operations (except FindValidIndex) on KVIndex
+ // are O(1).
using KVIndex = size_t;
static const KVIndex kInvalidKVIndex = static_cast<KVIndex>(-1);
@@ -199,9 +199,7 @@ class FixedHashMap {
// the simplest solution is to just allocate a humongous chunk of address
// space.
- CHECK_LT(next_unused_cell_, num_cells_ + 1)
- << "Allocation Register hash table has too little capacity. Increase "
- "the capacity to run heap profiler in large sessions.";
+ DCHECK_LT(next_unused_cell_, num_cells_ + 1);
return &cells_[idx];
}
@@ -302,25 +300,15 @@ class BASE_EXPORT AllocationRegister {
private:
friend AllocationRegisterTest;
-// Expect lower number of allocations from mobile platforms. Load factor
-// (capacity / bucket count) is kept less than 10 for optimal hashing. The
-// number of buckets should be changed together with AddressHasher.
-#if defined(OS_ANDROID) || defined(OS_IOS)
+ // Expect max 1.5M allocations. Number of buckets is 2^18 for optimal
+ // hashing and should be changed together with AddressHasher.
static const size_t kAllocationBuckets = 1 << 18;
static const size_t kAllocationCapacity = 1500000;
-#else
- static const size_t kAllocationBuckets = 1 << 19;
- static const size_t kAllocationCapacity = 5000000;
-#endif
-
- // 2^16 works well with BacktraceHasher. When increasing this number make
- // sure BacktraceHasher still produces low number of collisions.
- static const size_t kBacktraceBuckets = 1 << 16;
-#if defined(OS_ANDROID)
- static const size_t kBacktraceCapacity = 32000; // 22K was observed
-#else
- static const size_t kBacktraceCapacity = 55000; // 45K was observed on Linux
-#endif
+
+ // Expect max 2^15 unique backtraces. Can be changed to 2^16 without
+ // needing to tweak BacktraceHasher implementation.
+ static const size_t kBacktraceBuckets = 1 << 15;
+ static const size_t kBacktraceCapacity = kBacktraceBuckets;
struct BacktraceHasher {
size_t operator () (const Backtrace& backtrace) const;
diff --git a/base/trace_event/heap_profiler_event_filter.cc b/base/trace_event/heap_profiler_event_filter.cc
deleted file mode 100644
index 6c91c91b13..0000000000
--- a/base/trace_event/heap_profiler_event_filter.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2016 The Chromium 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/trace_event/heap_profiler_event_filter.h"
-
-#include "base/trace_event/category_registry.h"
-#include "base/trace_event/heap_profiler_allocation_context_tracker.h"
-#include "base/trace_event/trace_category.h"
-#include "base/trace_event/trace_event.h"
-#include "base/trace_event/trace_event_impl.h"
-
-namespace base {
-namespace trace_event {
-
-namespace {
-
-inline bool IsPseudoStackEnabled() {
- return AllocationContextTracker::capture_mode() ==
- AllocationContextTracker::CaptureMode::PSEUDO_STACK;
-}
-
-inline AllocationContextTracker* GetThreadLocalTracker() {
- return AllocationContextTracker::GetInstanceForCurrentThread();
-}
-
-} // namespace
-
-// static
-const char HeapProfilerEventFilter::kName[] = "heap_profiler_predicate";
-
-HeapProfilerEventFilter::HeapProfilerEventFilter() {}
-HeapProfilerEventFilter::~HeapProfilerEventFilter() {}
-
-bool HeapProfilerEventFilter::FilterTraceEvent(
- const TraceEvent& trace_event) const {
- if (!IsPseudoStackEnabled())
- return true;
-
- // TODO(primiano): Add support for events with copied name crbug.com/581079.
- if (trace_event.flags() & TRACE_EVENT_FLAG_COPY)
- return true;
-
- const auto* category = CategoryRegistry::GetCategoryByStatePtr(
- trace_event.category_group_enabled());
- AllocationContextTracker::PseudoStackFrame frame = {category->name(),
- trace_event.name()};
- if (trace_event.phase() == TRACE_EVENT_PHASE_BEGIN ||
- trace_event.phase() == TRACE_EVENT_PHASE_COMPLETE) {
- GetThreadLocalTracker()->PushPseudoStackFrame(frame);
- } else if (trace_event.phase() == TRACE_EVENT_PHASE_END) {
- // The pop for |TRACE_EVENT_PHASE_COMPLETE| events is in |EndEvent|.
- GetThreadLocalTracker()->PopPseudoStackFrame(frame);
- }
- // Do not filter-out any events and always return true. TraceLog adds the
- // event only if it is enabled for recording.
- return true;
-}
-
-void HeapProfilerEventFilter::EndEvent(const char* category_name,
- const char* event_name) const {
- if (IsPseudoStackEnabled())
- GetThreadLocalTracker()->PopPseudoStackFrame({category_name, event_name});
-}
-
-} // namespace trace_event
-} // namespace base
diff --git a/base/trace_event/heap_profiler_event_filter.h b/base/trace_event/heap_profiler_event_filter.h
deleted file mode 100644
index 47368a1b07..0000000000
--- a/base/trace_event/heap_profiler_event_filter.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2016 The Chromium 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_TRACE_EVENT_HEAP_PROFILER_EVENT_FILTER_H_
-#define BASE_TRACE_EVENT_HEAP_PROFILER_EVENT_FILTER_H_
-
-#include "base/base_export.h"
-#include "base/macros.h"
-#include "base/trace_event/trace_event_filter.h"
-
-namespace base {
-namespace trace_event {
-
-class TraceEvent;
-
-// This filter unconditionally accepts all events and pushes/pops them from the
-// thread-local AllocationContextTracker instance as they are seen.
-// This is used to cheaply construct the heap profiler pseudo stack without
-// having to actually record all events.
-class BASE_EXPORT HeapProfilerEventFilter : public TraceEventFilter {
- public:
- static const char kName[];
-
- HeapProfilerEventFilter();
- ~HeapProfilerEventFilter() override;
-
- // TraceEventFilter implementation.
- bool FilterTraceEvent(const TraceEvent& trace_event) const override;
- void EndEvent(const char* category_name,
- const char* event_name) const override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(HeapProfilerEventFilter);
-};
-
-} // namespace trace_event
-} // namespace base
-
-#endif // BASE_TRACE_EVENT_HEAP_PROFILER_EVENT_FILTER_H_
diff --git a/base/trace_event/heap_profiler_heap_dump_writer.cc b/base/trace_event/heap_profiler_heap_dump_writer.cc
index 8043fff995..1bf06dbd97 100644
--- a/base/trace_event/heap_profiler_heap_dump_writer.cc
+++ b/base/trace_event/heap_profiler_heap_dump_writer.cc
@@ -314,7 +314,8 @@ std::unique_ptr<TracedValue> ExportHeapDump(
internal::HeapDumpWriter writer(
session_state.stack_frame_deduplicator(),
session_state.type_name_deduplicator(),
- session_state.heap_profiler_breakdown_threshold_bytes());
+ session_state.memory_dump_config().heap_profiler_options
+ .breakdown_threshold_bytes);
return Serialize(writer.Summarize(metrics_by_context));
}
diff --git a/base/trace_event/heap_profiler_stack_frame_deduplicator.cc b/base/trace_event/heap_profiler_stack_frame_deduplicator.cc
index fc5da0d1dd..49a235051c 100644
--- a/base/trace_event/heap_profiler_stack_frame_deduplicator.cc
+++ b/base/trace_event/heap_profiler_stack_frame_deduplicator.cc
@@ -11,7 +11,6 @@
#include <utility>
#include "base/strings/stringprintf.h"
-#include "base/trace_event/memory_usage_estimator.h"
#include "base/trace_event/trace_event_argument.h"
#include "base/trace_event/trace_event_memory_overhead.h"
@@ -24,10 +23,6 @@ StackFrameDeduplicator::FrameNode::FrameNode(StackFrame frame,
StackFrameDeduplicator::FrameNode::FrameNode(const FrameNode& other) = default;
StackFrameDeduplicator::FrameNode::~FrameNode() {}
-size_t StackFrameDeduplicator::FrameNode::EstimateMemoryUsage() const {
- return base::trace_event::EstimateMemoryUsage(children);
-}
-
StackFrameDeduplicator::StackFrameDeduplicator() {}
StackFrameDeduplicator::~StackFrameDeduplicator() {}
@@ -121,10 +116,19 @@ void StackFrameDeduplicator::AppendAsTraceFormat(std::string* out) const {
void StackFrameDeduplicator::EstimateTraceMemoryOverhead(
TraceEventMemoryOverhead* overhead) {
- size_t memory_usage =
- EstimateMemoryUsage(frames_) + EstimateMemoryUsage(roots_);
+ // The sizes here are only estimates; they fail to take into account the
+ // overhead of the tree nodes for the map, but as an estimate this should be
+ // fine.
+ size_t maps_size = roots_.size() * sizeof(std::pair<StackFrame, int>);
+ size_t frames_allocated = frames_.capacity() * sizeof(FrameNode);
+ size_t frames_resident = frames_.size() * sizeof(FrameNode);
+
+ for (const FrameNode& node : frames_)
+ maps_size += node.children.size() * sizeof(std::pair<StackFrame, int>);
+
overhead->Add("StackFrameDeduplicator",
- sizeof(StackFrameDeduplicator) + memory_usage);
+ sizeof(StackFrameDeduplicator) + maps_size + frames_allocated,
+ sizeof(StackFrameDeduplicator) + maps_size + frames_resident);
}
} // namespace trace_event
diff --git a/base/trace_event/heap_profiler_stack_frame_deduplicator.h b/base/trace_event/heap_profiler_stack_frame_deduplicator.h
index 66d430f2ee..4932534e1d 100644
--- a/base/trace_event/heap_profiler_stack_frame_deduplicator.h
+++ b/base/trace_event/heap_profiler_stack_frame_deduplicator.h
@@ -34,8 +34,6 @@ class BASE_EXPORT StackFrameDeduplicator : public ConvertableToTraceFormat {
FrameNode(const FrameNode& other);
~FrameNode();
- size_t EstimateMemoryUsage() const;
-
StackFrame frame;
// The index of the parent stack frame in |frames_|, or -1 if there is no
diff --git a/base/trace_event/heap_profiler_type_name_deduplicator.cc b/base/trace_event/heap_profiler_type_name_deduplicator.cc
index a6dab51ad2..055f86abf0 100644
--- a/base/trace_event/heap_profiler_type_name_deduplicator.cc
+++ b/base/trace_event/heap_profiler_type_name_deduplicator.cc
@@ -10,10 +10,7 @@
#include <utility>
#include "base/json/string_escape.h"
-#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h"
-#include "base/trace_event/memory_usage_estimator.h"
-#include "base/trace_event/trace_event.h"
#include "base/trace_event/trace_event_memory_overhead.h"
namespace base {
@@ -21,24 +18,16 @@ namespace trace_event {
namespace {
-// If |type_name| is file name then extract directory name. Or if |type_name| is
-// category name, then disambiguate multple categories and remove
-// "disabled-by-default" prefix if present.
-StringPiece ExtractCategoryFromTypeName(const char* type_name) {
+// Extract directory name if |type_name| was file name. Otherwise, return
+// |type_name|.
+StringPiece ExtractDirNameFromFileName(const char* type_name) {
StringPiece result(type_name);
size_t last_seperator = result.find_last_of("\\/");
// If |type_name| was a not a file path, the seperator will not be found, so
// the whole type name is returned.
- if (last_seperator == StringPiece::npos) {
- // Use the first the category name if it has ",".
- size_t first_comma_position = result.find(',');
- if (first_comma_position != StringPiece::npos)
- result = result.substr(0, first_comma_position);
- if (result.starts_with(TRACE_DISABLED_BY_DEFAULT("")))
- result.remove_prefix(sizeof(TRACE_DISABLED_BY_DEFAULT("")) - 1);
+ if (last_seperator == StringPiece::npos)
return result;
- }
// Remove the file name from the path.
result.remove_suffix(result.length() - last_seperator);
@@ -93,7 +82,7 @@ void TypeNameDeduplicator::AppendAsTraceFormat(std::string* out) const {
// TODO(ssid): crbug.com/594803 the type name is misused for file name in
// some cases.
- StringPiece type_info = ExtractCategoryFromTypeName(it->first);
+ StringPiece type_info = ExtractDirNameFromFileName(it->first);
// |EscapeJSONString| appends, it does not overwrite |buffer|.
bool put_in_quotes = true;
@@ -106,9 +95,12 @@ void TypeNameDeduplicator::AppendAsTraceFormat(std::string* out) const {
void TypeNameDeduplicator::EstimateTraceMemoryOverhead(
TraceEventMemoryOverhead* overhead) {
- size_t memory_usage = EstimateMemoryUsage(type_ids_);
+ // The size here is only an estimate; it fails to take into account the size
+ // of the tree nodes for the map, but as an estimate this should be fine.
+ size_t map_size = type_ids_.size() * sizeof(std::pair<const char*, int>);
+
overhead->Add("TypeNameDeduplicator",
- sizeof(TypeNameDeduplicator) + memory_usage);
+ sizeof(TypeNameDeduplicator) + map_size);
}
} // namespace trace_event
diff --git a/base/trace_event/malloc_dump_provider.cc b/base/trace_event/malloc_dump_provider.cc
index 3565b8b95b..c3d3258651 100644
--- a/base/trace_event/malloc_dump_provider.cc
+++ b/base/trace_event/malloc_dump_provider.cc
@@ -9,7 +9,6 @@
#include "base/allocator/allocator_extension.h"
#include "base/allocator/allocator_shim.h"
#include "base/allocator/features.h"
-#include "base/debug/profiler.h"
#include "base/trace_event/heap_profiler_allocation_context.h"
#include "base/trace_event/heap_profiler_allocation_context_tracker.h"
#include "base/trace_event/heap_profiler_allocation_register.h"
@@ -23,32 +22,26 @@
#else
#include <malloc.h>
#endif
-#if defined(OS_WIN)
-#include <windows.h>
-#endif
namespace base {
namespace trace_event {
-namespace {
#if BUILDFLAG(USE_EXPERIMENTAL_ALLOCATOR_SHIM)
+namespace {
using allocator::AllocatorDispatch;
-void* HookAlloc(const AllocatorDispatch* self, size_t size, void* context) {
+void* HookAlloc(const AllocatorDispatch* self, size_t size) {
const AllocatorDispatch* const next = self->next;
- void* ptr = next->alloc_function(next, size, context);
+ void* ptr = next->alloc_function(next, size);
if (ptr)
MallocDumpProvider::GetInstance()->InsertAllocation(ptr, size);
return ptr;
}
-void* HookZeroInitAlloc(const AllocatorDispatch* self,
- size_t n,
- size_t size,
- void* context) {
+void* HookZeroInitAlloc(const AllocatorDispatch* self, size_t n, size_t size) {
const AllocatorDispatch* const next = self->next;
- void* ptr = next->alloc_zero_initialized_function(next, n, size, context);
+ void* ptr = next->alloc_zero_initialized_function(next, n, size);
if (ptr)
MallocDumpProvider::GetInstance()->InsertAllocation(ptr, n * size);
return ptr;
@@ -56,127 +49,41 @@ void* HookZeroInitAlloc(const AllocatorDispatch* self,
void* HookllocAligned(const AllocatorDispatch* self,
size_t alignment,
- size_t size,
- void* context) {
+ size_t size) {
const AllocatorDispatch* const next = self->next;
- void* ptr = next->alloc_aligned_function(next, alignment, size, context);
+ void* ptr = next->alloc_aligned_function(next, alignment, size);
if (ptr)
MallocDumpProvider::GetInstance()->InsertAllocation(ptr, size);
return ptr;
}
-void* HookRealloc(const AllocatorDispatch* self,
- void* address,
- size_t size,
- void* context) {
+void* HookRealloc(const AllocatorDispatch* self, void* address, size_t size) {
const AllocatorDispatch* const next = self->next;
- void* ptr = next->realloc_function(next, address, size, context);
+ void* ptr = next->realloc_function(next, address, size);
MallocDumpProvider::GetInstance()->RemoveAllocation(address);
if (size > 0) // realloc(size == 0) means free().
MallocDumpProvider::GetInstance()->InsertAllocation(ptr, size);
return ptr;
}
-void HookFree(const AllocatorDispatch* self, void* address, void* context) {
+void HookFree(const AllocatorDispatch* self, void* address) {
if (address)
MallocDumpProvider::GetInstance()->RemoveAllocation(address);
const AllocatorDispatch* const next = self->next;
- next->free_function(next, address, context);
-}
-
-size_t HookGetSizeEstimate(const AllocatorDispatch* self,
- void* address,
- void* context) {
- const AllocatorDispatch* const next = self->next;
- return next->get_size_estimate_function(next, address, context);
-}
-
-unsigned HookBatchMalloc(const AllocatorDispatch* self,
- size_t size,
- void** results,
- unsigned num_requested,
- void* context) {
- const AllocatorDispatch* const next = self->next;
- unsigned count =
- next->batch_malloc_function(next, size, results, num_requested, context);
- for (unsigned i = 0; i < count; ++i) {
- MallocDumpProvider::GetInstance()->InsertAllocation(results[i], size);
- }
- return count;
-}
-
-void HookBatchFree(const AllocatorDispatch* self,
- void** to_be_freed,
- unsigned num_to_be_freed,
- void* context) {
- const AllocatorDispatch* const next = self->next;
- for (unsigned i = 0; i < num_to_be_freed; ++i) {
- MallocDumpProvider::GetInstance()->RemoveAllocation(to_be_freed[i]);
- }
- next->batch_free_function(next, to_be_freed, num_to_be_freed, context);
-}
-
-void HookFreeDefiniteSize(const AllocatorDispatch* self,
- void* ptr,
- size_t size,
- void* context) {
- if (ptr)
- MallocDumpProvider::GetInstance()->RemoveAllocation(ptr);
- const AllocatorDispatch* const next = self->next;
- next->free_definite_size_function(next, ptr, size, context);
+ next->free_function(next, address);
}
AllocatorDispatch g_allocator_hooks = {
- &HookAlloc, /* alloc_function */
- &HookZeroInitAlloc, /* alloc_zero_initialized_function */
- &HookllocAligned, /* alloc_aligned_function */
- &HookRealloc, /* realloc_function */
- &HookFree, /* free_function */
- &HookGetSizeEstimate, /* get_size_estimate_function */
- &HookBatchMalloc, /* batch_malloc_function */
- &HookBatchFree, /* batch_free_function */
- &HookFreeDefiniteSize, /* free_definite_size_function */
- nullptr, /* next */
+ &HookAlloc, /* alloc_function */
+ &HookZeroInitAlloc, /* alloc_zero_initialized_function */
+ &HookllocAligned, /* alloc_aligned_function */
+ &HookRealloc, /* realloc_function */
+ &HookFree, /* free_function */
+ nullptr, /* next */
};
-#endif // BUILDFLAG(USE_EXPERIMENTAL_ALLOCATOR_SHIM)
-#if defined(OS_WIN)
-// A structure containing some information about a given heap.
-struct WinHeapInfo {
- size_t committed_size;
- size_t uncommitted_size;
- size_t allocated_size;
- size_t block_count;
-};
-
-// NOTE: crbug.com/665516
-// Unfortunately, there is no safe way to collect information from secondary
-// heaps due to limitations and racy nature of this piece of WinAPI.
-void WinHeapMemoryDumpImpl(WinHeapInfo* crt_heap_info) {
-#if defined(SYZYASAN)
- if (base::debug::IsBinaryInstrumented())
- return;
-#endif
-
- // Iterate through whichever heap our CRT is using.
- HANDLE crt_heap = reinterpret_cast<HANDLE>(_get_heap_handle());
- ::HeapLock(crt_heap);
- PROCESS_HEAP_ENTRY heap_entry;
- heap_entry.lpData = nullptr;
- // Walk over all the entries in the main heap.
- while (::HeapWalk(crt_heap, &heap_entry) != FALSE) {
- if ((heap_entry.wFlags & PROCESS_HEAP_ENTRY_BUSY) != 0) {
- crt_heap_info->allocated_size += heap_entry.cbData;
- crt_heap_info->block_count++;
- } else if ((heap_entry.wFlags & PROCESS_HEAP_REGION) != 0) {
- crt_heap_info->committed_size += heap_entry.Region.dwCommittedSize;
- crt_heap_info->uncommitted_size += heap_entry.Region.dwUnCommittedSize;
- }
- }
- CHECK(::HeapUnlock(crt_heap) == TRUE);
-}
-#endif // defined(OS_WIN)
} // namespace
+#endif // BUILDFLAG(USE_EXPERIMENTAL_ALLOCATOR_SHIM)
// static
const char MallocDumpProvider::kAllocatedObjects[] = "malloc/allocated_objects";
@@ -199,7 +106,6 @@ bool MallocDumpProvider::OnMemoryDump(const MemoryDumpArgs& args,
size_t total_virtual_size = 0;
size_t resident_size = 0;
size_t allocated_objects_size = 0;
- size_t allocated_objects_count = 0;
#if defined(USE_TCMALLOC)
bool res =
allocator::GetNumericProperty("generic.heap_size", &total_virtual_size);
@@ -211,35 +117,18 @@ bool MallocDumpProvider::OnMemoryDump(const MemoryDumpArgs& args,
&allocated_objects_size);
DCHECK(res);
#elif defined(OS_MACOSX) || defined(OS_IOS)
- malloc_statistics_t stats = {0};
+ malloc_statistics_t stats;
+ memset(&stats, 0, sizeof(stats));
malloc_zone_statistics(nullptr, &stats);
total_virtual_size = stats.size_allocated;
allocated_objects_size = stats.size_in_use;
- // Resident size is approximated pretty well by stats.max_size_in_use.
- // However, on macOS, freed blocks are both resident and reusable, which is
- // semantically equivalent to deallocated. The implementation of libmalloc
- // will also only hold a fixed number of freed regions before actually
- // starting to deallocate them, so stats.max_size_in_use is also not
- // representative of the peak size. As a result, stats.max_size_in_use is
- // typically somewhere between actually resident [non-reusable] pages, and
- // peak size. This is not very useful, so we just use stats.size_in_use for
- // resident_size, even though it's an underestimate and fails to account for
- // fragmentation. See
- // https://bugs.chromium.org/p/chromium/issues/detail?id=695263#c1.
- resident_size = stats.size_in_use;
-#elif defined(OS_WIN)
- WinHeapInfo main_heap_info = {};
- WinHeapMemoryDumpImpl(&main_heap_info);
- total_virtual_size =
- main_heap_info.committed_size + main_heap_info.uncommitted_size;
- // Resident size is approximated with committed heap size. Note that it is
- // possible to do this with better accuracy on windows by intersecting the
- // working set with the virtual memory ranges occuipied by the heap. It's not
- // clear that this is worth it, as it's fairly expensive to do.
- resident_size = main_heap_info.committed_size;
- allocated_objects_size = main_heap_info.allocated_size;
- allocated_objects_count = main_heap_info.block_count;
+ // The resident size is approximated to the max size in use, which would count
+ // the total size of all regions other than the free bytes at the end of each
+ // region. In each allocation region the allocations are rounded off to a
+ // fixed quantum, so the excess region will not be resident.
+ // See crrev.com/1531463004 for detailed explanation.
+ resident_size = stats.max_size_in_use;
#else
struct mallinfo info = mallinfo();
DCHECK_GE(info.arena + info.hblkhd, info.uordblks);
@@ -249,8 +138,6 @@ bool MallocDumpProvider::OnMemoryDump(const MemoryDumpArgs& args,
// |arena| + |hblkhd|. For more details see link: http://goo.gl/fMR8lF.
total_virtual_size = info.arena + info.hblkhd;
resident_size = info.uordblks;
-
- // Total allocated space is given by |uordblks|.
allocated_objects_size = info.uordblks;
#endif
@@ -260,17 +147,13 @@ bool MallocDumpProvider::OnMemoryDump(const MemoryDumpArgs& args,
outer_dump->AddScalar(MemoryAllocatorDump::kNameSize,
MemoryAllocatorDump::kUnitsBytes, resident_size);
+ // Total allocated space is given by |uordblks|.
MemoryAllocatorDump* inner_dump = pmd->CreateAllocatorDump(kAllocatedObjects);
inner_dump->AddScalar(MemoryAllocatorDump::kNameSize,
MemoryAllocatorDump::kUnitsBytes,
allocated_objects_size);
- if (allocated_objects_count != 0) {
- inner_dump->AddScalar(MemoryAllocatorDump::kNameObjectCount,
- MemoryAllocatorDump::kUnitsObjects,
- allocated_objects_count);
- }
- if (resident_size > allocated_objects_size) {
+ if (resident_size - allocated_objects_size > 0) {
// Explicitly specify why is extra memory resident. In tcmalloc it accounts
// for free lists and caches. In mac and ios it accounts for the
// fragmentation and metadata.
@@ -350,10 +233,7 @@ void MallocDumpProvider::InsertAllocation(void* address, size_t size) {
auto* tracker = AllocationContextTracker::GetInstanceForCurrentThread();
if (!tracker)
return;
-
- AllocationContext context;
- if (!tracker->GetContextSnapshot(&context))
- return;
+ AllocationContext context = tracker->GetContextSnapshot();
AutoLock lock(allocation_register_lock_);
if (!allocation_register_)
diff --git a/base/trace_event/malloc_dump_provider.h b/base/trace_event/malloc_dump_provider.h
index 384033c9b8..4746cf5896 100644
--- a/base/trace_event/malloc_dump_provider.h
+++ b/base/trace_event/malloc_dump_provider.h
@@ -15,7 +15,7 @@
#include "base/trace_event/memory_dump_provider.h"
#include "build/build_config.h"
-#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_WIN) || \
+#if defined(OS_LINUX) || defined(OS_ANDROID) || \
(defined(OS_MACOSX) && !defined(OS_IOS))
#define MALLOC_MEMORY_TRACING_SUPPORTED
#endif
diff --git a/base/trace_event/memory_allocator_dump.h b/base/trace_event/memory_allocator_dump.h
index c781f071bb..7d1023606b 100644
--- a/base/trace_event/memory_allocator_dump.h
+++ b/base/trace_event/memory_allocator_dump.h
@@ -19,6 +19,7 @@
namespace base {
namespace trace_event {
+class MemoryDumpManager;
class ProcessMemoryDump;
class TracedValue;
@@ -69,6 +70,11 @@ class BASE_EXPORT MemoryAllocatorDump {
// Called at trace generation time to populate the TracedValue.
void AsValueInto(TracedValue* value) const;
+ // Get the ProcessMemoryDump instance that owns this.
+ ProcessMemoryDump* process_memory_dump() const {
+ return process_memory_dump_;
+ }
+
// Use enum Flags to set values.
void set_flags(int flags) { flags_ |= flags; }
void clear_flags(int flags) { flags_ &= ~flags; }
diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc
index 5a54a773c5..eed070a782 100644
--- a/base/trace_event/memory_dump_manager.cc
+++ b/base/trace_event/memory_dump_manager.cc
@@ -7,26 +7,21 @@
#include <algorithm>
#include <utility>
-#include "base/allocator/features.h"
#include "base/atomic_sequence_num.h"
#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
-#include "base/debug/alias.h"
#include "base/debug/debugging_flags.h"
#include "base/debug/stack_trace.h"
-#include "base/debug/thread_heap_usage_tracker.h"
#include "base/memory/ptr_util.h"
#include "base/threading/thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/heap_profiler.h"
#include "base/trace_event/heap_profiler_allocation_context_tracker.h"
-#include "base/trace_event/heap_profiler_event_filter.h"
#include "base/trace_event/heap_profiler_stack_frame_deduplicator.h"
#include "base/trace_event/heap_profiler_type_name_deduplicator.h"
#include "base/trace_event/malloc_dump_provider.h"
#include "base/trace_event/memory_dump_provider.h"
-#include "base/trace_event/memory_dump_scheduler.h"
#include "base/trace_event/memory_dump_session_state.h"
#include "base/trace_event/memory_infra_background_whitelist.h"
#include "base/trace_event/process_memory_dump.h"
@@ -38,6 +33,10 @@
#include "base/trace_event/java_heap_dump_provider_android.h"
#endif
+#if defined(OS_WIN)
+#include "base/trace_event/winheap_dump_provider_win.h"
+#endif
+
namespace base {
namespace trace_event {
@@ -50,31 +49,6 @@ const unsigned char kTraceEventArgTypes[] = {TRACE_VALUE_TYPE_CONVERTABLE};
StaticAtomicSequenceNumber g_next_guid;
MemoryDumpManager* g_instance_for_testing = nullptr;
-// The list of names of dump providers that are blacklisted from strict thread
-// affinity check on unregistration. These providers could potentially cause
-// crashes on build bots if they do not unregister on right thread.
-// TODO(ssid): Fix all the dump providers to unregister if needed and clear the
-// blacklist, crbug.com/643438.
-const char* const kStrictThreadCheckBlacklist[] = {
- "ClientDiscardableSharedMemoryManager",
- "ContextProviderCommandBuffer",
- "DiscardableSharedMemoryManager",
- "FontCaches",
- "GpuMemoryBufferVideoFramePool",
- "IndexedDBBackingStore",
- "Sql",
- "ThreadLocalEventBuffer",
- "TraceLog",
- "URLRequestContext",
- "VpxVideoDecoder",
- "cc::SoftwareImageDecodeCache",
- "cc::StagingBufferPool",
- "gpu::BufferManager",
- "gpu::MappedMemoryManager",
- "gpu::RenderbufferManager",
- "BlacklistTestDumpProvider" // for testing
-};
-
// Callback wrapper to hook upon the completion of RequestGlobalDump() and
// inject trace markers.
void OnGlobalDumpDone(MemoryDumpCallback wrapped_callback,
@@ -136,6 +110,8 @@ const uint64_t MemoryDumpManager::kInvalidTracingProcessId = 0;
const char* const MemoryDumpManager::kSystemAllocatorPoolName =
#if defined(MALLOC_MEMORY_TRACING_SUPPORTED)
MallocDumpProvider::kAllocatedObjects;
+#elif defined(OS_WIN)
+ WinHeapDumpProvider::kAllocatedObjects;
#else
nullptr;
#endif
@@ -166,9 +142,6 @@ MemoryDumpManager::MemoryDumpManager()
// At this point the command line may not be initialized but we try to
// enable the heap profiler to capture allocations as soon as possible.
EnableHeapProfilingIfNeeded();
-
- strict_thread_check_blacklist_.insert(std::begin(kStrictThreadCheckBlacklist),
- std::end(kStrictThreadCheckBlacklist));
}
MemoryDumpManager::~MemoryDumpManager() {
@@ -189,20 +162,18 @@ void MemoryDumpManager::EnableHeapProfilingIfNeeded() {
if (profiling_mode == "") {
AllocationContextTracker::SetCaptureMode(
AllocationContextTracker::CaptureMode::PSEUDO_STACK);
+ }
+ else if (profiling_mode == switches::kEnableHeapProfilingModeNative) {
#if HAVE_TRACE_STACK_FRAME_POINTERS && \
(BUILDFLAG(ENABLE_PROFILING) || !defined(NDEBUG))
- } else if (profiling_mode == switches::kEnableHeapProfilingModeNative) {
// We need frame pointers for native tracing to work, and they are
// enabled in profiling and debug builds.
AllocationContextTracker::SetCaptureMode(
AllocationContextTracker::CaptureMode::NATIVE_STACK);
-#endif
-#if BUILDFLAG(ENABLE_MEMORY_TASK_PROFILER)
- } else if (profiling_mode == switches::kEnableHeapProfilingTaskProfiler) {
- // Enable heap tracking, which in turn enables capture of heap usage
- // tracking in tracked_objects.cc.
- if (!base::debug::ThreadHeapUsageTracker::IsHeapTrackingEnabled())
- base::debug::ThreadHeapUsageTracker::EnableHeapTracking();
+#else
+ CHECK(false) << "'" << profiling_mode << "' mode for "
+ << switches::kEnableHeapProfiling << " flag is not supported "
+ << "for this platform / build type.";
#endif
} else {
CHECK(false) << "Invalid mode '" << profiling_mode << "' for "
@@ -235,33 +206,14 @@ void MemoryDumpManager::Initialize(MemoryDumpManagerDelegate* delegate,
nullptr);
#endif
- TRACE_EVENT_WARMUP_CATEGORY(kTraceCategory);
-
- // TODO(ssid): This should be done in EnableHeapProfiling so that we capture
- // more allocations (crbug.com/625170).
- if (AllocationContextTracker::capture_mode() ==
- AllocationContextTracker::CaptureMode::PSEUDO_STACK &&
- !(TraceLog::GetInstance()->enabled_modes() & TraceLog::FILTERING_MODE)) {
- // Create trace config with heap profiling filter.
- TraceConfig::EventFilterConfig heap_profiler_filter_config(
- HeapProfilerEventFilter::kName);
- heap_profiler_filter_config.AddIncludedCategory("*");
- heap_profiler_filter_config.AddIncludedCategory(
- MemoryDumpManager::kTraceCategory);
- TraceConfig::EventFilters filters;
- filters.push_back(heap_profiler_filter_config);
- TraceConfig filtering_trace_config;
- filtering_trace_config.SetEventFilters(filters);
-
- TraceLog::GetInstance()->SetEnabled(filtering_trace_config,
- TraceLog::FILTERING_MODE);
- }
+#if defined(OS_WIN)
+ RegisterDumpProvider(WinHeapDumpProvider::GetInstance(), "WinHeap", nullptr);
+#endif
// If tracing was enabled before initializing MemoryDumpManager, we missed the
// OnTraceLogEnabled() event. Synthetize it so we can late-join the party.
- // IsEnabled is called before adding observer to avoid calling
- // OnTraceLogEnabled twice.
bool is_tracing_already_enabled = TraceLog::GetInstance()->IsEnabled();
+ TRACE_EVENT0(kTraceCategory, "init"); // Add to trace-viewer category list.
TraceLog::GetInstance()->AddEnabledStateObserver(this);
if (is_tracing_already_enabled)
OnTraceLogEnabled();
@@ -310,11 +262,6 @@ void MemoryDumpManager::RegisterDumpProviderInternal(
new MemoryDumpProviderInfo(mdp, name, std::move(task_runner), options,
whitelisted_for_background_mode);
- if (options.is_fast_polling_supported) {
- DCHECK(!mdpinfo->task_runner) << "MemoryDumpProviders capable of fast "
- "polling must NOT be thread bound.";
- }
-
{
AutoLock lock(lock_);
bool already_registered = !dump_providers_.insert(mdpinfo).second;
@@ -322,15 +269,6 @@ void MemoryDumpManager::RegisterDumpProviderInternal(
// path for RenderThreadImpl::Init().
if (already_registered)
return;
-
- // The list of polling MDPs is populated OnTraceLogEnabled(). This code
- // deals with the case of a MDP capable of fast polling that is registered
- // after the OnTraceLogEnabled()
- if (options.is_fast_polling_supported && dump_thread_) {
- dump_thread_->task_runner()->PostTask(
- FROM_HERE, Bind(&MemoryDumpManager::RegisterPollingMDPOnDumpThread,
- Unretained(this), mdpinfo));
- }
}
if (heap_profiling_enabled_)
@@ -369,18 +307,9 @@ void MemoryDumpManager::UnregisterDumpProviderInternal(
// - At the end of this function, if no dump is in progress.
// - Either in SetupNextMemoryDump() or InvokeOnMemoryDump() when MDPInfo is
// removed from |pending_dump_providers|.
- // - When the provider is removed from |dump_providers_for_polling_|.
DCHECK(!(*mdp_iter)->owned_dump_provider);
(*mdp_iter)->owned_dump_provider = std::move(owned_mdp);
- } else if (strict_thread_check_blacklist_.count((*mdp_iter)->name) == 0 ||
- subtle::NoBarrier_Load(&memory_tracing_enabled_)) {
- // If dump provider's name is on |strict_thread_check_blacklist_|, then the
- // DCHECK is fired only when tracing is enabled. Otherwise the DCHECK is
- // fired even when tracing is not enabled (stricter).
- // TODO(ssid): Remove this condition after removing all the dump providers
- // in the blacklist and the buildbots are no longer flakily hitting the
- // DCHECK, crbug.com/643438.
-
+ } else if (subtle::NoBarrier_Load(&memory_tracing_enabled_)) {
// If you hit this DCHECK, your dump provider has a bug.
// Unregistration of a MemoryDumpProvider is safe only if:
// - The MDP has specified a sequenced task runner affinity AND the
@@ -396,13 +325,6 @@ void MemoryDumpManager::UnregisterDumpProviderInternal(
<< "unregister itself in a racy way. Please file a crbug.";
}
- if ((*mdp_iter)->options.is_fast_polling_supported && dump_thread_) {
- DCHECK(take_mdp_ownership_and_delete_async);
- dump_thread_->task_runner()->PostTask(
- FROM_HERE, Bind(&MemoryDumpManager::UnregisterPollingMDPOnDumpThread,
- Unretained(this), *mdp_iter));
- }
-
// The MDPInfo instance can still be referenced by the
// |ProcessMemoryDumpAsyncState.pending_dump_providers|. For this reason
// the MDPInfo is flagged as disabled. It will cause InvokeOnMemoryDump()
@@ -412,28 +334,6 @@ void MemoryDumpManager::UnregisterDumpProviderInternal(
dump_providers_.erase(mdp_iter);
}
-void MemoryDumpManager::RegisterPollingMDPOnDumpThread(
- scoped_refptr<MemoryDumpManager::MemoryDumpProviderInfo> mdpinfo) {
- AutoLock lock(lock_);
- dump_providers_for_polling_.insert(mdpinfo);
-
- // Notify ready for polling when first polling supported provider is
- // registered. This handles the case where OnTraceLogEnabled() did not notify
- // ready since no polling supported mdp has yet been registered.
- if (dump_providers_for_polling_.size() == 1)
- dump_scheduler_->NotifyPollingSupported();
-}
-
-void MemoryDumpManager::UnregisterPollingMDPOnDumpThread(
- scoped_refptr<MemoryDumpManager::MemoryDumpProviderInfo> mdpinfo) {
- mdpinfo->dump_provider->SuspendFastMemoryPolling();
-
- AutoLock lock(lock_);
- dump_providers_for_polling_.erase(mdpinfo);
- DCHECK(!dump_providers_for_polling_.empty())
- << "All polling MDPs cannot be unregistered.";
-}
-
void MemoryDumpManager::RequestGlobalDump(
MemoryDumpType dump_type,
MemoryDumpLevelOfDetail level_of_detail,
@@ -513,10 +413,8 @@ void MemoryDumpManager::CreateProcessDump(const MemoryDumpRequestArgs& args,
// with disallowed modes. If |session_state_| is null then tracing is
// disabled.
CHECK(!session_state_ ||
- session_state_->IsDumpModeAllowed(args.level_of_detail));
-
- if (dump_scheduler_)
- dump_scheduler_->NotifyDumpTriggered();
+ session_state_->memory_dump_config().allowed_dump_modes.count(
+ args.level_of_detail));
}
TRACE_EVENT_WITH_FLOW0(kTraceCategory, "MemoryDumpManager::CreateProcessDump",
@@ -672,16 +570,6 @@ void MemoryDumpManager::InvokeOnMemoryDump(
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
"dump_provider.name", mdpinfo->name);
- // A stack allocated string with dump provider name is useful to debug
- // crashes while invoking dump after a |dump_provider| is not unregistered
- // in safe way.
- // TODO(ssid): Remove this after fixing crbug.com/643438.
- char provider_name_for_debugging[16];
- strncpy(provider_name_for_debugging, mdpinfo->name,
- sizeof(provider_name_for_debugging) - 1);
- provider_name_for_debugging[sizeof(provider_name_for_debugging) - 1] = '\0';
- base::debug::Alias(provider_name_for_debugging);
-
// Pid of the target process being dumped. Often kNullProcessId (= current
// process), non-zero when the coordinator process creates dumps on behalf
// of child processes (see crbug.com/461788).
@@ -699,28 +587,6 @@ void MemoryDumpManager::InvokeOnMemoryDump(
SetupNextMemoryDump(std::move(pmd_async_state));
}
-bool MemoryDumpManager::PollFastMemoryTotal(uint64_t* memory_total) {
-#if DCHECK_IS_ON()
- {
- AutoLock lock(lock_);
- if (dump_thread_)
- DCHECK(dump_thread_->task_runner()->BelongsToCurrentThread());
- }
-#endif
- if (dump_providers_for_polling_.empty())
- return false;
-
- *memory_total = 0;
- // Note that we call PollFastMemoryTotal() even if the dump provider is
- // disabled (unregistered). This is to avoid taking lock while polling.
- for (const auto& mdpinfo : dump_providers_for_polling_) {
- uint64_t value = 0;
- mdpinfo->dump_provider->PollFastMemoryTotal(&value);
- *memory_total += value;
- }
- return true;
-}
-
// static
void MemoryDumpManager::FinalizeDumpAndAddToTrace(
std::unique_ptr<ProcessMemoryDumpAsyncState> pmd_async_state) {
@@ -797,15 +663,11 @@ void MemoryDumpManager::OnTraceLogEnabled() {
return;
}
- const TraceConfig& trace_config =
+ const TraceConfig trace_config =
TraceLog::GetInstance()->GetCurrentTraceConfig();
- const TraceConfig::MemoryDumpConfig& memory_dump_config =
- trace_config.memory_dump_config();
scoped_refptr<MemoryDumpSessionState> session_state =
new MemoryDumpSessionState;
- session_state->SetAllowedDumpModes(memory_dump_config.allowed_dump_modes);
- session_state->set_heap_profiler_breakdown_threshold_bytes(
- memory_dump_config.heap_profiler_options.breakdown_threshold_bytes);
+ session_state->SetMemoryDumpConfig(trace_config.memory_dump_config());
if (heap_profiling_enabled_) {
// If heap profiling is enabled, the stack frame deduplicator and type name
// deduplicator will be in use. Add a metadata events to write the frames
@@ -819,26 +681,14 @@ void MemoryDumpManager::OnTraceLogEnabled() {
TRACE_EVENT_API_ADD_METADATA_EVENT(
TraceLog::GetCategoryGroupEnabled("__metadata"), "stackFrames",
"stackFrames",
- MakeUnique<SessionStateConvertableProxy<StackFrameDeduplicator>>(
- session_state, &MemoryDumpSessionState::stack_frame_deduplicator));
+ WrapUnique(new SessionStateConvertableProxy<StackFrameDeduplicator>(
+ session_state, &MemoryDumpSessionState::stack_frame_deduplicator)));
TRACE_EVENT_API_ADD_METADATA_EVENT(
TraceLog::GetCategoryGroupEnabled("__metadata"), "typeNames",
"typeNames",
- MakeUnique<SessionStateConvertableProxy<TypeNameDeduplicator>>(
- session_state, &MemoryDumpSessionState::type_name_deduplicator));
- }
-
- std::unique_ptr<MemoryDumpScheduler> dump_scheduler(
- new MemoryDumpScheduler(this, dump_thread->task_runner()));
- DCHECK_LE(memory_dump_config.triggers.size(), 3u);
- for (const auto& trigger : memory_dump_config.triggers) {
- if (!session_state->IsDumpModeAllowed(trigger.level_of_detail)) {
- NOTREACHED();
- continue;
- }
- dump_scheduler->AddTrigger(trigger.trigger_type, trigger.level_of_detail,
- trigger.min_time_between_dumps_ms);
+ WrapUnique(new SessionStateConvertableProxy<TypeNameDeduplicator>(
+ session_state, &MemoryDumpSessionState::type_name_deduplicator)));
}
{
@@ -849,65 +699,48 @@ void MemoryDumpManager::OnTraceLogEnabled() {
DCHECK(!dump_thread_);
dump_thread_ = std::move(dump_thread);
- dump_scheduler_ = std::move(dump_scheduler);
subtle::NoBarrier_Store(&memory_tracing_enabled_, 1);
- dump_providers_for_polling_.clear();
- for (const auto& mdpinfo : dump_providers_) {
- if (mdpinfo->options.is_fast_polling_supported)
- dump_providers_for_polling_.insert(mdpinfo);
+ // TODO(primiano): This is a temporary hack to disable periodic memory dumps
+ // when running memory benchmarks until telemetry uses TraceConfig to
+ // enable/disable periodic dumps. See crbug.com/529184 .
+ if (!is_coordinator_ ||
+ CommandLine::ForCurrentProcess()->HasSwitch(
+ "enable-memory-benchmarking")) {
+ return;
}
- // Notify polling supported only if some polling supported provider was
- // registered, else RegisterPollingMDPOnDumpThread() will notify when first
- // polling MDP registers.
- if (!dump_providers_for_polling_.empty())
- dump_scheduler_->NotifyPollingSupported();
-
- // Only coordinator process triggers periodic global memory dumps.
- if (is_coordinator_)
- dump_scheduler_->NotifyPeriodicTriggerSupported();
}
+ // Enable periodic dumps if necessary.
+ periodic_dump_timer_.Start(trace_config.memory_dump_config().triggers);
}
void MemoryDumpManager::OnTraceLogDisabled() {
// There might be a memory dump in progress while this happens. Therefore,
// ensure that the MDM state which depends on the tracing enabled / disabled
// state is always accessed by the dumping methods holding the |lock_|.
- if (!subtle::NoBarrier_Load(&memory_tracing_enabled_))
- return;
subtle::NoBarrier_Store(&memory_tracing_enabled_, 0);
std::unique_ptr<Thread> dump_thread;
- std::unique_ptr<MemoryDumpScheduler> scheduler;
{
AutoLock lock(lock_);
dump_thread = std::move(dump_thread_);
session_state_ = nullptr;
- scheduler = std::move(dump_scheduler_);
}
- scheduler->DisableAllTriggers();
// Thread stops are blocking and must be performed outside of the |lock_|
// or will deadlock (e.g., if SetupNextMemoryDump() tries to acquire it).
+ periodic_dump_timer_.Stop();
if (dump_thread)
dump_thread->Stop();
-
- // |dump_providers_for_polling_| must be cleared only after the dump thread is
- // stopped (polling tasks are done).
- {
- AutoLock lock(lock_);
- for (const auto& mdpinfo : dump_providers_for_polling_)
- mdpinfo->dump_provider->SuspendFastMemoryPolling();
- dump_providers_for_polling_.clear();
- }
}
bool MemoryDumpManager::IsDumpModeAllowed(MemoryDumpLevelOfDetail dump_mode) {
AutoLock lock(lock_);
if (!session_state_)
return false;
- return session_state_->IsDumpModeAllowed(dump_mode);
+ return session_state_->memory_dump_config().allowed_dump_modes.count(
+ dump_mode) != 0;
}
uint64_t MemoryDumpManager::GetTracingProcessId() const {
@@ -973,5 +806,78 @@ ProcessMemoryDump* MemoryDumpManager::ProcessMemoryDumpAsyncState::
return iter->second.get();
}
+MemoryDumpManager::PeriodicGlobalDumpTimer::PeriodicGlobalDumpTimer() {}
+
+MemoryDumpManager::PeriodicGlobalDumpTimer::~PeriodicGlobalDumpTimer() {
+ Stop();
+}
+
+void MemoryDumpManager::PeriodicGlobalDumpTimer::Start(
+ const std::vector<TraceConfig::MemoryDumpConfig::Trigger>& triggers_list) {
+ if (triggers_list.empty())
+ return;
+
+ // At the moment the periodic support is limited to at most one periodic
+ // trigger per dump mode. All intervals should be an integer multiple of the
+ // smallest interval specified.
+ periodic_dumps_count_ = 0;
+ uint32_t min_timer_period_ms = std::numeric_limits<uint32_t>::max();
+ uint32_t light_dump_period_ms = 0;
+ uint32_t heavy_dump_period_ms = 0;
+ DCHECK_LE(triggers_list.size(), 3u);
+ auto* mdm = MemoryDumpManager::GetInstance();
+ for (const TraceConfig::MemoryDumpConfig::Trigger& config : triggers_list) {
+ DCHECK_NE(0u, config.periodic_interval_ms);
+ switch (config.level_of_detail) {
+ case MemoryDumpLevelOfDetail::BACKGROUND:
+ DCHECK(mdm->IsDumpModeAllowed(MemoryDumpLevelOfDetail::BACKGROUND));
+ break;
+ case MemoryDumpLevelOfDetail::LIGHT:
+ DCHECK_EQ(0u, light_dump_period_ms);
+ DCHECK(mdm->IsDumpModeAllowed(MemoryDumpLevelOfDetail::LIGHT));
+ light_dump_period_ms = config.periodic_interval_ms;
+ break;
+ case MemoryDumpLevelOfDetail::DETAILED:
+ DCHECK_EQ(0u, heavy_dump_period_ms);
+ DCHECK(mdm->IsDumpModeAllowed(MemoryDumpLevelOfDetail::DETAILED));
+ heavy_dump_period_ms = config.periodic_interval_ms;
+ break;
+ }
+ min_timer_period_ms =
+ std::min(min_timer_period_ms, config.periodic_interval_ms);
+ }
+
+ DCHECK_EQ(0u, light_dump_period_ms % min_timer_period_ms);
+ light_dump_rate_ = light_dump_period_ms / min_timer_period_ms;
+ DCHECK_EQ(0u, heavy_dump_period_ms % min_timer_period_ms);
+ heavy_dump_rate_ = heavy_dump_period_ms / min_timer_period_ms;
+
+ timer_.Start(FROM_HERE, TimeDelta::FromMilliseconds(min_timer_period_ms),
+ base::Bind(&PeriodicGlobalDumpTimer::RequestPeriodicGlobalDump,
+ base::Unretained(this)));
+}
+
+void MemoryDumpManager::PeriodicGlobalDumpTimer::Stop() {
+ if (IsRunning()) {
+ timer_.Stop();
+ }
+}
+
+bool MemoryDumpManager::PeriodicGlobalDumpTimer::IsRunning() {
+ return timer_.IsRunning();
+}
+
+void MemoryDumpManager::PeriodicGlobalDumpTimer::RequestPeriodicGlobalDump() {
+ MemoryDumpLevelOfDetail level_of_detail = MemoryDumpLevelOfDetail::BACKGROUND;
+ if (light_dump_rate_ > 0 && periodic_dumps_count_ % light_dump_rate_ == 0)
+ level_of_detail = MemoryDumpLevelOfDetail::LIGHT;
+ if (heavy_dump_rate_ > 0 && periodic_dumps_count_ % heavy_dump_rate_ == 0)
+ level_of_detail = MemoryDumpLevelOfDetail::DETAILED;
+ ++periodic_dumps_count_;
+
+ MemoryDumpManager::GetInstance()->RequestGlobalDump(
+ MemoryDumpType::PERIODIC_INTERVAL, level_of_detail);
+}
+
} // namespace trace_event
} // namespace base
diff --git a/base/trace_event/memory_dump_manager.h b/base/trace_event/memory_dump_manager.h
index 92cc2f401b..06b772c6e4 100644
--- a/base/trace_event/memory_dump_manager.h
+++ b/base/trace_event/memory_dump_manager.h
@@ -18,6 +18,7 @@
#include "base/memory/ref_counted.h"
#include "base/memory/singleton.h"
#include "base/synchronization/lock.h"
+#include "base/timer/timer.h"
#include "base/trace_event/memory_dump_request_args.h"
#include "base/trace_event/process_memory_dump.h"
#include "base/trace_event/trace_event.h"
@@ -32,7 +33,6 @@ namespace trace_event {
class MemoryDumpManagerDelegate;
class MemoryDumpProvider;
class MemoryDumpSessionState;
-class MemoryDumpScheduler;
// This is the interface exposed to the rest of the codebase to deal with
// memory tracing. The main entry point for clients is represented by
@@ -94,8 +94,7 @@ class BASE_EXPORT MemoryDumpManager : public TraceLog::EnabledStateObserver {
// This method takes ownership of the dump provider and guarantees that:
// - The |mdp| will be deleted at some point in the near future.
// - Its deletion will not happen concurrently with the OnMemoryDump() call.
- // Note that OnMemoryDump() and PollFastMemoryTotal() calls can still happen
- // after this method returns.
+ // Note that OnMemoryDump() calls can still happen after this method returns.
void UnregisterAndDeleteDumpProviderSoon(
std::unique_ptr<MemoryDumpProvider> mdp);
@@ -117,9 +116,6 @@ class BASE_EXPORT MemoryDumpManager : public TraceLog::EnabledStateObserver {
void OnTraceLogEnabled() override;
void OnTraceLogDisabled() override;
- // Enable heap profiling if kEnableHeapProfiling is specified.
- void EnableHeapProfilingIfNeeded();
-
// Returns true if the dump mode is allowed for current tracing session.
bool IsDumpModeAllowed(MemoryDumpLevelOfDetail dump_mode);
@@ -155,7 +151,6 @@ class BASE_EXPORT MemoryDumpManager : public TraceLog::EnabledStateObserver {
friend struct DefaultSingletonTraits<MemoryDumpManager>;
friend class MemoryDumpManagerDelegate;
friend class MemoryDumpManagerTest;
- friend class MemoryDumpScheduler;
// Descriptor used to hold information about registered MDPs.
// Some important considerations about lifetime of this object:
@@ -278,6 +273,31 @@ class BASE_EXPORT MemoryDumpManager : public TraceLog::EnabledStateObserver {
DISALLOW_COPY_AND_ASSIGN(ProcessMemoryDumpAsyncState);
};
+ // Sets up periodic memory dump timers to start global dump requests based on
+ // the dump triggers from trace config.
+ class BASE_EXPORT PeriodicGlobalDumpTimer {
+ public:
+ PeriodicGlobalDumpTimer();
+ ~PeriodicGlobalDumpTimer();
+
+ void Start(const std::vector<TraceConfig::MemoryDumpConfig::Trigger>&
+ triggers_list);
+ void Stop();
+
+ bool IsRunning();
+
+ private:
+ // Periodically called by the timer.
+ void RequestPeriodicGlobalDump();
+
+ RepeatingTimer timer_;
+ uint32_t periodic_dumps_count_;
+ uint32_t light_dump_rate_;
+ uint32_t heavy_dump_rate_;
+
+ DISALLOW_COPY_AND_ASSIGN(PeriodicGlobalDumpTimer);
+ };
+
static const int kMaxConsecutiveFailuresCount;
static const char* const kSystemAllocatorPoolName;
@@ -288,6 +308,9 @@ class BASE_EXPORT MemoryDumpManager : public TraceLog::EnabledStateObserver {
static void FinalizeDumpAndAddToTrace(
std::unique_ptr<ProcessMemoryDumpAsyncState> pmd_async_state);
+ // Enable heap profiling if kEnableHeapProfiling is specified.
+ void EnableHeapProfilingIfNeeded();
+
// Internal, used only by MemoryDumpManagerDelegate.
// Creates a memory dump for the current process and appends it to the trace.
// |callback| will be invoked asynchronously upon completion on the same
@@ -306,14 +329,6 @@ class BASE_EXPORT MemoryDumpManager : public TraceLog::EnabledStateObserver {
// runner.
void InvokeOnMemoryDump(ProcessMemoryDumpAsyncState* owned_pmd_async_state);
- // Records a quick total memory usage in |memory_total|. This is used to track
- // and detect peaks in the memory usage of the process without having to
- // record all data from dump providers. This value is approximate to trade-off
- // speed, and not consistent with the rest of the memory-infra metrics. Must
- // be called on the dump thread.
- // Returns true if |memory_total| was updated by polling at least 1 MDP.
- bool PollFastMemoryTotal(uint64_t* memory_total);
-
// Helper for RegierDumpProvider* functions.
void RegisterDumpProviderInternal(
MemoryDumpProvider* mdp,
@@ -325,29 +340,13 @@ class BASE_EXPORT MemoryDumpManager : public TraceLog::EnabledStateObserver {
void UnregisterDumpProviderInternal(MemoryDumpProvider* mdp,
bool take_mdp_ownership_and_delete_async);
- // Adds / removes provider that supports polling to
- // |dump_providers_for_polling_|.
- void RegisterPollingMDPOnDumpThread(
- scoped_refptr<MemoryDumpProviderInfo> mdpinfo);
- void UnregisterPollingMDPOnDumpThread(
- scoped_refptr<MemoryDumpProviderInfo> mdpinfo);
-
// An ordererd set of registered MemoryDumpProviderInfo(s), sorted by task
// runner affinity (MDPs belonging to the same task runners are adjacent).
MemoryDumpProviderInfo::OrderedSet dump_providers_;
- // A copy of mdpinfo list that support polling. It must be accessed only on
- // the dump thread if dump thread exists.
- MemoryDumpProviderInfo::OrderedSet dump_providers_for_polling_;
-
// Shared among all the PMDs to keep state scoped to the tracing session.
scoped_refptr<MemoryDumpSessionState> session_state_;
- // The list of names of dump providers that are blacklisted from strict thread
- // affinity check on unregistration.
- std::unordered_set<StringPiece, StringPieceHash>
- strict_thread_check_blacklist_;
-
MemoryDumpManagerDelegate* delegate_; // Not owned.
// When true, this instance is in charge of coordinating periodic dumps.
@@ -361,8 +360,8 @@ class BASE_EXPORT MemoryDumpManager : public TraceLog::EnabledStateObserver {
// dump_providers_enabled_ list) when tracing is not enabled.
subtle::AtomicWord memory_tracing_enabled_;
- // For triggering memory dumps.
- std::unique_ptr<MemoryDumpScheduler> dump_scheduler_;
+ // For time-triggered periodic dumps.
+ PeriodicGlobalDumpTimer periodic_dump_timer_;
// Thread used for MemoryDumpProviders which don't specify a task runner
// affinity.
diff --git a/base/trace_event/memory_dump_manager_unittest.cc b/base/trace_event/memory_dump_manager_unittest.cc
index 51d41943fb..d14093cbcc 100644
--- a/base/trace_event/memory_dump_manager_unittest.cc
+++ b/base/trace_event/memory_dump_manager_unittest.cc
@@ -16,16 +16,13 @@
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/waitable_event.h"
-#include "base/test/sequenced_worker_pool_owner.h"
#include "base/test/test_io_thread.h"
#include "base/test/trace_event_analyzer.h"
#include "base/threading/platform_thread.h"
-#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/sequenced_worker_pool.h"
#include "base/threading/thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/memory_dump_provider.h"
-#include "base/trace_event/memory_dump_scheduler.h"
#include "base/trace_event/memory_infra_background_whitelist.h"
#include "base/trace_event/process_memory_dump.h"
#include "base/trace_event/trace_buffer.h"
@@ -73,10 +70,8 @@ void RegisterDumpProvider(
mdm->set_dumper_registrations_ignored_for_testing(true);
}
-void RegisterDumpProvider(
- MemoryDumpProvider* mdp,
- scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
- RegisterDumpProvider(mdp, task_runner, MemoryDumpProvider::Options());
+void RegisterDumpProvider(MemoryDumpProvider* mdp) {
+ RegisterDumpProvider(mdp, nullptr, MemoryDumpProvider::Options());
}
void RegisterDumpProviderWithSequencedTaskRunner(
@@ -99,20 +94,6 @@ void OnTraceDataCollected(Closure quit_closure,
quit_closure.Run();
}
-// Posts |task| to |task_runner| and blocks until it is executed.
-void PostTaskAndWait(const tracked_objects::Location& from_here,
- SequencedTaskRunner* task_runner,
- const base::Closure& task) {
- base::WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL,
- WaitableEvent::InitialState::NOT_SIGNALED);
- task_runner->PostTask(from_here, task);
- task_runner->PostTask(
- FROM_HERE, base::Bind(&WaitableEvent::Signal, base::Unretained(&event)));
- // The SequencedTaskRunner guarantees that |event| will only be signaled after
- // |task| is executed.
- event.Wait();
-}
-
} // namespace
// Testing MemoryDumpManagerDelegate which, by default, short-circuits dump
@@ -143,8 +124,6 @@ class MockMemoryDumpProvider : public MemoryDumpProvider {
MOCK_METHOD0(Destructor, void());
MOCK_METHOD2(OnMemoryDump,
bool(const MemoryDumpArgs& args, ProcessMemoryDump* pmd));
- MOCK_METHOD1(PollFastMemoryTotal, void(uint64_t* memory_total));
- MOCK_METHOD0(SuspendFastMemoryPolling, void());
MockMemoryDumpProvider() : enable_mock_destructor(false) {
ON_CALL(*this, OnMemoryDump(_, _))
@@ -156,10 +135,6 @@ class MockMemoryDumpProvider : public MemoryDumpProvider {
EXPECT_TRUE(pmd->session_state().get() != nullptr);
return true;
}));
-
- ON_CALL(*this, PollFastMemoryTotal(_))
- .WillByDefault(
- Invoke([](uint64_t* memory_total) -> void { NOTREACHED(); }));
}
~MockMemoryDumpProvider() override {
if (enable_mock_destructor)
@@ -172,7 +147,8 @@ class MockMemoryDumpProvider : public MemoryDumpProvider {
class TestSequencedTaskRunner : public SequencedTaskRunner {
public:
TestSequencedTaskRunner()
- : worker_pool_(2 /* max_threads */, "Test Task Runner"),
+ : worker_pool_(
+ new SequencedWorkerPool(2 /* max_threads */, "Test Task Runner")),
enabled_(true),
num_of_post_tasks_(0) {}
@@ -190,21 +166,19 @@ class TestSequencedTaskRunner : public SequencedTaskRunner {
const Closure& task,
TimeDelta delay) override {
num_of_post_tasks_++;
- if (enabled_) {
- return worker_pool_.pool()->PostSequencedWorkerTask(token_, from_here,
- task);
- }
+ if (enabled_)
+ return worker_pool_->PostSequencedWorkerTask(token_, from_here, task);
return false;
}
bool RunsTasksOnCurrentThread() const override {
- return worker_pool_.pool()->RunsTasksOnCurrentThread();
+ return worker_pool_->IsRunningSequenceOnCurrentThread(token_);
}
private:
~TestSequencedTaskRunner() override {}
- SequencedWorkerPoolOwner worker_pool_;
+ scoped_refptr<SequencedWorkerPool> worker_pool_;
const SequencedWorkerPool::SequenceToken token_;
bool enabled_;
unsigned num_of_post_tasks_;
@@ -241,10 +215,6 @@ class MemoryDumpManagerTest : public testing::Test {
task_runner->PostTask(FROM_HERE, closure);
}
- void PollFastMemoryTotal(uint64_t* memory_total) {
- mdm_->PollFastMemoryTotal(memory_total);
- }
-
protected:
void InitializeMemoryDumpManager(bool is_coordinator) {
mdm_->set_dumper_registrations_ignored_for_testing(true);
@@ -274,7 +244,7 @@ class MemoryDumpManagerTest : public testing::Test {
void DisableTracing() { TraceLog::GetInstance()->SetDisabled(); }
bool IsPeriodicDumpingEnabled() const {
- return mdm_->dump_scheduler_->IsPeriodicTimerRunningForTesting();
+ return mdm_->periodic_dump_timer_.IsRunning();
}
int GetMaxConsecutiveFailuresCount() const {
@@ -298,7 +268,7 @@ class MemoryDumpManagerTest : public testing::Test {
TEST_F(MemoryDumpManagerTest, SingleDumper) {
InitializeMemoryDumpManager(false /* is_coordinator */);
MockMemoryDumpProvider mdp;
- RegisterDumpProvider(&mdp, ThreadTaskRunnerHandle::Get());
+ RegisterDumpProvider(&mdp);
// Check that the dumper is not called if the memory category is not enabled.
EnableTracingWithLegacyCategories("foobar-but-not-memory");
@@ -339,7 +309,7 @@ TEST_F(MemoryDumpManagerTest, CheckMemoryDumpArgs) {
InitializeMemoryDumpManager(false /* is_coordinator */);
MockMemoryDumpProvider mdp;
- RegisterDumpProvider(&mdp, ThreadTaskRunnerHandle::Get());
+ RegisterDumpProvider(&mdp);
EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1);
EXPECT_CALL(mdp, OnMemoryDump(IsDetailedDump(), _)).WillOnce(Return(true));
@@ -350,7 +320,7 @@ TEST_F(MemoryDumpManagerTest, CheckMemoryDumpArgs) {
// Check that requesting dumps with low level of detail actually propagates to
// OnMemoryDump() call on dump providers.
- RegisterDumpProvider(&mdp, ThreadTaskRunnerHandle::Get());
+ RegisterDumpProvider(&mdp);
EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1);
EXPECT_CALL(mdp, OnMemoryDump(IsLightDump(), _)).WillOnce(Return(true));
@@ -365,8 +335,8 @@ TEST_F(MemoryDumpManagerTest, SharedSessionState) {
InitializeMemoryDumpManager(false /* is_coordinator */);
MockMemoryDumpProvider mdp1;
MockMemoryDumpProvider mdp2;
- RegisterDumpProvider(&mdp1, nullptr);
- RegisterDumpProvider(&mdp2, nullptr);
+ RegisterDumpProvider(&mdp1);
+ RegisterDumpProvider(&mdp2);
EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
const MemoryDumpSessionState* session_state =
@@ -402,7 +372,7 @@ TEST_F(MemoryDumpManagerTest, MultipleDumpers) {
MockMemoryDumpProvider mdp2;
// Enable only mdp1.
- RegisterDumpProvider(&mdp1, ThreadTaskRunnerHandle::Get());
+ RegisterDumpProvider(&mdp1);
EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1);
EXPECT_CALL(mdp1, OnMemoryDump(_, _)).WillOnce(Return(true));
@@ -413,7 +383,7 @@ TEST_F(MemoryDumpManagerTest, MultipleDumpers) {
// Invert: enable mdp1 and disable mdp2.
mdm_->UnregisterDumpProvider(&mdp1);
- RegisterDumpProvider(&mdp2, nullptr);
+ RegisterDumpProvider(&mdp2);
EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1);
EXPECT_CALL(mdp1, OnMemoryDump(_, _)).Times(0);
@@ -423,7 +393,7 @@ TEST_F(MemoryDumpManagerTest, MultipleDumpers) {
DisableTracing();
// Enable both mdp1 and mdp2.
- RegisterDumpProvider(&mdp1, nullptr);
+ RegisterDumpProvider(&mdp1);
EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1);
EXPECT_CALL(mdp1, OnMemoryDump(_, _)).WillOnce(Return(true));
@@ -439,7 +409,7 @@ TEST_F(MemoryDumpManagerTest, RegistrationConsistency) {
InitializeMemoryDumpManager(false /* is_coordinator */);
MockMemoryDumpProvider mdp;
- RegisterDumpProvider(&mdp, ThreadTaskRunnerHandle::Get());
+ RegisterDumpProvider(&mdp);
{
EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1);
@@ -461,7 +431,7 @@ TEST_F(MemoryDumpManagerTest, RegistrationConsistency) {
DisableTracing();
}
- RegisterDumpProvider(&mdp, ThreadTaskRunnerHandle::Get());
+ RegisterDumpProvider(&mdp);
mdm_->UnregisterDumpProvider(&mdp);
{
@@ -473,9 +443,9 @@ TEST_F(MemoryDumpManagerTest, RegistrationConsistency) {
DisableTracing();
}
- RegisterDumpProvider(&mdp, ThreadTaskRunnerHandle::Get());
+ RegisterDumpProvider(&mdp);
mdm_->UnregisterDumpProvider(&mdp);
- RegisterDumpProvider(&mdp, ThreadTaskRunnerHandle::Get());
+ RegisterDumpProvider(&mdp);
{
EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1);
@@ -597,8 +567,8 @@ TEST_F(MemoryDumpManagerTest, DisableFailingDumpers) {
MockMemoryDumpProvider mdp1;
MockMemoryDumpProvider mdp2;
- RegisterDumpProvider(&mdp1, nullptr);
- RegisterDumpProvider(&mdp2, nullptr);
+ RegisterDumpProvider(&mdp1);
+ RegisterDumpProvider(&mdp2);
EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
const int kNumDumps = 2 * GetMaxConsecutiveFailuresCount();
@@ -631,7 +601,7 @@ TEST_F(MemoryDumpManagerTest, RegisterDumperWhileDumping) {
MockMemoryDumpProvider mdp1;
MockMemoryDumpProvider mdp2;
- RegisterDumpProvider(&mdp1, nullptr);
+ RegisterDumpProvider(&mdp1);
EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(4);
@@ -641,7 +611,7 @@ TEST_F(MemoryDumpManagerTest, RegisterDumperWhileDumping) {
.WillOnce(Return(true))
.WillOnce(
Invoke([&mdp2](const MemoryDumpArgs&, ProcessMemoryDump*) -> bool {
- RegisterDumpProvider(&mdp2, nullptr);
+ RegisterDumpProvider(&mdp2);
return true;
}))
.WillRepeatedly(Return(true));
@@ -717,16 +687,13 @@ TEST_F(MemoryDumpManagerTest, UnregisterDumperFromThreadWhileDumping) {
// unregister the other one.
for (const std::unique_ptr<MockMemoryDumpProvider>& mdp : mdps) {
int other_idx = (mdps.front() == mdp);
- // TestIOThread's task runner must be obtained from the main thread but can
- // then be used from other threads.
- scoped_refptr<SingleThreadTaskRunner> other_runner =
- threads[other_idx]->task_runner();
+ TestIOThread* other_thread = threads[other_idx].get();
MockMemoryDumpProvider* other_mdp = mdps[other_idx].get();
- auto on_dump = [this, other_runner, other_mdp, &on_memory_dump_call_count](
+ auto on_dump = [this, other_thread, other_mdp, &on_memory_dump_call_count](
const MemoryDumpArgs& args, ProcessMemoryDump* pmd) {
- PostTaskAndWait(FROM_HERE, other_runner.get(),
- base::Bind(&MemoryDumpManager::UnregisterDumpProvider,
- base::Unretained(&*mdm_), other_mdp));
+ other_thread->PostTaskAndWait(
+ FROM_HERE, base::Bind(&MemoryDumpManager::UnregisterDumpProvider,
+ base::Unretained(&*mdm_), other_mdp));
on_memory_dump_call_count++;
return true;
};
@@ -749,75 +716,6 @@ TEST_F(MemoryDumpManagerTest, UnregisterDumperFromThreadWhileDumping) {
DisableTracing();
}
-TEST_F(MemoryDumpManagerTest, TestPollingOnDumpThread) {
- InitializeMemoryDumpManager(false /* is_coordinator */);
- std::unique_ptr<MockMemoryDumpProvider> mdp1(new MockMemoryDumpProvider());
- std::unique_ptr<MockMemoryDumpProvider> mdp2(new MockMemoryDumpProvider());
- mdp1->enable_mock_destructor = true;
- mdp2->enable_mock_destructor = true;
-
- EXPECT_CALL(*mdp1, SuspendFastMemoryPolling()).Times(1);
- EXPECT_CALL(*mdp2, SuspendFastMemoryPolling()).Times(1);
- EXPECT_CALL(*mdp1, Destructor());
- EXPECT_CALL(*mdp2, Destructor());
-
- MemoryDumpProvider::Options options;
- options.is_fast_polling_supported = true;
- RegisterDumpProvider(mdp1.get(), nullptr, options);
-
- RunLoop run_loop;
- scoped_refptr<SingleThreadTaskRunner> test_task_runner =
- ThreadTaskRunnerHandle::Get();
- auto quit_closure = run_loop.QuitClosure();
-
- const int kPollsToQuit = 10;
- int call_count = 0;
- MemoryDumpManager* mdm = mdm_.get();
- const auto poll_function1 = [&call_count, &test_task_runner, quit_closure,
- &mdp2, mdm, &options, kPollsToQuit,
- this](uint64_t* total) -> void {
- ++call_count;
- if (call_count == 1)
- RegisterDumpProvider(mdp2.get(), nullptr, options, kMDPName);
- else if (call_count == 4)
- mdm->UnregisterAndDeleteDumpProviderSoon(std::move(mdp2));
- else if (call_count == kPollsToQuit)
- test_task_runner->PostTask(FROM_HERE, quit_closure);
-
- // Record increase of 1 GiB of memory at each call.
- *total = static_cast<uint64_t>(call_count) * 1024 * 1024 * 1024;
- };
- EXPECT_CALL(*mdp1, PollFastMemoryTotal(_))
- .Times(testing::AtLeast(kPollsToQuit))
- .WillRepeatedly(Invoke(poll_function1));
-
- // Depending on the order of PostTask calls the mdp2 might be registered after
- // all polls or in between polls.
- EXPECT_CALL(*mdp2, PollFastMemoryTotal(_))
- .Times(Between(0, kPollsToQuit - 1))
- .WillRepeatedly(Return());
-
- MemoryDumpScheduler::SetPollingIntervalForTesting(1);
- EnableTracingWithTraceConfig(
- TraceConfigMemoryTestUtil::GetTraceConfig_PeakDetectionTrigger(3));
-
- int last_poll_to_request_dump = -2;
- EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _))
- .Times(testing::AtLeast(2))
- .WillRepeatedly(Invoke([&last_poll_to_request_dump, &call_count](
- const MemoryDumpRequestArgs& args,
- const MemoryDumpCallback& callback) -> void {
- // Minimum number of polls between dumps must be 3 (polling interval is
- // 1ms).
- EXPECT_GE(call_count - last_poll_to_request_dump, 3);
- last_poll_to_request_dump = call_count;
- }));
-
- run_loop.Run();
- DisableTracing();
- mdm_->UnregisterAndDeleteDumpProviderSoon(std::move(mdp1));
-}
-
// If a thread (with a dump provider living on it) is torn down during a dump
// its dump provider should be skipped but the dump itself should succeed.
TEST_F(MemoryDumpManagerTest, TearDownThreadWhileDumping) {
@@ -840,14 +738,9 @@ TEST_F(MemoryDumpManagerTest, TearDownThreadWhileDumping) {
for (const std::unique_ptr<MockMemoryDumpProvider>& mdp : mdps) {
int other_idx = (mdps.front() == mdp);
TestIOThread* other_thread = threads[other_idx].get();
- // TestIOThread isn't thread-safe and must be stopped on the |main_runner|.
- scoped_refptr<SequencedTaskRunner> main_runner =
- SequencedTaskRunnerHandle::Get();
- auto on_dump = [other_thread, main_runner, &on_memory_dump_call_count](
+ auto on_dump = [other_thread, &on_memory_dump_call_count](
const MemoryDumpArgs& args, ProcessMemoryDump* pmd) {
- PostTaskAndWait(
- FROM_HERE, main_runner.get(),
- base::Bind(&TestIOThread::Stop, base::Unretained(other_thread)));
+ other_thread->Stop();
on_memory_dump_call_count++;
return true;
};
@@ -875,7 +768,7 @@ TEST_F(MemoryDumpManagerTest, TearDownThreadWhileDumping) {
TEST_F(MemoryDumpManagerTest, CallbackCalledOnFailure) {
InitializeMemoryDumpManager(false /* is_coordinator */);
MockMemoryDumpProvider mdp1;
- RegisterDumpProvider(&mdp1, nullptr);
+ RegisterDumpProvider(&mdp1);
EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(0);
EXPECT_CALL(mdp1, OnMemoryDump(_, _)).Times(0);
@@ -890,7 +783,7 @@ TEST_F(MemoryDumpManagerTest, CallbackCalledOnFailure) {
// began, it will still late-join the party (real use case: startup tracing).
TEST_F(MemoryDumpManagerTest, InitializedAfterStartOfTracing) {
MockMemoryDumpProvider mdp;
- RegisterDumpProvider(&mdp, nullptr);
+ RegisterDumpProvider(&mdp);
EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
// First check that a RequestGlobalDump() issued before the MemoryDumpManager
@@ -1073,7 +966,7 @@ TEST_F(MemoryDumpManagerTest, DisableTracingRightBeforeStartOfDump) {
// Create both same-thread MDP and another MDP with dedicated thread
MockMemoryDumpProvider mdp1;
- RegisterDumpProvider(&mdp1, nullptr);
+ RegisterDumpProvider(&mdp1);
MockMemoryDumpProvider mdp2;
RegisterDumpProvider(&mdp2, mdp_thread->task_runner(), kDefaultOptions);
EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
@@ -1192,8 +1085,8 @@ TEST_F(MemoryDumpManagerTest, UnregisterAndDeleteDumpProviderSoonDuringDump) {
const MemoryDumpArgs&, ProcessMemoryDump*) -> bool {
thread_ref = PlatformThread::CurrentRef();
TestIOThread thread_for_unregistration(TestIOThread::kAutoStart);
- PostTaskAndWait(
- FROM_HERE, thread_for_unregistration.task_runner().get(),
+ thread_for_unregistration.PostTaskAndWait(
+ FROM_HERE,
base::Bind(
&MemoryDumpManager::UnregisterAndDeleteDumpProviderSoon,
base::Unretained(MemoryDumpManager::GetInstance()),
@@ -1223,7 +1116,7 @@ TEST_F(MemoryDumpManagerTest, TestWhitelistingMDP) {
InitializeMemoryDumpManager(false /* is_coordinator */);
SetDumpProviderWhitelistForTesting(kTestMDPWhitelist);
std::unique_ptr<MockMemoryDumpProvider> mdp1(new MockMemoryDumpProvider);
- RegisterDumpProvider(mdp1.get(), nullptr);
+ RegisterDumpProvider(mdp1.get());
std::unique_ptr<MockMemoryDumpProvider> mdp2(new MockMemoryDumpProvider);
RegisterDumpProvider(mdp2.get(), nullptr, kDefaultOptions,
kWhitelistedMDPName);
@@ -1274,22 +1167,5 @@ TEST_F(MemoryDumpManagerTest, TestBackgroundTracingSetup) {
DisableTracing();
}
-TEST_F(MemoryDumpManagerTest, TestBlacklistedUnsafeUnregistration) {
- InitializeMemoryDumpManager(false /* is_coordinator */);
- MockMemoryDumpProvider mdp1;
- RegisterDumpProvider(&mdp1, nullptr, kDefaultOptions,
- "BlacklistTestDumpProvider");
- // Not calling UnregisterAndDeleteDumpProviderSoon() should not crash.
- mdm_->UnregisterDumpProvider(&mdp1);
-
- Thread thread("test thread");
- thread.Start();
- RegisterDumpProvider(&mdp1, thread.task_runner(), kDefaultOptions,
- "BlacklistTestDumpProvider");
- // Unregistering on wrong thread should not crash.
- mdm_->UnregisterDumpProvider(&mdp1);
- thread.Stop();
-}
-
} // namespace trace_event
} // namespace base
diff --git a/base/trace_event/memory_dump_provider.h b/base/trace_event/memory_dump_provider.h
index 76c2969e96..2c502861d8 100644
--- a/base/trace_event/memory_dump_provider.h
+++ b/base/trace_event/memory_dump_provider.h
@@ -22,8 +22,7 @@ class BASE_EXPORT MemoryDumpProvider {
struct Options {
Options()
: target_pid(kNullProcessId),
- dumps_on_single_thread_task_runner(false),
- is_fast_polling_supported(false) {}
+ dumps_on_single_thread_task_runner(false) {}
// If the dump provider generates dumps on behalf of another process,
// |target_pid| contains the pid of that process.
@@ -35,11 +34,6 @@ class BASE_EXPORT MemoryDumpProvider {
// a SingleThreadTaskRunner, which is usually the case. It is faster to run
// all providers that run on the same thread together without thread hops.
bool dumps_on_single_thread_task_runner;
-
- // Set to true if the dump provider implementation supports high frequency
- // polling. Only providers running without task runner affinity are
- // supported.
- bool is_fast_polling_supported;
};
virtual ~MemoryDumpProvider() {}
@@ -58,18 +52,6 @@ class BASE_EXPORT MemoryDumpProvider {
// collecting extensive allocation data, if supported.
virtual void OnHeapProfilingEnabled(bool) {}
- // Quickly record the total memory usage in |memory_total|. This method will
- // be called only when the dump provider registration has
- // |is_fast_polling_supported| set to true. This method is used for polling at
- // high frequency for detecting peaks. See comment on
- // |is_fast_polling_supported| option if you need to override this method.
- virtual void PollFastMemoryTotal(uint64_t* /* memory_total */) {}
-
- // Indicates that fast memory polling is not going to be used in the near
- // future and the MDP can tear down any resource kept around for fast memory
- // polling.
- virtual void SuspendFastMemoryPolling() {}
-
protected:
MemoryDumpProvider() {}
diff --git a/base/trace_event/memory_dump_request_args.cc b/base/trace_event/memory_dump_request_args.cc
index bf72bef5e4..e6c5b87b22 100644
--- a/base/trace_event/memory_dump_request_args.cc
+++ b/base/trace_event/memory_dump_request_args.cc
@@ -12,28 +12,19 @@ namespace trace_event {
// static
const char* MemoryDumpTypeToString(const MemoryDumpType& dump_type) {
switch (dump_type) {
+ case MemoryDumpType::TASK_BEGIN:
+ return "task_begin";
+ case MemoryDumpType::TASK_END:
+ return "task_end";
case MemoryDumpType::PERIODIC_INTERVAL:
return "periodic_interval";
case MemoryDumpType::EXPLICITLY_TRIGGERED:
return "explicitly_triggered";
- case MemoryDumpType::PEAK_MEMORY_USAGE:
- return "peak_memory_usage";
}
NOTREACHED();
return "unknown";
}
-MemoryDumpType StringToMemoryDumpType(const std::string& str) {
- if (str == "periodic_interval")
- return MemoryDumpType::PERIODIC_INTERVAL;
- if (str == "explicitly_triggered")
- return MemoryDumpType::EXPLICITLY_TRIGGERED;
- if (str == "peak_memory_usage")
- return MemoryDumpType::PEAK_MEMORY_USAGE;
- NOTREACHED();
- return MemoryDumpType::LAST;
-}
-
const char* MemoryDumpLevelOfDetailToString(
const MemoryDumpLevelOfDetail& level_of_detail) {
switch (level_of_detail) {
diff --git a/base/trace_event/memory_dump_request_args.h b/base/trace_event/memory_dump_request_args.h
index 90a866fa7a..f3ff9d8e3b 100644
--- a/base/trace_event/memory_dump_request_args.h
+++ b/base/trace_event/memory_dump_request_args.h
@@ -18,19 +18,16 @@ namespace base {
namespace trace_event {
// Captures the reason why a memory dump is being requested. This is to allow
-// selective enabling of dumps, filtering and post-processing. Important: this
-// must be kept consistent with
-// services/resource_coordinator/public/cpp/memory/memory_infra_traits.cc.
+// selective enabling of dumps, filtering and post-processing.
enum class MemoryDumpType {
- PERIODIC_INTERVAL, // Dumping memory at periodic intervals.
+ TASK_BEGIN, // Dumping memory at the beginning of a message-loop task.
+ TASK_END, // Dumping memory at the ending of a message-loop task.
+ PERIODIC_INTERVAL, // Dumping memory at periodic intervals.
EXPLICITLY_TRIGGERED, // Non maskable dump request.
- PEAK_MEMORY_USAGE, // Dumping memory at detected peak total memory usage.
- LAST = PEAK_MEMORY_USAGE // For IPC macros.
+ LAST = EXPLICITLY_TRIGGERED // For IPC macros.
};
// Tells the MemoryDumpProvider(s) how much detailed their dumps should be.
-// Important: this must be kept consistent with
-// services/resource_Coordinator/public/cpp/memory/memory_infra_traits.cc.
enum class MemoryDumpLevelOfDetail : uint32_t {
FIRST,
@@ -53,8 +50,7 @@ enum class MemoryDumpLevelOfDetail : uint32_t {
};
// Initial request arguments for a global memory dump. (see
-// MemoryDumpManager::RequestGlobalMemoryDump()). Important: this must be kept
-// consistent with services/memory_infra/public/cpp/memory_infra_traits.cc.
+// MemoryDumpManager::RequestGlobalMemoryDump()).
struct BASE_EXPORT MemoryDumpRequestArgs {
// Globally unique identifier. In multi-process dumps, all processes issue a
// local dump with the same guid. This allows the trace importers to
@@ -76,8 +72,6 @@ using MemoryDumpCallback = Callback<void(uint64_t dump_guid, bool success)>;
BASE_EXPORT const char* MemoryDumpTypeToString(const MemoryDumpType& dump_type);
-BASE_EXPORT MemoryDumpType StringToMemoryDumpType(const std::string& str);
-
BASE_EXPORT const char* MemoryDumpLevelOfDetailToString(
const MemoryDumpLevelOfDetail& level_of_detail);
diff --git a/base/trace_event/memory_dump_scheduler.cc b/base/trace_event/memory_dump_scheduler.cc
deleted file mode 100644
index eaa8d63661..0000000000
--- a/base/trace_event/memory_dump_scheduler.cc
+++ /dev/null
@@ -1,304 +0,0 @@
-// Copyright 2017 The Chromium 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/trace_event/memory_dump_scheduler.h"
-
-#include "base/process/process_metrics.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/trace_event/memory_dump_manager.h"
-#include "build/build_config.h"
-
-namespace base {
-namespace trace_event {
-
-namespace {
-// Threshold on increase in memory from last dump beyond which a new dump must
-// be triggered.
-int64_t kDefaultMemoryIncreaseThreshold = 50 * 1024 * 1024; // 50MiB
-const uint32_t kMemoryTotalsPollingInterval = 25;
-uint32_t g_polling_interval_ms_for_testing = 0;
-} // namespace
-
-MemoryDumpScheduler::MemoryDumpScheduler(
- MemoryDumpManager* mdm,
- scoped_refptr<SingleThreadTaskRunner> polling_task_runner)
- : mdm_(mdm), polling_state_(polling_task_runner) {}
-
-MemoryDumpScheduler::~MemoryDumpScheduler() {}
-
-void MemoryDumpScheduler::AddTrigger(MemoryDumpType trigger_type,
- MemoryDumpLevelOfDetail level_of_detail,
- uint32_t min_time_between_dumps_ms) {
- if (trigger_type == MemoryDumpType::PEAK_MEMORY_USAGE) {
- DCHECK(!periodic_state_.is_configured);
- DCHECK_EQ(PollingTriggerState::DISABLED, polling_state_.current_state);
- DCHECK_NE(0u, min_time_between_dumps_ms);
-
- polling_state_.level_of_detail = level_of_detail;
- polling_state_.min_polls_between_dumps =
- (min_time_between_dumps_ms + polling_state_.polling_interval_ms - 1) /
- polling_state_.polling_interval_ms;
- polling_state_.current_state = PollingTriggerState::CONFIGURED;
- } else if (trigger_type == MemoryDumpType::PERIODIC_INTERVAL) {
- DCHECK_EQ(PollingTriggerState::DISABLED, polling_state_.current_state);
- periodic_state_.is_configured = true;
- DCHECK_NE(0u, min_time_between_dumps_ms);
- switch (level_of_detail) {
- case MemoryDumpLevelOfDetail::BACKGROUND:
- break;
- case MemoryDumpLevelOfDetail::LIGHT:
- DCHECK_EQ(0u, periodic_state_.light_dump_period_ms);
- periodic_state_.light_dump_period_ms = min_time_between_dumps_ms;
- break;
- case MemoryDumpLevelOfDetail::DETAILED:
- DCHECK_EQ(0u, periodic_state_.heavy_dump_period_ms);
- periodic_state_.heavy_dump_period_ms = min_time_between_dumps_ms;
- break;
- }
-
- periodic_state_.min_timer_period_ms = std::min(
- periodic_state_.min_timer_period_ms, min_time_between_dumps_ms);
- DCHECK_EQ(0u, periodic_state_.light_dump_period_ms %
- periodic_state_.min_timer_period_ms);
- DCHECK_EQ(0u, periodic_state_.heavy_dump_period_ms %
- periodic_state_.min_timer_period_ms);
- }
-}
-
-void MemoryDumpScheduler::NotifyPeriodicTriggerSupported() {
- if (!periodic_state_.is_configured || periodic_state_.timer.IsRunning())
- return;
- periodic_state_.light_dumps_rate = periodic_state_.light_dump_period_ms /
- periodic_state_.min_timer_period_ms;
- periodic_state_.heavy_dumps_rate = periodic_state_.heavy_dump_period_ms /
- periodic_state_.min_timer_period_ms;
-
- periodic_state_.dump_count = 0;
- periodic_state_.timer.Start(
- FROM_HERE,
- TimeDelta::FromMilliseconds(periodic_state_.min_timer_period_ms),
- Bind(&MemoryDumpScheduler::RequestPeriodicGlobalDump, Unretained(this)));
-}
-
-void MemoryDumpScheduler::NotifyPollingSupported() {
- if (polling_state_.current_state != PollingTriggerState::CONFIGURED)
- return;
-
- polling_state_.current_state = PollingTriggerState::ENABLED;
- polling_state_.ResetTotals();
-
- polling_state_.polling_task_runner->PostTask(
- FROM_HERE,
- Bind(&MemoryDumpScheduler::PollMemoryOnPollingThread, Unretained(this)));
-}
-
-void MemoryDumpScheduler::NotifyDumpTriggered() {
- if (polling_state_.polling_task_runner &&
- polling_state_.polling_task_runner->RunsTasksOnCurrentThread()) {
- polling_state_.polling_task_runner->PostTask(
- FROM_HERE,
- Bind(&MemoryDumpScheduler::NotifyDumpTriggered, Unretained(this)));
- return;
- }
- if (polling_state_.current_state != PollingTriggerState::ENABLED)
- return;
-
- polling_state_.ResetTotals();
-}
-
-void MemoryDumpScheduler::DisableAllTriggers() {
- if (periodic_state_.timer.IsRunning())
- periodic_state_.timer.Stop();
- DisablePolling();
-}
-
-void MemoryDumpScheduler::DisablePolling() {
- if (polling_state_.polling_task_runner->RunsTasksOnCurrentThread()) {
- if (polling_state_.polling_task_runner->PostTask(
- FROM_HERE,
- Bind(&MemoryDumpScheduler::DisablePolling, Unretained(this))))
- return;
- }
- polling_state_.current_state = PollingTriggerState::DISABLED;
- polling_state_.polling_task_runner = nullptr;
-}
-
-// static
-void MemoryDumpScheduler::SetPollingIntervalForTesting(uint32_t interval) {
- g_polling_interval_ms_for_testing = interval;
-}
-
-bool MemoryDumpScheduler::IsPeriodicTimerRunningForTesting() {
- return periodic_state_.timer.IsRunning();
-}
-
-void MemoryDumpScheduler::RequestPeriodicGlobalDump() {
- MemoryDumpLevelOfDetail level_of_detail = MemoryDumpLevelOfDetail::BACKGROUND;
- if (periodic_state_.light_dumps_rate > 0 &&
- periodic_state_.dump_count % periodic_state_.light_dumps_rate == 0)
- level_of_detail = MemoryDumpLevelOfDetail::LIGHT;
- if (periodic_state_.heavy_dumps_rate > 0 &&
- periodic_state_.dump_count % periodic_state_.heavy_dumps_rate == 0)
- level_of_detail = MemoryDumpLevelOfDetail::DETAILED;
- ++periodic_state_.dump_count;
-
- mdm_->RequestGlobalDump(MemoryDumpType::PERIODIC_INTERVAL, level_of_detail);
-}
-
-void MemoryDumpScheduler::PollMemoryOnPollingThread() {
- if (polling_state_.current_state != PollingTriggerState::ENABLED)
- return;
-
- uint64_t polled_memory = 0;
- bool res = mdm_->PollFastMemoryTotal(&polled_memory);
- DCHECK(res);
- if (polling_state_.level_of_detail == MemoryDumpLevelOfDetail::DETAILED) {
- TRACE_COUNTER1(MemoryDumpManager::kTraceCategory, "PolledMemoryMB",
- polled_memory / 1024 / 1024);
- }
-
- if (ShouldTriggerDump(polled_memory)) {
- TRACE_EVENT_INSTANT1(MemoryDumpManager::kTraceCategory,
- "Peak memory dump Triggered",
- TRACE_EVENT_SCOPE_PROCESS, "total_usage_MB",
- polled_memory / 1024 / 1024);
-
- mdm_->RequestGlobalDump(MemoryDumpType::PEAK_MEMORY_USAGE,
- polling_state_.level_of_detail);
- }
-
- // TODO(ssid): Use RequestSchedulerCallback, crbug.com/607533.
- ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE,
- Bind(&MemoryDumpScheduler::PollMemoryOnPollingThread, Unretained(this)),
- TimeDelta::FromMilliseconds(polling_state_.polling_interval_ms));
-}
-
-bool MemoryDumpScheduler::ShouldTriggerDump(uint64_t current_memory_total) {
- // This function tries to detect peak memory usage as discussed in
- // https://goo.gl/0kOU4A.
-
- if (current_memory_total == 0)
- return false;
-
- bool should_dump = false;
- ++polling_state_.num_polls_from_last_dump;
- if (polling_state_.last_dump_memory_total == 0) {
- // If it's first sample then trigger memory dump.
- should_dump = true;
- } else if (polling_state_.min_polls_between_dumps >
- polling_state_.num_polls_from_last_dump) {
- return false;
- }
-
- int64_t increase_from_last_dump =
- current_memory_total - polling_state_.last_dump_memory_total;
- should_dump |=
- increase_from_last_dump > polling_state_.memory_increase_threshold;
- should_dump |= IsCurrentSamplePeak(current_memory_total);
- if (should_dump)
- polling_state_.ResetTotals();
- return should_dump;
-}
-
-bool MemoryDumpScheduler::IsCurrentSamplePeak(
- uint64_t current_memory_total_bytes) {
- uint64_t current_memory_total_kb = current_memory_total_bytes / 1024;
- polling_state_.last_memory_totals_kb_index =
- (polling_state_.last_memory_totals_kb_index + 1) %
- PollingTriggerState::kMaxNumMemorySamples;
- uint64_t mean = 0;
- for (uint32_t i = 0; i < PollingTriggerState::kMaxNumMemorySamples; ++i) {
- if (polling_state_.last_memory_totals_kb[i] == 0) {
- // Not enough samples to detect peaks.
- polling_state_
- .last_memory_totals_kb[polling_state_.last_memory_totals_kb_index] =
- current_memory_total_kb;
- return false;
- }
- mean += polling_state_.last_memory_totals_kb[i];
- }
- mean = mean / PollingTriggerState::kMaxNumMemorySamples;
- uint64_t variance = 0;
- for (uint32_t i = 0; i < PollingTriggerState::kMaxNumMemorySamples; ++i) {
- variance += (polling_state_.last_memory_totals_kb[i] - mean) *
- (polling_state_.last_memory_totals_kb[i] - mean);
- }
- variance = variance / PollingTriggerState::kMaxNumMemorySamples;
-
- polling_state_
- .last_memory_totals_kb[polling_state_.last_memory_totals_kb_index] =
- current_memory_total_kb;
-
- // If stddev is less than 0.2% then we consider that the process is inactive.
- bool is_stddev_low = variance < mean / 500 * mean / 500;
- if (is_stddev_low)
- return false;
-
- // (mean + 3.69 * stddev) corresponds to a value that is higher than current
- // sample with 99.99% probability.
- return (current_memory_total_kb - mean) * (current_memory_total_kb - mean) >
- (3.69 * 3.69 * variance);
-}
-
-MemoryDumpScheduler::PeriodicTriggerState::PeriodicTriggerState()
- : is_configured(false),
- dump_count(0),
- min_timer_period_ms(std::numeric_limits<uint32_t>::max()),
- light_dumps_rate(0),
- heavy_dumps_rate(0),
- light_dump_period_ms(0),
- heavy_dump_period_ms(0) {}
-
-MemoryDumpScheduler::PeriodicTriggerState::~PeriodicTriggerState() {
- DCHECK(!timer.IsRunning());
-}
-
-MemoryDumpScheduler::PollingTriggerState::PollingTriggerState(
- scoped_refptr<SingleThreadTaskRunner> polling_task_runner)
- : current_state(DISABLED),
- level_of_detail(MemoryDumpLevelOfDetail::FIRST),
- polling_task_runner(polling_task_runner),
- polling_interval_ms(g_polling_interval_ms_for_testing
- ? g_polling_interval_ms_for_testing
- : kMemoryTotalsPollingInterval),
- min_polls_between_dumps(0),
- num_polls_from_last_dump(-1),
- last_dump_memory_total(0),
- memory_increase_threshold(0),
- last_memory_totals_kb_index(0) {}
-
-MemoryDumpScheduler::PollingTriggerState::~PollingTriggerState() {
- DCHECK(!polling_task_runner);
-}
-
-void MemoryDumpScheduler::PollingTriggerState::ResetTotals() {
- if (!memory_increase_threshold) {
- memory_increase_threshold = kDefaultMemoryIncreaseThreshold;
-#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
- defined(OS_ANDROID)
- // Set threshold to 1% of total system memory.
- SystemMemoryInfoKB meminfo;
- bool res = GetSystemMemoryInfo(&meminfo);
- if (res)
- memory_increase_threshold = (meminfo.total / 100) * 1024;
-#endif
- }
-
- // Update the |last_dump_memory_total|'s value from the totals if it's not
- // first poll.
- if (num_polls_from_last_dump >= 0 &&
- last_memory_totals_kb[last_memory_totals_kb_index]) {
- last_dump_memory_total =
- last_memory_totals_kb[last_memory_totals_kb_index] * 1024;
- }
- num_polls_from_last_dump = 0;
- for (uint32_t i = 0; i < kMaxNumMemorySamples; ++i)
- last_memory_totals_kb[i] = 0;
- last_memory_totals_kb_index = 0;
-}
-
-} // namespace trace_event
-} // namespace base
diff --git a/base/trace_event/memory_dump_scheduler.h b/base/trace_event/memory_dump_scheduler.h
deleted file mode 100644
index fd21fce834..0000000000
--- a/base/trace_event/memory_dump_scheduler.h
+++ /dev/null
@@ -1,141 +0,0 @@
-// Copyright 2017 The Chromium 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_TRACE_EVENT_MEMORY_DUMP_SCHEDULER_H
-#define BASE_TRACE_EVENT_MEMORY_DUMP_SCHEDULER_H
-
-#include "base/base_export.h"
-#include "base/gtest_prod_util.h"
-#include "base/memory/ref_counted.h"
-#include "base/timer/timer.h"
-#include "base/trace_event/memory_dump_request_args.h"
-
-namespace base {
-class SingleThreadTaskRunner;
-
-namespace trace_event {
-
-class MemoryDumpManager;
-
-// Schedules global dump requests based on the triggers added.
-class BASE_EXPORT MemoryDumpScheduler {
- public:
- MemoryDumpScheduler(
- MemoryDumpManager* mdm_,
- scoped_refptr<SingleThreadTaskRunner> polling_task_runner);
- ~MemoryDumpScheduler();
-
- // Adds triggers for scheduling global dumps. Both periodic and peak triggers
- // cannot be added together. At the moment the periodic support is limited to
- // at most one periodic trigger per dump mode and peak triggers are limited to
- // at most one. All intervals should be an integeral multiple of the smallest
- // interval specified.
- void AddTrigger(MemoryDumpType trigger_type,
- MemoryDumpLevelOfDetail level_of_detail,
- uint32_t min_time_between_dumps_ms);
-
- // Starts periodic dumps.
- void NotifyPeriodicTriggerSupported();
-
- // Starts polling memory total.
- void NotifyPollingSupported();
-
- // Resets time for triggering dump to account for minimum time between the
- // dumps.
- void NotifyDumpTriggered();
-
- // Disables all triggers.
- void DisableAllTriggers();
-
- private:
- friend class MemoryDumpManagerTest;
- FRIEND_TEST_ALL_PREFIXES(MemoryDumpManagerTest, TestPollingOnDumpThread);
-
- // Helper class to schdule periodic memory dumps.
- struct PeriodicTriggerState {
- PeriodicTriggerState();
- ~PeriodicTriggerState();
-
- bool is_configured;
-
- RepeatingTimer timer;
- uint32_t dump_count;
- uint32_t min_timer_period_ms;
- uint32_t light_dumps_rate;
- uint32_t heavy_dumps_rate;
-
- uint32_t light_dump_period_ms;
- uint32_t heavy_dump_period_ms;
-
- DISALLOW_COPY_AND_ASSIGN(PeriodicTriggerState);
- };
-
- struct PollingTriggerState {
- enum State {
- CONFIGURED, // Polling trigger was added.
- ENABLED, // Polling is running.
- DISABLED // Polling is disabled.
- };
-
- static const uint32_t kMaxNumMemorySamples = 50;
-
- explicit PollingTriggerState(
- scoped_refptr<SingleThreadTaskRunner> polling_task_runner);
- ~PollingTriggerState();
-
- // Helper to clear the tracked memory totals and poll count from last dump.
- void ResetTotals();
-
- State current_state;
- MemoryDumpLevelOfDetail level_of_detail;
-
- scoped_refptr<SingleThreadTaskRunner> polling_task_runner;
- uint32_t polling_interval_ms;
-
- // Minimum numer of polls after the last dump at which next dump can be
- // triggered.
- int min_polls_between_dumps;
- int num_polls_from_last_dump;
-
- uint64_t last_dump_memory_total;
- int64_t memory_increase_threshold;
- uint64_t last_memory_totals_kb[kMaxNumMemorySamples];
- uint32_t last_memory_totals_kb_index;
-
- DISALLOW_COPY_AND_ASSIGN(PollingTriggerState);
- };
-
- // Helper to set polling disabled on the polling thread.
- void DisablePolling();
-
- // Periodically called by the timer.
- void RequestPeriodicGlobalDump();
-
- // Called for polling memory usage and trigger dumps if peak is detected.
- void PollMemoryOnPollingThread();
-
- // Returns true if peak memory value is detected.
- bool ShouldTriggerDump(uint64_t current_memory_total);
-
- // Helper to detect peaks in memory usage.
- bool IsCurrentSamplePeak(uint64_t current_memory_total);
-
- // Must be set before enabling tracing.
- static void SetPollingIntervalForTesting(uint32_t interval);
-
- // True if periodic dumping is enabled.
- bool IsPeriodicTimerRunningForTesting();
-
- MemoryDumpManager* mdm_;
-
- PeriodicTriggerState periodic_state_;
- PollingTriggerState polling_state_;
-
- DISALLOW_COPY_AND_ASSIGN(MemoryDumpScheduler);
-};
-
-} // namespace trace_event
-} // namespace base
-
-#endif // BASE_TRACE_EVENT_MEMORY_DUMP_SCHEDULER_H
diff --git a/base/trace_event/memory_dump_session_state.cc b/base/trace_event/memory_dump_session_state.cc
index d26b82a5b7..b3d9a8ccfc 100644
--- a/base/trace_event/memory_dump_session_state.cc
+++ b/base/trace_event/memory_dump_session_state.cc
@@ -7,8 +7,8 @@
namespace base {
namespace trace_event {
-MemoryDumpSessionState::MemoryDumpSessionState()
- : heap_profiler_breakdown_threshold_bytes_(0) {}
+MemoryDumpSessionState::MemoryDumpSessionState() {}
+
MemoryDumpSessionState::~MemoryDumpSessionState() {}
void MemoryDumpSessionState::SetStackFrameDeduplicator(
@@ -23,14 +23,9 @@ void MemoryDumpSessionState::SetTypeNameDeduplicator(
type_name_deduplicator_ = std::move(type_name_deduplicator);
}
-void MemoryDumpSessionState::SetAllowedDumpModes(
- std::set<MemoryDumpLevelOfDetail> allowed_dump_modes) {
- allowed_dump_modes_ = allowed_dump_modes;
-}
-
-bool MemoryDumpSessionState::IsDumpModeAllowed(
- MemoryDumpLevelOfDetail dump_mode) const {
- return allowed_dump_modes_.count(dump_mode) != 0;
+void MemoryDumpSessionState::SetMemoryDumpConfig(
+ const TraceConfig::MemoryDumpConfig& config) {
+ memory_dump_config_ = config;
}
} // namespace trace_event
diff --git a/base/trace_event/memory_dump_session_state.h b/base/trace_event/memory_dump_session_state.h
index 46092cb483..f199ec1a2f 100644
--- a/base/trace_event/memory_dump_session_state.h
+++ b/base/trace_event/memory_dump_session_state.h
@@ -6,12 +6,11 @@
#define BASE_TRACE_EVENT_MEMORY_DUMP_SESSION_STATE_H_
#include <memory>
-#include <set>
#include "base/base_export.h"
#include "base/trace_event/heap_profiler_stack_frame_deduplicator.h"
#include "base/trace_event/heap_profiler_type_name_deduplicator.h"
-#include "base/trace_event/memory_dump_request_args.h"
+#include "base/trace_event/trace_config.h"
namespace base {
namespace trace_event {
@@ -41,18 +40,11 @@ class BASE_EXPORT MemoryDumpSessionState
void SetTypeNameDeduplicator(
std::unique_ptr<TypeNameDeduplicator> type_name_deduplicator);
- void SetAllowedDumpModes(
- std::set<MemoryDumpLevelOfDetail> allowed_dump_modes);
-
- bool IsDumpModeAllowed(MemoryDumpLevelOfDetail dump_mode) const;
-
- void set_heap_profiler_breakdown_threshold_bytes(uint32_t value) {
- heap_profiler_breakdown_threshold_bytes_ = value;
+ const TraceConfig::MemoryDumpConfig& memory_dump_config() const {
+ return memory_dump_config_;
}
- uint32_t heap_profiler_breakdown_threshold_bytes() const {
- return heap_profiler_breakdown_threshold_bytes_;
- }
+ void SetMemoryDumpConfig(const TraceConfig::MemoryDumpConfig& config);
private:
friend class RefCountedThreadSafe<MemoryDumpSessionState>;
@@ -66,9 +58,9 @@ class BASE_EXPORT MemoryDumpSessionState
// trace is finalized.
std::unique_ptr<TypeNameDeduplicator> type_name_deduplicator_;
- std::set<MemoryDumpLevelOfDetail> allowed_dump_modes_;
-
- uint32_t heap_profiler_breakdown_threshold_bytes_;
+ // The memory dump config, copied at the time when the tracing session was
+ // started.
+ TraceConfig::MemoryDumpConfig memory_dump_config_;
};
} // namespace trace_event
diff --git a/base/trace_event/memory_infra_background_whitelist.cc b/base/trace_event/memory_infra_background_whitelist.cc
index ae74322040..aed187fa1d 100644
--- a/base/trace_event/memory_infra_background_whitelist.cc
+++ b/base/trace_event/memory_infra_background_whitelist.cc
@@ -17,26 +17,20 @@ namespace {
// providers can be added here only if the background mode dump has very
// less performance and memory overhead.
const char* const kDumpProviderWhitelist[] = {
- "android::ResourceManagerImpl",
"BlinkGC",
- "ClientDiscardableSharedMemoryManager",
+ "ChildDiscardableSharedMemoryManager",
"DOMStorage",
- "DiscardableSharedMemoryManager",
+ "HostDiscardableSharedMemoryManager",
"IndexedDBBackingStore",
"JavaHeap",
- "LevelDB",
"LeveldbValueStore",
"Malloc",
- "MemoryCache",
"PartitionAlloc",
"ProcessMemoryMetrics",
"Skia",
"Sql",
- "URLRequestContext",
"V8Isolate",
"WinHeap",
- "SyncDirectory",
- "TabRestoreServiceHelper",
nullptr // End of list marker.
};
@@ -52,7 +46,6 @@ const char* const kAllocatorDumpNameWhitelist[] = {
"java_heap",
"java_heap/allocated_objects",
"leveldb/index_db/0x?",
- "leveldb/leveldb_proto/0x?",
"leveldb/value_store/Extensions.Database.Open.Settings/0x?",
"leveldb/value_store/Extensions.Database.Open.Rules/0x?",
"leveldb/value_store/Extensions.Database.Open.State/0x?",
@@ -62,33 +55,14 @@ const char* const kAllocatorDumpNameWhitelist[] = {
"malloc",
"malloc/allocated_objects",
"malloc/metadata_fragmentation_caches",
- "net/http_network_session_0x?",
- "net/http_network_session_0x?/quic_stream_factory",
- "net/http_network_session_0x?/socket_pool",
- "net/http_network_session_0x?/spdy_session_pool",
- "net/http_network_session_0x?/stream_factory",
- "net/sdch_manager_0x?",
- "net/ssl_session_cache",
- "net/url_request_context_0x?",
- "net/url_request_context_0x?/http_cache",
- "net/url_request_context_0x?/http_network_session",
- "net/url_request_context_0x?/sdch_manager",
- "web_cache/Image_resources",
- "web_cache/CSS stylesheet_resources",
- "web_cache/Script_resources",
- "web_cache/XSL stylesheet_resources",
- "web_cache/Font_resources",
- "web_cache/Other_resources",
"partition_alloc/allocated_objects",
"partition_alloc/partitions",
- "partition_alloc/partitions/array_buffer",
"partition_alloc/partitions/buffer",
"partition_alloc/partitions/fast_malloc",
"partition_alloc/partitions/layout",
"skia/sk_glyph_cache",
"skia/sk_resource_cache",
"sqlite",
- "ui/resource_manager_0x?",
"v8/isolate_0x?/heap_spaces",
"v8/isolate_0x?/heap_spaces/code_space",
"v8/isolate_0x?/heap_spaces/large_object_space",
@@ -100,47 +74,6 @@ const char* const kAllocatorDumpNameWhitelist[] = {
"v8/isolate_0x?/zapped_for_debug",
"winheap",
"winheap/allocated_objects",
- "sync/0x?/kernel",
- "sync/0x?/store",
- "sync/0x?/model_type/APP",
- "sync/0x?/model_type/APP_LIST",
- "sync/0x?/model_type/APP_NOTIFICATION",
- "sync/0x?/model_type/APP_SETTING",
- "sync/0x?/model_type/ARC_PACKAGE",
- "sync/0x?/model_type/ARTICLE",
- "sync/0x?/model_type/AUTOFILL",
- "sync/0x?/model_type/AUTOFILL_PROFILE",
- "sync/0x?/model_type/AUTOFILL_WALLET",
- "sync/0x?/model_type/BOOKMARK",
- "sync/0x?/model_type/DEVICE_INFO",
- "sync/0x?/model_type/DICTIONARY",
- "sync/0x?/model_type/EXPERIMENTS",
- "sync/0x?/model_type/EXTENSION",
- "sync/0x?/model_type/EXTENSION_SETTING",
- "sync/0x?/model_type/FAVICON_IMAGE",
- "sync/0x?/model_type/FAVICON_TRACKING",
- "sync/0x?/model_type/HISTORY_DELETE_DIRECTIVE",
- "sync/0x?/model_type/MANAGED_USER",
- "sync/0x?/model_type/MANAGED_USER_SETTING",
- "sync/0x?/model_type/MANAGED_USER_SHARED_SETTING",
- "sync/0x?/model_type/MANAGED_USER_WHITELIST",
- "sync/0x?/model_type/NIGORI",
- "sync/0x?/model_type/PASSWORD",
- "sync/0x?/model_type/PREFERENCE",
- "sync/0x?/model_type/PRINTER",
- "sync/0x?/model_type/PRIORITY_PREFERENCE",
- "sync/0x?/model_type/READING_LIST",
- "sync/0x?/model_type/SEARCH_ENGINE",
- "sync/0x?/model_type/SESSION",
- "sync/0x?/model_type/SYNCED_NOTIFICATION",
- "sync/0x?/model_type/SYNCED_NOTIFICATION_APP_INFO",
- "sync/0x?/model_type/THEME",
- "sync/0x?/model_type/TYPED_URL",
- "sync/0x?/model_type/WALLET_METADATA",
- "sync/0x?/model_type/WIFI_CREDENTIAL",
- "tab_restore/service_helper_0x?/entries",
- "tab_restore/service_helper_0x?/entries/tab_0x?",
- "tab_restore/service_helper_0x?/entries/window_0x?",
nullptr // End of list marker.
};
diff --git a/base/trace_event/memory_usage_estimator.cc b/base/trace_event/memory_usage_estimator.cc
deleted file mode 100644
index c769d5b6f1..0000000000
--- a/base/trace_event/memory_usage_estimator.cc
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2016 The Chromium 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/trace_event/memory_usage_estimator.h"
-
-namespace base {
-namespace trace_event {
-
-template size_t EstimateMemoryUsage(const std::string&);
-template size_t EstimateMemoryUsage(const string16&);
-
-} // namespace trace_event
-} // namespace base
diff --git a/base/trace_event/memory_usage_estimator.h b/base/trace_event/memory_usage_estimator.h
deleted file mode 100644
index db4ea6956c..0000000000
--- a/base/trace_event/memory_usage_estimator.h
+++ /dev/null
@@ -1,549 +0,0 @@
-// Copyright 2016 The Chromium 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_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_
-#define BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_
-
-#include <stdint.h>
-
-#include <array>
-#include <deque>
-#include <list>
-#include <map>
-#include <memory>
-#include <queue>
-#include <set>
-#include <stack>
-#include <string>
-#include <type_traits>
-#include <unordered_map>
-#include <unordered_set>
-#include <vector>
-
-#include "base/base_export.h"
-#include "base/containers/linked_list.h"
-#include "base/strings/string16.h"
-#include "base/template_util.h"
-
-// Composable memory usage estimators.
-//
-// This file defines set of EstimateMemoryUsage(object) functions that return
-// approximate memory usage of their argument.
-//
-// The ultimate goal is to make memory usage estimation for a class simply a
-// matter of aggregating EstimateMemoryUsage() results over all fields.
-//
-// That is achieved via composability: if EstimateMemoryUsage() is defined
-// for T then EstimateMemoryUsage() is also defined for any combination of
-// containers holding T (e.g. std::map<int, std::vector<T>>).
-//
-// There are two ways of defining EstimateMemoryUsage() for a type:
-//
-// 1. As a global function 'size_t EstimateMemoryUsage(T)' in
-// in base::trace_event namespace.
-//
-// 2. As 'size_t T::EstimateMemoryUsage() const' method. In this case
-// EstimateMemoryUsage(T) function in base::trace_event namespace is
-// provided automatically.
-//
-// Here is an example implementation:
-//
-// size_t foo::bar::MyClass::EstimateMemoryUsage() const {
-// return base::trace_event::EstimateMemoryUsage(name_) +
-// base::trace_event::EstimateMemoryUsage(id_) +
-// base::trace_event::EstimateMemoryUsage(items_);
-// }
-//
-// The approach is simple: first call EstimateMemoryUsage() on all members,
-// then recursively fix compilation errors that are caused by types not
-// implementing EstimateMemoryUsage().
-
-namespace base {
-namespace trace_event {
-
-// Declarations
-
-// If T declares 'EstimateMemoryUsage() const' member function, then
-// global function EstimateMemoryUsage(T) is available, and just calls
-// the member function.
-template <class T>
-auto EstimateMemoryUsage(const T& object)
- -> decltype(object.EstimateMemoryUsage());
-
-// String
-
-template <class C, class T, class A>
-size_t EstimateMemoryUsage(const std::basic_string<C, T, A>& string);
-
-// Arrays
-
-template <class T, size_t N>
-size_t EstimateMemoryUsage(const std::array<T, N>& array);
-
-template <class T, size_t N>
-size_t EstimateMemoryUsage(T (&array)[N]);
-
-template <class T>
-size_t EstimateMemoryUsage(const T* array, size_t array_length);
-
-// std::unique_ptr
-
-template <class T, class D>
-size_t EstimateMemoryUsage(const std::unique_ptr<T, D>& ptr);
-
-template <class T, class D>
-size_t EstimateMemoryUsage(const std::unique_ptr<T[], D>& array,
- size_t array_length);
-
-// std::shared_ptr
-
-template <class T>
-size_t EstimateMemoryUsage(const std::shared_ptr<T>& ptr);
-
-// Containers
-
-template <class F, class S>
-size_t EstimateMemoryUsage(const std::pair<F, S>& pair);
-
-template <class T, class A>
-size_t EstimateMemoryUsage(const std::vector<T, A>& vector);
-
-template <class T, class A>
-size_t EstimateMemoryUsage(const std::list<T, A>& list);
-
-template <class T>
-size_t EstimateMemoryUsage(const base::LinkedList<T>& list);
-
-template <class T, class C, class A>
-size_t EstimateMemoryUsage(const std::set<T, C, A>& set);
-
-template <class T, class C, class A>
-size_t EstimateMemoryUsage(const std::multiset<T, C, A>& set);
-
-template <class K, class V, class C, class A>
-size_t EstimateMemoryUsage(const std::map<K, V, C, A>& map);
-
-template <class K, class V, class C, class A>
-size_t EstimateMemoryUsage(const std::multimap<K, V, C, A>& map);
-
-template <class T, class H, class KE, class A>
-size_t EstimateMemoryUsage(const std::unordered_set<T, H, KE, A>& set);
-
-template <class T, class H, class KE, class A>
-size_t EstimateMemoryUsage(const std::unordered_multiset<T, H, KE, A>& set);
-
-template <class K, class V, class H, class KE, class A>
-size_t EstimateMemoryUsage(const std::unordered_map<K, V, H, KE, A>& map);
-
-template <class K, class V, class H, class KE, class A>
-size_t EstimateMemoryUsage(const std::unordered_multimap<K, V, H, KE, A>& map);
-
-template <class T, class A>
-size_t EstimateMemoryUsage(const std::deque<T, A>& deque);
-
-template <class T, class C>
-size_t EstimateMemoryUsage(const std::queue<T, C>& queue);
-
-template <class T, class C>
-size_t EstimateMemoryUsage(const std::priority_queue<T, C>& queue);
-
-template <class T, class C>
-size_t EstimateMemoryUsage(const std::stack<T, C>& stack);
-
-// TODO(dskiba):
-// std::forward_list
-
-// Definitions
-
-namespace internal {
-
-// HasEMU<T>::value is true iff EstimateMemoryUsage(T) is available.
-// (This is the default version, which is false.)
-template <class T, class X = void>
-struct HasEMU : std::false_type {};
-
-// This HasEMU specialization is only picked up if there exists function
-// EstimateMemoryUsage(const T&) that returns size_t. Simpler ways to
-// achieve this don't work on MSVC.
-template <class T>
-struct HasEMU<
- T,
- typename std::enable_if<std::is_same<
- size_t,
- decltype(EstimateMemoryUsage(std::declval<const T&>()))>::value>::type>
- : std::true_type {};
-
-// EMUCaller<T> does three things:
-// 1. Defines Call() method that calls EstimateMemoryUsage(T) if it's
-// available.
-// 2. If EstimateMemoryUsage(T) is not available, but T has trivial dtor
-// (i.e. it's POD, integer, pointer, enum, etc.) then it defines Call()
-// method that returns 0. This is useful for containers, which allocate
-// memory regardless of T (also for cases like std::map<int, MyClass>).
-// 3. Finally, if EstimateMemoryUsage(T) is not available, then it triggers
-// a static_assert with a helpful message. That cuts numbers of errors
-// considerably - if you just call EstimateMemoryUsage(T) but it's not
-// available for T, then compiler will helpfully list *all* possible
-// variants of it, with an explanation for each.
-template <class T, class X = void>
-struct EMUCaller {
- // std::is_same<> below makes static_assert depend on T, in order to
- // prevent it from asserting regardless instantiation.
- static_assert(std::is_same<T, std::false_type>::value,
- "Neither global function 'size_t EstimateMemoryUsage(T)' "
- "nor member function 'size_t T::EstimateMemoryUsage() const' "
- "is defined for the type.");
-
- static size_t Call(const T&) { return 0; }
-};
-
-template <class T>
-struct EMUCaller<T, typename std::enable_if<HasEMU<T>::value>::type> {
- static size_t Call(const T& value) { return EstimateMemoryUsage(value); }
-};
-
-template <class T>
-struct EMUCaller<
- T,
- typename std::enable_if<!HasEMU<T>::value &&
- is_trivially_destructible<T>::value>::type> {
- static size_t Call(const T&) { return 0; }
-};
-
-// Returns reference to the underlying container of a container adapter.
-// Works for std::stack, std::queue and std::priority_queue.
-template <class A>
-const typename A::container_type& GetUnderlyingContainer(const A& adapter) {
- struct ExposedAdapter : A {
- using A::c;
- };
- return adapter.*&ExposedAdapter::c;
-}
-
-} // namespace internal
-
-// Proxy that deducts T and calls EMUCaller<T>.
-// To be used by EstimateMemoryUsage() implementations for containers.
-template <class T>
-size_t EstimateItemMemoryUsage(const T& value) {
- return internal::EMUCaller<T>::Call(value);
-}
-
-template <class I>
-size_t EstimateIterableMemoryUsage(const I& iterable) {
- size_t memory_usage = 0;
- for (const auto& item : iterable) {
- memory_usage += EstimateItemMemoryUsage(item);
- }
- return memory_usage;
-}
-
-// Global EstimateMemoryUsage(T) that just calls T::EstimateMemoryUsage().
-template <class T>
-auto EstimateMemoryUsage(const T& object)
- -> decltype(object.EstimateMemoryUsage()) {
- static_assert(
- std::is_same<decltype(object.EstimateMemoryUsage()), size_t>::value,
- "'T::EstimateMemoryUsage() const' must return size_t.");
- return object.EstimateMemoryUsage();
-}
-
-// String
-
-template <class C, class T, class A>
-size_t EstimateMemoryUsage(const std::basic_string<C, T, A>& string) {
- using string_type = std::basic_string<C, T, A>;
- using value_type = typename string_type::value_type;
- // C++11 doesn't leave much room for implementors - std::string can
- // use short string optimization, but that's about it. We detect SSO
- // by checking that c_str() points inside |string|.
- const uint8_t* cstr = reinterpret_cast<const uint8_t*>(string.c_str());
- const uint8_t* inline_cstr = reinterpret_cast<const uint8_t*>(&string);
- if (cstr >= inline_cstr && cstr < inline_cstr + sizeof(string)) {
- // SSO string
- return 0;
- }
- return (string.capacity() + 1) * sizeof(value_type);
-}
-
-// Use explicit instantiations from the .cc file (reduces bloat).
-extern template BASE_EXPORT size_t EstimateMemoryUsage(const std::string&);
-extern template BASE_EXPORT size_t EstimateMemoryUsage(const string16&);
-
-// Arrays
-
-template <class T, size_t N>
-size_t EstimateMemoryUsage(const std::array<T, N>& array) {
- return EstimateIterableMemoryUsage(array);
-}
-
-template <class T, size_t N>
-size_t EstimateMemoryUsage(T (&array)[N]) {
- return EstimateIterableMemoryUsage(array);
-}
-
-template <class T>
-size_t EstimateMemoryUsage(const T* array, size_t array_length) {
- size_t memory_usage = sizeof(T) * array_length;
- for (size_t i = 0; i != array_length; ++i) {
- memory_usage += EstimateItemMemoryUsage(array[i]);
- }
- return memory_usage;
-}
-
-// std::unique_ptr
-
-template <class T, class D>
-size_t EstimateMemoryUsage(const std::unique_ptr<T, D>& ptr) {
- return ptr ? (sizeof(T) + EstimateItemMemoryUsage(*ptr)) : 0;
-}
-
-template <class T, class D>
-size_t EstimateMemoryUsage(const std::unique_ptr<T[], D>& array,
- size_t array_length) {
- return EstimateMemoryUsage(array.get(), array_length);
-}
-
-// std::shared_ptr
-
-template <class T>
-size_t EstimateMemoryUsage(const std::shared_ptr<T>& ptr) {
- auto use_count = ptr.use_count();
- if (use_count == 0) {
- return 0;
- }
- // Model shared_ptr after libc++,
- // see __shared_ptr_pointer from include/memory
- struct SharedPointer {
- void* vtbl;
- long shared_owners;
- long shared_weak_owners;
- T* value;
- };
- // If object of size S shared N > S times we prefer to (potentially)
- // overestimate than to return 0.
- return sizeof(SharedPointer) +
- (EstimateItemMemoryUsage(*ptr) + (use_count - 1)) / use_count;
-}
-
-// std::pair
-
-template <class F, class S>
-size_t EstimateMemoryUsage(const std::pair<F, S>& pair) {
- return EstimateItemMemoryUsage(pair.first) +
- EstimateItemMemoryUsage(pair.second);
-}
-
-// std::vector
-
-template <class T, class A>
-size_t EstimateMemoryUsage(const std::vector<T, A>& vector) {
- return sizeof(T) * vector.capacity() + EstimateIterableMemoryUsage(vector);
-}
-
-// std::list
-
-template <class T, class A>
-size_t EstimateMemoryUsage(const std::list<T, A>& list) {
- using value_type = typename std::list<T, A>::value_type;
- struct Node {
- Node* prev;
- Node* next;
- value_type value;
- };
- return sizeof(Node) * list.size() +
- EstimateIterableMemoryUsage(list);
-}
-
-template <class T>
-size_t EstimateMemoryUsage(const base::LinkedList<T>& list) {
- size_t memory_usage = 0u;
- for (base::LinkNode<T>* node = list.head(); node != list.end();
- node = node->next()) {
- // Since we increment by calling node = node->next() we know that node
- // isn't nullptr.
- memory_usage += EstimateMemoryUsage(*node->value()) + sizeof(T);
- }
- return memory_usage;
-}
-
-// Tree containers
-
-template <class V>
-size_t EstimateTreeMemoryUsage(size_t size) {
- // Tree containers are modeled after libc++
- // (__tree_node from include/__tree)
- struct Node {
- Node* left;
- Node* right;
- Node* parent;
- bool is_black;
- V value;
- };
- return sizeof(Node) * size;
-}
-
-template <class T, class C, class A>
-size_t EstimateMemoryUsage(const std::set<T, C, A>& set) {
- using value_type = typename std::set<T, C, A>::value_type;
- return EstimateTreeMemoryUsage<value_type>(set.size()) +
- EstimateIterableMemoryUsage(set);
-}
-
-template <class T, class C, class A>
-size_t EstimateMemoryUsage(const std::multiset<T, C, A>& set) {
- using value_type = typename std::multiset<T, C, A>::value_type;
- return EstimateTreeMemoryUsage<value_type>(set.size()) +
- EstimateIterableMemoryUsage(set);
-}
-
-template <class K, class V, class C, class A>
-size_t EstimateMemoryUsage(const std::map<K, V, C, A>& map) {
- using value_type = typename std::map<K, V, C, A>::value_type;
- return EstimateTreeMemoryUsage<value_type>(map.size()) +
- EstimateIterableMemoryUsage(map);
-}
-
-template <class K, class V, class C, class A>
-size_t EstimateMemoryUsage(const std::multimap<K, V, C, A>& map) {
- using value_type = typename std::multimap<K, V, C, A>::value_type;
- return EstimateTreeMemoryUsage<value_type>(map.size()) +
- EstimateIterableMemoryUsage(map);
-}
-
-// HashMap containers
-
-namespace internal {
-
-// While hashtable containers model doesn't depend on STL implementation, one
-// detail still crept in: bucket_count. It's used in size estimation, but its
-// value after inserting N items is not predictable.
-// This function is specialized by unittests to return constant value, thus
-// excluding bucket_count from testing.
-template <class V>
-size_t HashMapBucketCountForTesting(size_t bucket_count) {
- return bucket_count;
-}
-
-} // namespace internal
-
-template <class V>
-size_t EstimateHashMapMemoryUsage(size_t bucket_count, size_t size) {
- // Hashtable containers are modeled after libc++
- // (__hash_node from include/__hash_table)
- struct Node {
- void* next;
- size_t hash;
- V value;
- };
- using Bucket = void*;
- bucket_count = internal::HashMapBucketCountForTesting<V>(bucket_count);
- return sizeof(Bucket) * bucket_count + sizeof(Node) * size;
-}
-
-template <class K, class H, class KE, class A>
-size_t EstimateMemoryUsage(const std::unordered_set<K, H, KE, A>& set) {
- using value_type = typename std::unordered_set<K, H, KE, A>::value_type;
- return EstimateHashMapMemoryUsage<value_type>(set.bucket_count(),
- set.size()) +
- EstimateIterableMemoryUsage(set);
-}
-
-template <class K, class H, class KE, class A>
-size_t EstimateMemoryUsage(const std::unordered_multiset<K, H, KE, A>& set) {
- using value_type = typename std::unordered_multiset<K, H, KE, A>::value_type;
- return EstimateHashMapMemoryUsage<value_type>(set.bucket_count(),
- set.size()) +
- EstimateIterableMemoryUsage(set);
-}
-
-template <class K, class V, class H, class KE, class A>
-size_t EstimateMemoryUsage(const std::unordered_map<K, V, H, KE, A>& map) {
- using value_type = typename std::unordered_map<K, V, H, KE, A>::value_type;
- return EstimateHashMapMemoryUsage<value_type>(map.bucket_count(),
- map.size()) +
- EstimateIterableMemoryUsage(map);
-}
-
-template <class K, class V, class H, class KE, class A>
-size_t EstimateMemoryUsage(const std::unordered_multimap<K, V, H, KE, A>& map) {
- using value_type =
- typename std::unordered_multimap<K, V, H, KE, A>::value_type;
- return EstimateHashMapMemoryUsage<value_type>(map.bucket_count(),
- map.size()) +
- EstimateIterableMemoryUsage(map);
-}
-
-// std::deque
-
-template <class T, class A>
-size_t EstimateMemoryUsage(const std::deque<T, A>& deque) {
-// Since std::deque implementations are wildly different
-// (see crbug.com/674287), we can't have one "good enough"
-// way to estimate.
-
-// kBlockSize - minimum size of a block, in bytes
-// kMinBlockLength - number of elements in a block
-// if sizeof(T) > kBlockSize
-#if defined(_LIBCPP_VERSION)
- size_t kBlockSize = 4096;
- size_t kMinBlockLength = 16;
-#elif defined(__GLIBCXX__)
- size_t kBlockSize = 512;
- size_t kMinBlockLength = 1;
-#elif defined(_MSC_VER)
- size_t kBlockSize = 16;
- size_t kMinBlockLength = 1;
-#else
- size_t kBlockSize = 0;
- size_t kMinBlockLength = 1;
-#endif
-
- size_t block_length =
- (sizeof(T) > kBlockSize) ? kMinBlockLength : kBlockSize / sizeof(T);
-
- size_t blocks = (deque.size() + block_length - 1) / block_length;
-
-#if defined(__GLIBCXX__)
- // libstdc++: deque always has at least one block
- if (!blocks)
- blocks = 1;
-#endif
-
-#if defined(_LIBCPP_VERSION)
- // libc++: deque keeps at most two blocks when it shrinks,
- // so even if the size is zero, deque might be holding up
- // to 4096 * 2 bytes. One way to know whether deque has
- // ever allocated (and hence has 1 or 2 blocks) is to check
- // iterator's pointer. Non-zero value means that deque has
- // at least one block.
- if (!blocks && deque.begin().operator->())
- blocks = 1;
-#endif
-
- return (blocks * block_length * sizeof(T)) +
- EstimateIterableMemoryUsage(deque);
-}
-
-// Container adapters
-
-template <class T, class C>
-size_t EstimateMemoryUsage(const std::queue<T, C>& queue) {
- return EstimateMemoryUsage(internal::GetUnderlyingContainer(queue));
-}
-
-template <class T, class C>
-size_t EstimateMemoryUsage(const std::priority_queue<T, C>& queue) {
- return EstimateMemoryUsage(internal::GetUnderlyingContainer(queue));
-}
-
-template <class T, class C>
-size_t EstimateMemoryUsage(const std::stack<T, C>& stack) {
- return EstimateMemoryUsage(internal::GetUnderlyingContainer(stack));
-}
-
-} // namespace trace_event
-} // namespace base
-
-#endif // BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_
diff --git a/base/trace_event/memory_usage_estimator_unittest.cc b/base/trace_event/memory_usage_estimator_unittest.cc
deleted file mode 100644
index 80237c0192..0000000000
--- a/base/trace_event/memory_usage_estimator_unittest.cc
+++ /dev/null
@@ -1,244 +0,0 @@
-// Copyright 2016 The Chromium 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/trace_event/memory_usage_estimator.h"
-
-#include <stdlib.h>
-
-#include "base/memory/ptr_util.h"
-#include "base/strings/string16.h"
-#include "build/build_config.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-#if defined(ARCH_CPU_64_BITS)
-#define EXPECT_EQ_32_64(_, e, a) EXPECT_EQ(e, a)
-#else
-#define EXPECT_EQ_32_64(e, _, a) EXPECT_EQ(e, a)
-#endif
-
-namespace base {
-namespace trace_event {
-
-namespace {
-
-// Test class with predictable memory usage.
-class Data {
- public:
- explicit Data(size_t size = 17): size_(size) {
- }
-
- size_t size() const { return size_; }
-
- size_t EstimateMemoryUsage() const {
- return size_;
- }
-
- bool operator < (const Data& other) const {
- return size_ < other.size_;
- }
- bool operator == (const Data& other) const {
- return size_ == other.size_;
- }
-
- struct Hasher {
- size_t operator () (const Data& data) const {
- return data.size();
- }
- };
-
- private:
- size_t size_;
-};
-
-} // namespace
-
-namespace internal {
-
-// This kills variance of bucket_count across STL implementations.
-template <>
-size_t HashMapBucketCountForTesting<Data>(size_t) {
- return 10;
-}
-template <>
-size_t HashMapBucketCountForTesting<std::pair<const Data, short>>(size_t) {
- return 10;
-}
-
-} // namespace internal
-
-TEST(EstimateMemoryUsageTest, String) {
- std::string string(777, 'a');
- EXPECT_EQ(string.capacity() + 1, EstimateMemoryUsage(string));
-}
-
-TEST(EstimateMemoryUsageTest, String16) {
- string16 string(777, 'a');
- EXPECT_EQ(sizeof(char16) * (string.capacity() + 1),
- EstimateMemoryUsage(string));
-}
-
-TEST(EstimateMemoryUsageTest, Arrays) {
- // std::array
- {
- std::array<Data, 10> array;
- EXPECT_EQ(170u, EstimateMemoryUsage(array));
- }
-
- // T[N]
- {
- Data array[10];
- EXPECT_EQ(170u, EstimateMemoryUsage(array));
- }
-
- // C array
- {
- struct Item {
- char payload[10];
- };
- Item* array = new Item[7];
- EXPECT_EQ(70u, EstimateMemoryUsage(array, 7));
- delete[] array;
- }
-}
-
-TEST(EstimateMemoryUsageTest, UniquePtr) {
- // Empty
- {
- std::unique_ptr<Data> ptr;
- EXPECT_EQ(0u, EstimateMemoryUsage(ptr));
- }
-
- // Not empty
- {
- std::unique_ptr<Data> ptr(new Data());
- EXPECT_EQ_32_64(21u, 25u, EstimateMemoryUsage(ptr));
- }
-
- // With a pointer
- {
- std::unique_ptr<Data*> ptr(new Data*());
- EXPECT_EQ(sizeof(void*), EstimateMemoryUsage(ptr));
- }
-
- // With an array
- {
- struct Item {
- uint32_t payload[10];
- };
- std::unique_ptr<Item[]> ptr(new Item[7]);
- EXPECT_EQ(280u, EstimateMemoryUsage(ptr, 7));
- }
-}
-
-TEST(EstimateMemoryUsageTest, Vector) {
- std::vector<Data> vector;
- vector.reserve(1000);
-
- // For an empty vector we should return memory usage of its buffer
- size_t capacity = vector.capacity();
- size_t expected_size = capacity * sizeof(Data);
- EXPECT_EQ(expected_size, EstimateMemoryUsage(vector));
-
- // If vector is not empty, its size should also include memory usages
- // of all elements.
- for (size_t i = 0; i != capacity / 2; ++i) {
- vector.push_back(Data(i));
- expected_size += EstimateMemoryUsage(vector.back());
- }
- EXPECT_EQ(expected_size, EstimateMemoryUsage(vector));
-}
-
-TEST(EstimateMemoryUsageTest, List) {
- struct POD {
- short data;
- };
- std::list<POD> list;
- for (int i = 0; i != 1000; ++i) {
- list.push_back(POD());
- }
- EXPECT_EQ_32_64(12000u, 24000u, EstimateMemoryUsage(list));
-}
-
-TEST(EstimateMemoryUsageTest, Set) {
- std::set<std::pair<int, Data>> set;
- for (int i = 0; i != 1000; ++i) {
- set.insert({i, Data(i)});
- }
- EXPECT_EQ_32_64(523500u, 547500u, EstimateMemoryUsage(set));
-}
-
-TEST(EstimateMemoryUsageTest, MultiSet) {
- std::multiset<bool> set;
- for (int i = 0; i != 1000; ++i) {
- set.insert((i & 1) != 0);
- }
- EXPECT_EQ_32_64(16000u, 32000u, EstimateMemoryUsage(set));
-}
-
-TEST(EstimateMemoryUsageTest, Map) {
- std::map<Data, int> map;
- for (int i = 0; i != 1000; ++i) {
- map.insert({Data(i), i});
- }
- EXPECT_EQ_32_64(523500u, 547500u, EstimateMemoryUsage(map));
-}
-
-TEST(EstimateMemoryUsageTest, MultiMap) {
- std::multimap<char, Data> map;
- for (int i = 0; i != 1000; ++i) {
- map.insert({static_cast<char>(i), Data(i)});
- }
- EXPECT_EQ_32_64(523500u, 547500u, EstimateMemoryUsage(map));
-}
-
-TEST(EstimateMemoryUsageTest, UnorderedSet) {
- std::unordered_set<Data, Data::Hasher> set;
- for (int i = 0; i != 1000; ++i) {
- set.insert(Data(i));
- }
- EXPECT_EQ_32_64(511540u, 523580u, EstimateMemoryUsage(set));
-}
-
-TEST(EstimateMemoryUsageTest, UnorderedMultiSet) {
- std::unordered_multiset<Data, Data::Hasher> set;
- for (int i = 0; i != 500; ++i) {
- set.insert(Data(i));
- set.insert(Data(i));
- }
- EXPECT_EQ_32_64(261540u, 273580u, EstimateMemoryUsage(set));
-}
-
-TEST(EstimateMemoryUsageTest, UnorderedMap) {
- std::unordered_map<Data, short, Data::Hasher> map;
- for (int i = 0; i != 1000; ++i) {
- map.insert({Data(i), static_cast<short>(i)});
- }
- EXPECT_EQ_32_64(515540u, 531580u, EstimateMemoryUsage(map));
-}
-
-TEST(EstimateMemoryUsageTest, UnorderedMultiMap) {
- std::unordered_multimap<Data, short, Data::Hasher> map;
- for (int i = 0; i != 1000; ++i) {
- map.insert({Data(i), static_cast<short>(i)});
- }
- EXPECT_EQ_32_64(515540u, 531580u, EstimateMemoryUsage(map));
-}
-
-TEST(EstimateMemoryUsageTest, Deque) {
- std::deque<Data> deque;
-
- // Pick a large value so that platform-specific accounting
- // for deque's blocks is small compared to usage of all items.
- constexpr size_t kDataSize = 100000;
- for (int i = 0; i != 1500; ++i) {
- deque.push_back(Data(kDataSize));
- }
-
- // Compare against a reasonable minimum (i.e. no overhead).
- size_t min_expected_usage = deque.size() * (sizeof(Data) + kDataSize);
- EXPECT_LE(min_expected_usage, EstimateMemoryUsage(deque));
-}
-
-} // namespace trace_event
-} // namespace base
diff --git a/base/trace_event/process_memory_dump.cc b/base/trace_event/process_memory_dump.cc
index 63d1340e42..826989237b 100644
--- a/base/trace_event/process_memory_dump.cc
+++ b/base/trace_event/process_memory_dump.cc
@@ -18,7 +18,7 @@
#include "build/build_config.h"
#if defined(OS_IOS)
-#include <mach/vm_page_size.h>
+#include <sys/sysctl.h>
#endif
#if defined(OS_POSIX)
@@ -57,13 +57,19 @@ bool ProcessMemoryDump::is_black_hole_non_fatal_for_testing_ = false;
size_t ProcessMemoryDump::GetSystemPageSize() {
#if defined(OS_IOS)
// On iOS, getpagesize() returns the user page sizes, but for allocating
- // arrays for mincore(), kernel page sizes is needed. Use vm_kernel_page_size
- // as recommended by Apple, https://forums.developer.apple.com/thread/47532/.
- // Refer to http://crbug.com/542671 and Apple rdar://23651782
- return vm_kernel_page_size;
-#else
- return base::GetPageSize();
+ // arrays for mincore(), kernel page sizes is needed. sysctlbyname() should
+ // be used for this. Refer to crbug.com/542671 and Apple rdar://23651782
+ int pagesize;
+ size_t pagesize_len;
+ int status = sysctlbyname("vm.pagesize", NULL, &pagesize_len, nullptr, 0);
+ if (!status && pagesize_len == sizeof(pagesize)) {
+ if (!sysctlbyname("vm.pagesize", &pagesize, &pagesize_len, nullptr, 0))
+ return pagesize;
+ }
+ LOG(ERROR) << "sysctlbyname(\"vm.pagesize\") failed.";
+ // Falls back to getpagesize() although it may be wrong in certain cases.
#endif // defined(OS_IOS)
+ return base::GetPageSize();
}
// static
@@ -158,14 +164,14 @@ ProcessMemoryDump::~ProcessMemoryDump() {}
MemoryAllocatorDump* ProcessMemoryDump::CreateAllocatorDump(
const std::string& absolute_name) {
return AddAllocatorDumpInternal(
- MakeUnique<MemoryAllocatorDump>(absolute_name, this));
+ WrapUnique(new MemoryAllocatorDump(absolute_name, this)));
}
MemoryAllocatorDump* ProcessMemoryDump::CreateAllocatorDump(
const std::string& absolute_name,
const MemoryAllocatorDumpGuid& guid) {
return AddAllocatorDumpInternal(
- MakeUnique<MemoryAllocatorDump>(absolute_name, this, guid));
+ WrapUnique(new MemoryAllocatorDump(absolute_name, this, guid)));
}
MemoryAllocatorDump* ProcessMemoryDump::AddAllocatorDumpInternal(
diff --git a/base/trace_event/process_memory_dump.h b/base/trace_event/process_memory_dump.h
index 6f8d167273..d020c7d652 100644
--- a/base/trace_event/process_memory_dump.h
+++ b/base/trace_event/process_memory_dump.h
@@ -31,6 +31,7 @@
namespace base {
namespace trace_event {
+class MemoryDumpManager;
class MemoryDumpSessionState;
class TracedValue;
diff --git a/base/trace_event/trace_buffer.cc b/base/trace_event/trace_buffer.cc
index e26e9fd28f..d40f4302fe 100644
--- a/base/trace_event/trace_buffer.cc
+++ b/base/trace_event/trace_buffer.cc
@@ -168,8 +168,7 @@ class TraceBufferVector : public TraceBuffer {
// have to add the metadata events and flush thread-local buffers even if
// the buffer is full.
*index = chunks_.size();
- // Put nullptr in the slot of a in-flight chunk.
- chunks_.push_back(nullptr);
+ chunks_.push_back(NULL); // Put NULL in the slot of a in-flight chunk.
++in_flight_chunk_count_;
// + 1 because zero chunk_seq is not allowed.
return std::unique_ptr<TraceBufferChunk>(
@@ -182,7 +181,7 @@ class TraceBufferVector : public TraceBuffer {
DCHECK_LT(index, chunks_.size());
DCHECK(!chunks_[index]);
--in_flight_chunk_count_;
- chunks_[index] = std::move(chunk);
+ chunks_[index] = chunk.release();
}
bool IsFull() const override { return chunks_.size() >= max_chunks_; }
@@ -199,7 +198,7 @@ class TraceBufferVector : public TraceBuffer {
TraceEvent* GetEventByHandle(TraceEventHandle handle) override {
if (handle.chunk_index >= chunks_.size())
return NULL;
- TraceBufferChunk* chunk = chunks_[handle.chunk_index].get();
+ TraceBufferChunk* chunk = chunks_[handle.chunk_index];
if (!chunk || chunk->seq() != handle.chunk_seq)
return NULL;
return chunk->GetEventAt(handle.event_index);
@@ -208,7 +207,7 @@ class TraceBufferVector : public TraceBuffer {
const TraceBufferChunk* NextChunk() override {
while (current_iteration_index_ < chunks_.size()) {
// Skip in-flight chunks.
- const TraceBufferChunk* chunk = chunks_[current_iteration_index_++].get();
+ const TraceBufferChunk* chunk = chunks_[current_iteration_index_++];
if (chunk)
return chunk;
}
@@ -224,7 +223,7 @@ class TraceBufferVector : public TraceBuffer {
overhead->Add("TraceBufferVector", chunks_ptr_vector_allocated_size,
chunks_ptr_vector_resident_size);
for (size_t i = 0; i < chunks_.size(); ++i) {
- TraceBufferChunk* chunk = chunks_[i].get();
+ TraceBufferChunk* chunk = chunks_[i];
// Skip the in-flight (nullptr) chunks. They will be accounted by the
// per-thread-local dumpers, see ThreadLocalEventBuffer::OnMemoryDump.
if (chunk)
@@ -236,7 +235,7 @@ class TraceBufferVector : public TraceBuffer {
size_t in_flight_chunk_count_;
size_t current_iteration_index_;
size_t max_chunks_;
- std::vector<std::unique_ptr<TraceBufferChunk>> chunks_;
+ ScopedVector<TraceBufferChunk> chunks_;
DISALLOW_COPY_AND_ASSIGN(TraceBufferVector);
};
diff --git a/base/trace_event/trace_category.h b/base/trace_event/trace_category.h
deleted file mode 100644
index 5a7915ac03..0000000000
--- a/base/trace_event/trace_category.h
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2016 The Chromium 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_TRACE_EVENT_TRACE_CATEGORY_H_
-#define BASE_TRACE_EVENT_TRACE_CATEGORY_H_
-
-#include <stdint.h>
-
-namespace base {
-namespace trace_event {
-
-// Captures the state of an invidivual trace category. Nothing except tracing
-// internals (e.g., TraceLog) is supposed to have non-const Category pointers.
-struct TraceCategory {
- // The TRACE_EVENT macros should only use this value as a bool.
- // These enum values are effectively a public API and third_party projects
- // depend on their value. Hence, never remove or recycle existing bits, unless
- // you are sure that all the third-party projects that depend on this have
- // been updated.
- enum StateFlags : uint8_t {
- ENABLED_FOR_RECORDING = 1 << 0,
-
- // Not used anymore.
- DEPRECATED_ENABLED_FOR_MONITORING = 1 << 1,
- DEPRECATED_ENABLED_FOR_EVENT_CALLBACK = 1 << 2,
-
- ENABLED_FOR_ETW_EXPORT = 1 << 3,
- ENABLED_FOR_FILTERING = 1 << 4
- };
-
- static const TraceCategory* FromStatePtr(const uint8_t* state_ptr) {
- static_assert(
- offsetof(TraceCategory, state_) == 0,
- "|state_| must be the first field of the TraceCategory class.");
- return reinterpret_cast<const TraceCategory*>(state_ptr);
- }
-
- bool is_valid() const { return name_ != nullptr; }
- void set_name(const char* name) { name_ = name; }
- const char* name() const {
- DCHECK(is_valid());
- return name_;
- }
-
- // TODO(primiano): This is an intermediate solution to deal with the fact that
- // today TRACE_EVENT* macros cache the state ptr. They should just cache the
- // full TraceCategory ptr, which is immutable, and use these helper function
- // here. This will get rid of the need of this awkward ptr getter completely.
- const uint8_t* state_ptr() const {
- return const_cast<const uint8_t*>(&state_);
- }
-
- uint8_t state() const {
- return *const_cast<volatile const uint8_t*>(&state_);
- }
-
- bool is_enabled() const { return state() != 0; }
-
- void set_state(uint8_t state) {
- *const_cast<volatile uint8_t*>(&state_) = state;
- }
-
- void clear_state_flag(StateFlags flag) { set_state(state() & (~flag)); }
- void set_state_flag(StateFlags flag) { set_state(state() | flag); }
-
- uint32_t enabled_filters() const {
- return *const_cast<volatile const uint32_t*>(&enabled_filters_);
- }
-
- bool is_filter_enabled(size_t index) const {
- DCHECK(index < sizeof(enabled_filters_) * 8);
- return (enabled_filters() & (1 << index)) != 0;
- }
-
- void set_enabled_filters(uint32_t enabled_filters) {
- *const_cast<volatile uint32_t*>(&enabled_filters_) = enabled_filters;
- }
-
- void reset_for_testing() {
- set_state(0);
- set_enabled_filters(0);
- }
-
- // These fields should not be accessed directly, not even by tracing code.
- // The only reason why these are not private is because it makes it impossible
- // to have a global array of TraceCategory in category_registry.cc without
- // creating initializers. See discussion on goo.gl/qhZN94 and
- // crbug.com/{660967,660828}.
-
- // The enabled state. TRACE_EVENT* macros will capture events if any of the
- // flags here are set. Since TRACE_EVENTx macros are used in a lot of
- // fast-paths, accesses to this field are non-barriered and racy by design.
- // This field is mutated when starting/stopping tracing and we don't care
- // about missing some events.
- uint8_t state_;
-
- // When ENABLED_FOR_FILTERING is set, this contains a bitmap to the
- // coressponding filter (see event_filters.h).
- uint32_t enabled_filters_;
-
- // TraceCategory group names are long lived static strings.
- const char* name_;
-};
-
-} // namespace trace_event
-} // namespace base
-
-#endif // BASE_TRACE_EVENT_TRACE_CATEGORY_H_
diff --git a/base/trace_event/trace_config.cc b/base/trace_event/trace_config.cc
index 36de107bf8..b343ea00bc 100644
--- a/base/trace_event/trace_config.cc
+++ b/base/trace_event/trace_config.cc
@@ -30,11 +30,13 @@ const char kRecordUntilFull[] = "record-until-full";
const char kRecordContinuously[] = "record-continuously";
const char kRecordAsMuchAsPossible[] = "record-as-much-as-possible";
const char kTraceToConsole[] = "trace-to-console";
+const char kEnableSampling[] = "enable-sampling";
const char kEnableSystrace[] = "enable-systrace";
const char kEnableArgumentFilter[] = "enable-argument-filter";
// String parameters that can be used to parse the trace config string.
const char kRecordModeParam[] = "record_mode";
+const char kEnableSamplingParam[] = "enable_sampling";
const char kEnableSystraceParam[] = "enable_systrace";
const char kEnableArgumentFilterParam[] = "enable_argument_filter";
const char kIncludedCategoriesParam[] = "included_categories";
@@ -48,32 +50,24 @@ const char kSyntheticDelayCategoryFilterPrefix[] = "DELAY(";
const char kMemoryDumpConfigParam[] = "memory_dump_config";
const char kAllowedDumpModesParam[] = "allowed_dump_modes";
const char kTriggersParam[] = "triggers";
-const char kTriggerModeParam[] = "mode";
-const char kMinTimeBetweenDumps[] = "min_time_between_dumps_ms";
-const char kTriggerTypeParam[] = "type";
-const char kPeriodicIntervalLegacyParam[] = "periodic_interval_ms";
+const char kPeriodicIntervalParam[] = "periodic_interval_ms";
+const char kModeParam[] = "mode";
const char kHeapProfilerOptions[] = "heap_profiler_options";
const char kBreakdownThresholdBytes[] = "breakdown_threshold_bytes";
-// String parameters used to parse category event filters.
-const char kEventFiltersParam[] = "event_filters";
-const char kFilterPredicateParam[] = "filter_predicate";
-const char kFilterArgsParam[] = "filter_args";
-
// Default configuration of memory dumps.
const TraceConfig::MemoryDumpConfig::Trigger kDefaultHeavyMemoryDumpTrigger = {
- 2000, // min_time_between_dumps_ms
- MemoryDumpLevelOfDetail::DETAILED, MemoryDumpType::PERIODIC_INTERVAL};
+ 2000, // periodic_interval_ms
+ MemoryDumpLevelOfDetail::DETAILED};
const TraceConfig::MemoryDumpConfig::Trigger kDefaultLightMemoryDumpTrigger = {
- 250, // min_time_between_dumps_ms
- MemoryDumpLevelOfDetail::LIGHT, MemoryDumpType::PERIODIC_INTERVAL};
+ 250, // periodic_interval_ms
+ MemoryDumpLevelOfDetail::LIGHT};
class ConvertableTraceConfigToTraceFormat
: public base::trace_event::ConvertableToTraceFormat {
public:
explicit ConvertableTraceConfigToTraceFormat(const TraceConfig& trace_config)
: trace_config_(trace_config) {}
-
~ConvertableTraceConfigToTraceFormat() override {}
void AppendAsTraceFormat(std::string* out) const override {
@@ -121,94 +115,6 @@ void TraceConfig::MemoryDumpConfig::Clear() {
heap_profiler_options.Clear();
}
-void TraceConfig::MemoryDumpConfig::Merge(
- const TraceConfig::MemoryDumpConfig& config) {
- triggers.insert(triggers.end(), config.triggers.begin(),
- config.triggers.end());
- allowed_dump_modes.insert(config.allowed_dump_modes.begin(),
- config.allowed_dump_modes.end());
- heap_profiler_options.breakdown_threshold_bytes =
- std::min(heap_profiler_options.breakdown_threshold_bytes,
- config.heap_profiler_options.breakdown_threshold_bytes);
-}
-
-TraceConfig::EventFilterConfig::EventFilterConfig(
- const std::string& predicate_name)
- : predicate_name_(predicate_name) {}
-
-TraceConfig::EventFilterConfig::~EventFilterConfig() {}
-
-TraceConfig::EventFilterConfig::EventFilterConfig(const EventFilterConfig& tc) {
- *this = tc;
-}
-
-TraceConfig::EventFilterConfig& TraceConfig::EventFilterConfig::operator=(
- const TraceConfig::EventFilterConfig& rhs) {
- if (this == &rhs)
- return *this;
-
- predicate_name_ = rhs.predicate_name_;
- included_categories_ = rhs.included_categories_;
- excluded_categories_ = rhs.excluded_categories_;
- if (rhs.args_)
- args_ = rhs.args_->CreateDeepCopy();
-
- return *this;
-}
-
-void TraceConfig::EventFilterConfig::AddIncludedCategory(
- const std::string& category) {
- included_categories_.push_back(category);
-}
-
-void TraceConfig::EventFilterConfig::AddExcludedCategory(
- const std::string& category) {
- excluded_categories_.push_back(category);
-}
-
-void TraceConfig::EventFilterConfig::SetArgs(
- std::unique_ptr<base::DictionaryValue> args) {
- args_ = std::move(args);
-}
-
-bool TraceConfig::EventFilterConfig::GetArgAsSet(
- const char* key,
- std::unordered_set<std::string>* out_set) const {
- const ListValue* list = nullptr;
- if (!args_->GetList(key, &list))
- return false;
- for (size_t i = 0; i < list->GetSize(); ++i) {
- std::string value;
- if (list->GetString(i, &value))
- out_set->insert(value);
- }
- return true;
-}
-
-bool TraceConfig::EventFilterConfig::IsCategoryGroupEnabled(
- const char* category_group_name) const {
- CStringTokenizer category_group_tokens(
- category_group_name, category_group_name + strlen(category_group_name),
- ",");
- while (category_group_tokens.GetNext()) {
- std::string category_group_token = category_group_tokens.token();
-
- for (const auto& excluded_category : excluded_categories_) {
- if (base::MatchPattern(category_group_token, excluded_category)) {
- return false;
- }
- }
-
- for (const auto& included_category : included_categories_) {
- if (base::MatchPattern(category_group_token, included_category)) {
- return true;
- }
- }
- }
-
- return false;
-}
-
TraceConfig::TraceConfig() {
InitializeDefault();
}
@@ -253,14 +159,14 @@ TraceConfig::TraceConfig(StringPiece config_string) {
TraceConfig::TraceConfig(const TraceConfig& tc)
: record_mode_(tc.record_mode_),
+ enable_sampling_(tc.enable_sampling_),
enable_systrace_(tc.enable_systrace_),
enable_argument_filter_(tc.enable_argument_filter_),
memory_dump_config_(tc.memory_dump_config_),
included_categories_(tc.included_categories_),
disabled_categories_(tc.disabled_categories_),
excluded_categories_(tc.excluded_categories_),
- synthetic_delays_(tc.synthetic_delays_),
- event_filters_(tc.event_filters_) {}
+ synthetic_delays_(tc.synthetic_delays_) {}
TraceConfig::~TraceConfig() {
}
@@ -270,6 +176,7 @@ TraceConfig& TraceConfig::operator=(const TraceConfig& rhs) {
return *this;
record_mode_ = rhs.record_mode_;
+ enable_sampling_ = rhs.enable_sampling_;
enable_systrace_ = rhs.enable_systrace_;
enable_argument_filter_ = rhs.enable_argument_filter_;
memory_dump_config_ = rhs.memory_dump_config_;
@@ -277,7 +184,6 @@ TraceConfig& TraceConfig::operator=(const TraceConfig& rhs) {
disabled_categories_ = rhs.disabled_categories_;
excluded_categories_ = rhs.excluded_categories_;
synthetic_delays_ = rhs.synthetic_delays_;
- event_filters_ = rhs.event_filters_;
return *this;
}
@@ -294,7 +200,7 @@ std::string TraceConfig::ToString() const {
std::unique_ptr<ConvertableToTraceFormat>
TraceConfig::AsConvertableToTraceFormat() const {
- return MakeUnique<ConvertableTraceConfigToTraceFormat>(*this);
+ return WrapUnique(new ConvertableTraceConfigToTraceFormat(*this));
}
std::string TraceConfig::ToCategoryFilterString() const {
@@ -365,6 +271,7 @@ bool TraceConfig::IsCategoryGroupEnabled(
void TraceConfig::Merge(const TraceConfig& config) {
if (record_mode_ != config.record_mode_
+ || enable_sampling_ != config.enable_sampling_
|| enable_systrace_ != config.enable_systrace_
|| enable_argument_filter_ != config.enable_argument_filter_) {
DLOG(ERROR) << "Attempting to merge trace config with a different "
@@ -382,7 +289,9 @@ void TraceConfig::Merge(const TraceConfig& config) {
included_categories_.clear();
}
- memory_dump_config_.Merge(config.memory_dump_config_);
+ memory_dump_config_.triggers.insert(memory_dump_config_.triggers.end(),
+ config.memory_dump_config_.triggers.begin(),
+ config.memory_dump_config_.triggers.end());
disabled_categories_.insert(disabled_categories_.end(),
config.disabled_categories_.begin(),
@@ -393,12 +302,11 @@ void TraceConfig::Merge(const TraceConfig& config) {
synthetic_delays_.insert(synthetic_delays_.end(),
config.synthetic_delays_.begin(),
config.synthetic_delays_.end());
- event_filters_.insert(event_filters_.end(), config.event_filters().begin(),
- config.event_filters().end());
}
void TraceConfig::Clear() {
record_mode_ = RECORD_UNTIL_FULL;
+ enable_sampling_ = false;
enable_systrace_ = false;
enable_argument_filter_ = false;
included_categories_.clear();
@@ -406,11 +314,11 @@ void TraceConfig::Clear() {
excluded_categories_.clear();
synthetic_delays_.clear();
memory_dump_config_.Clear();
- event_filters_.clear();
}
void TraceConfig::InitializeDefault() {
record_mode_ = RECORD_UNTIL_FULL;
+ enable_sampling_ = false;
enable_systrace_ = false;
enable_argument_filter_ = false;
}
@@ -431,6 +339,7 @@ void TraceConfig::InitializeFromConfigDict(const DictionaryValue& dict) {
}
bool val;
+ enable_sampling_ = dict.GetBoolean(kEnableSamplingParam, &val) ? val : false;
enable_systrace_ = dict.GetBoolean(kEnableSystraceParam, &val) ? val : false;
enable_argument_filter_ =
dict.GetBoolean(kEnableArgumentFilterParam, &val) ? val : false;
@@ -443,10 +352,6 @@ void TraceConfig::InitializeFromConfigDict(const DictionaryValue& dict) {
if (dict.GetList(kSyntheticDelaysParam, &category_list))
SetSyntheticDelaysFromList(*category_list);
- const base::ListValue* category_event_filters = nullptr;
- if (dict.GetList(kEventFiltersParam, &category_event_filters))
- SetEventFiltersFromConfigList(*category_event_filters);
-
if (IsCategoryEnabled(MemoryDumpManager::kTraceCategory)) {
// If dump triggers not set, the client is using the legacy with just
// category enabled. So, use the default periodic dump config.
@@ -501,6 +406,7 @@ void TraceConfig::InitializeFromStrings(StringPiece category_filter_string,
}
record_mode_ = RECORD_UNTIL_FULL;
+ enable_sampling_ = false;
enable_systrace_ = false;
enable_argument_filter_ = false;
if (!trace_options_string.empty()) {
@@ -515,6 +421,8 @@ 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 == kEnableSampling) {
+ enable_sampling_ = true;
} else if (token == kEnableSystrace) {
enable_systrace_ = true;
} else if (token == kEnableArgumentFilter) {
@@ -608,26 +516,17 @@ void TraceConfig::SetMemoryDumpConfigFromConfigDict(
if (!trigger_list->GetDictionary(i, &trigger))
continue;
- MemoryDumpConfig::Trigger dump_config;
int interval = 0;
- if (!trigger->GetInteger(kMinTimeBetweenDumps, &interval)) {
- // If "min_time_between_dumps_ms" param was not given, then the trace
- // config uses old format where only periodic dumps are supported.
- trigger->GetInteger(kPeriodicIntervalLegacyParam, &interval);
- dump_config.trigger_type = MemoryDumpType::PERIODIC_INTERVAL;
- } else {
- std::string trigger_type_str;
- trigger->GetString(kTriggerTypeParam, &trigger_type_str);
- dump_config.trigger_type = StringToMemoryDumpType(trigger_type_str);
- }
- DCHECK_GT(interval, 0);
- dump_config.min_time_between_dumps_ms = static_cast<uint32_t>(interval);
+ if (!trigger->GetInteger(kPeriodicIntervalParam, &interval))
+ continue;
+ DCHECK_GT(interval, 0);
+ MemoryDumpConfig::Trigger dump_config;
+ dump_config.periodic_interval_ms = static_cast<uint32_t>(interval);
std::string level_of_detail_str;
- trigger->GetString(kTriggerModeParam, &level_of_detail_str);
+ trigger->GetString(kModeParam, &level_of_detail_str);
dump_config.level_of_detail =
StringToMemoryDumpLevelOfDetail(level_of_detail_str);
-
memory_dump_config_.triggers.push_back(dump_config);
}
}
@@ -656,50 +555,6 @@ void TraceConfig::SetDefaultMemoryDumpConfig() {
memory_dump_config_.allowed_dump_modes = GetDefaultAllowedMemoryDumpModes();
}
-void TraceConfig::SetEventFiltersFromConfigList(
- const base::ListValue& category_event_filters) {
- event_filters_.clear();
-
- for (size_t event_filter_index = 0;
- event_filter_index < category_event_filters.GetSize();
- ++event_filter_index) {
- const base::DictionaryValue* event_filter = nullptr;
- if (!category_event_filters.GetDictionary(event_filter_index,
- &event_filter))
- continue;
-
- std::string predicate_name;
- CHECK(event_filter->GetString(kFilterPredicateParam, &predicate_name))
- << "Invalid predicate name in category event filter.";
-
- EventFilterConfig new_config(predicate_name);
- const base::ListValue* included_list = nullptr;
- CHECK(event_filter->GetList(kIncludedCategoriesParam, &included_list))
- << "Missing included_categories in category event filter.";
-
- for (size_t i = 0; i < included_list->GetSize(); ++i) {
- std::string category;
- if (included_list->GetString(i, &category))
- new_config.AddIncludedCategory(category);
- }
-
- const base::ListValue* excluded_list = nullptr;
- if (event_filter->GetList(kExcludedCategoriesParam, &excluded_list)) {
- for (size_t i = 0; i < excluded_list->GetSize(); ++i) {
- std::string category;
- if (excluded_list->GetString(i, &category))
- new_config.AddExcludedCategory(category);
- }
- }
-
- const base::DictionaryValue* args_dict = nullptr;
- if (event_filter->GetDictionary(kFilterArgsParam, &args_dict))
- new_config.SetArgs(args_dict->CreateDeepCopy());
-
- event_filters_.push_back(new_config);
- }
-}
-
std::unique_ptr<DictionaryValue> TraceConfig::ToDict() const {
auto dict = MakeUnique<DictionaryValue>();
switch (record_mode_) {
@@ -719,6 +574,7 @@ std::unique_ptr<DictionaryValue> TraceConfig::ToDict() const {
NOTREACHED();
}
+ dict->SetBoolean(kEnableSamplingParam, enable_sampling_);
dict->SetBoolean(kEnableSystraceParam, enable_systrace_);
dict->SetBoolean(kEnableArgumentFilterParam, enable_argument_filter_);
@@ -730,41 +586,6 @@ std::unique_ptr<DictionaryValue> TraceConfig::ToDict() const {
AddCategoryToDict(dict.get(), kExcludedCategoriesParam, excluded_categories_);
AddCategoryToDict(dict.get(), kSyntheticDelaysParam, synthetic_delays_);
- if (!event_filters_.empty()) {
- std::unique_ptr<base::ListValue> filter_list(new base::ListValue());
- for (const EventFilterConfig& filter : event_filters_) {
- std::unique_ptr<base::DictionaryValue> filter_dict(
- new base::DictionaryValue());
- filter_dict->SetString(kFilterPredicateParam, filter.predicate_name());
-
- std::unique_ptr<base::ListValue> included_categories_list(
- new base::ListValue());
- for (const std::string& included_category : filter.included_categories())
- included_categories_list->AppendString(included_category);
-
- filter_dict->Set(kIncludedCategoriesParam,
- std::move(included_categories_list));
-
- if (!filter.excluded_categories().empty()) {
- std::unique_ptr<base::ListValue> excluded_categories_list(
- new base::ListValue());
- for (const std::string& excluded_category :
- filter.excluded_categories())
- excluded_categories_list->AppendString(excluded_category);
-
- filter_dict->Set(kExcludedCategoriesParam,
- std::move(excluded_categories_list));
- }
-
- if (filter.filter_args())
- filter_dict->Set(kFilterArgsParam,
- filter.filter_args()->CreateDeepCopy());
-
- filter_list->Append(std::move(filter_dict));
- }
- dict->Set(kEventFiltersParam, std::move(filter_list));
- }
-
if (IsCategoryEnabled(MemoryDumpManager::kTraceCategory)) {
auto allowed_modes = MakeUnique<ListValue>();
for (auto dump_mode : memory_dump_config_.allowed_dump_modes)
@@ -776,14 +597,10 @@ std::unique_ptr<DictionaryValue> TraceConfig::ToDict() const {
auto triggers_list = MakeUnique<ListValue>();
for (const auto& config : memory_dump_config_.triggers) {
auto trigger_dict = MakeUnique<DictionaryValue>();
- trigger_dict->SetString(kTriggerTypeParam,
- MemoryDumpTypeToString(config.trigger_type));
- trigger_dict->SetInteger(
- kMinTimeBetweenDumps,
- static_cast<int>(config.min_time_between_dumps_ms));
+ trigger_dict->SetInteger(kPeriodicIntervalParam,
+ static_cast<int>(config.periodic_interval_ms));
trigger_dict->SetString(
- kTriggerModeParam,
- MemoryDumpLevelOfDetailToString(config.level_of_detail));
+ kModeParam, MemoryDumpLevelOfDetailToString(config.level_of_detail));
triggers_list->Append(std::move(trigger_dict));
}
@@ -822,6 +639,8 @@ std::string TraceConfig::ToTraceOptionsString() const {
default:
NOTREACHED();
}
+ if (enable_sampling_)
+ ret = ret + "," + kEnableSampling;
if (enable_systrace_)
ret = ret + "," + kEnableSystrace;
if (enable_argument_filter_)
diff --git a/base/trace_event/trace_config.h b/base/trace_event/trace_config.h
index 717c261316..91d6f1f3bd 100644
--- a/base/trace_event/trace_config.h
+++ b/base/trace_event/trace_config.h
@@ -7,10 +7,8 @@
#include <stdint.h>
-#include <memory>
#include <set>
#include <string>
-#include <unordered_set>
#include <vector>
#include "base/base_export.h"
@@ -53,9 +51,8 @@ class BASE_EXPORT TraceConfig {
// Specifies the triggers in the memory dump config.
struct Trigger {
- uint32_t min_time_between_dumps_ms;
+ uint32_t periodic_interval_ms;
MemoryDumpLevelOfDetail level_of_detail;
- MemoryDumpType trigger_type;
};
// Specifies the configuration options for the heap profiler.
@@ -74,8 +71,6 @@ class BASE_EXPORT TraceConfig {
// Reset the values in the config.
void Clear();
- void Merge(const MemoryDumpConfig& config);
-
// Set of memory dump modes allowed for the tracing session. The explicitly
// triggered dumps will be successful only if the dump mode is allowed in
// the config.
@@ -85,39 +80,6 @@ class BASE_EXPORT TraceConfig {
HeapProfiler heap_profiler_options;
};
- class BASE_EXPORT EventFilterConfig {
- public:
- EventFilterConfig(const std::string& predicate_name);
- EventFilterConfig(const EventFilterConfig& tc);
-
- ~EventFilterConfig();
-
- EventFilterConfig& operator=(const EventFilterConfig& rhs);
-
- void AddIncludedCategory(const std::string& category);
- void AddExcludedCategory(const std::string& category);
- void SetArgs(std::unique_ptr<base::DictionaryValue> args);
- bool GetArgAsSet(const char* key, std::unordered_set<std::string>*) const;
-
- bool IsCategoryGroupEnabled(const char* category_group_name) const;
-
- const std::string& predicate_name() const { return predicate_name_; }
- base::DictionaryValue* filter_args() const { return args_.get(); }
- const StringList& included_categories() const {
- return included_categories_;
- }
- const StringList& excluded_categories() const {
- return excluded_categories_;
- }
-
- private:
- std::string predicate_name_;
- StringList included_categories_;
- StringList excluded_categories_;
- std::unique_ptr<base::DictionaryValue> args_;
- };
- typedef std::vector<EventFilterConfig> EventFilters;
-
TraceConfig();
// Create TraceConfig object from category filter and trace options strings.
@@ -131,22 +93,22 @@ class BASE_EXPORT TraceConfig {
//
// |trace_options_string| is a comma-delimited list of trace options.
// Possible options are: "record-until-full", "record-continuously",
- // "record-as-much-as-possible", "trace-to-console", "enable-systrace" and
- // "enable-argument-filter".
+ // "record-as-much-as-possible", "trace-to-console", "enable-sampling",
+ // "enable-systrace" and "enable-argument-filter".
// The first 4 options are trace recoding modes and hence
// mutually exclusive. If more than one trace recording modes appear in the
// options_string, the last one takes precedence. If none of the trace
// recording mode is specified, recording mode is RECORD_UNTIL_FULL.
//
// The trace option will first be reset to the default option
- // (record_mode set to RECORD_UNTIL_FULL, enable_systrace and
- // enable_argument_filter set to false) before options parsed from
+ // (record_mode set to RECORD_UNTIL_FULL, enable_sampling, enable_systrace,
+ // and enable_argument_filter set to false) before options parsed from
// |trace_options_string| are applied on it. If |trace_options_string| is
// invalid, the final state of trace options is undefined.
//
// Example: TraceConfig("test_MyTest*", "record-until-full");
// Example: TraceConfig("test_MyTest*,test_OtherStuff",
- // "record-continuously");
+ // "record-continuously, enable-sampling");
// Example: TraceConfig("-excluded_category1,-excluded_category2",
// "record-until-full, trace-to-console");
// would set ECHO_TO_CONSOLE as the recording mode.
@@ -176,6 +138,7 @@ class BASE_EXPORT TraceConfig {
// Example:
// {
// "record_mode": "record-continuously",
+ // "enable_sampling": true,
// "enable_systrace": true,
// "enable_argument_filter": true,
// "included_categories": ["included",
@@ -211,10 +174,12 @@ class BASE_EXPORT TraceConfig {
const StringList& GetSyntheticDelayValues() const;
TraceRecordMode GetTraceRecordMode() const { return record_mode_; }
+ bool IsSamplingEnabled() const { return enable_sampling_; }
bool IsSystraceEnabled() const { return enable_systrace_; }
bool IsArgumentFilterEnabled() const { return enable_argument_filter_; }
void SetTraceRecordMode(TraceRecordMode mode) { record_mode_ = mode; }
+ void EnableSampling() { enable_sampling_ = true; }
void EnableSystrace() { enable_systrace_ = true; }
void EnableArgumentFilter() { enable_argument_filter_ = true; }
@@ -231,7 +196,7 @@ class BASE_EXPORT TraceConfig {
// 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.
- bool IsCategoryGroupEnabled(const char* category_group_name) const;
+ bool IsCategoryGroupEnabled(const char* category_group) const;
// Merges config with the current TraceConfig
void Merge(const TraceConfig& config);
@@ -245,11 +210,6 @@ class BASE_EXPORT TraceConfig {
return memory_dump_config_;
}
- const EventFilters& event_filters() const { return event_filters_; }
- void SetEventFilters(const EventFilters& filter_configs) {
- event_filters_ = filter_configs;
- }
-
private:
FRIEND_TEST_ALL_PREFIXES(TraceConfigTest, TraceConfigFromValidLegacyFormat);
FRIEND_TEST_ALL_PREFIXES(TraceConfigTest,
@@ -290,7 +250,6 @@ class BASE_EXPORT TraceConfig {
const DictionaryValue& memory_dump_config);
void SetDefaultMemoryDumpConfig();
- void SetEventFiltersFromConfigList(const base::ListValue& event_filters);
std::unique_ptr<DictionaryValue> ToDict() const;
std::string ToTraceOptionsString() const;
@@ -312,6 +271,7 @@ class BASE_EXPORT TraceConfig {
bool HasIncludedPatterns() const;
TraceRecordMode record_mode_;
+ bool enable_sampling_ : 1;
bool enable_systrace_ : 1;
bool enable_argument_filter_ : 1;
@@ -321,7 +281,6 @@ class BASE_EXPORT TraceConfig {
StringList disabled_categories_;
StringList excluded_categories_;
StringList synthetic_delays_;
- EventFilters event_filters_;
};
} // namespace trace_event
diff --git a/base/trace_event/trace_config_memory_test_util.h b/base/trace_event/trace_config_memory_test_util.h
index 744e8a8acc..6b47f8dc55 100644
--- a/base/trace_event/trace_config_memory_test_util.h
+++ b/base/trace_event/trace_config_memory_test_util.h
@@ -13,144 +13,87 @@ namespace trace_event {
class TraceConfigMemoryTestUtil {
public:
- static std::string GetTraceConfig_LegacyPeriodicTriggers(int light_period,
- int heavy_period) {
- return StringPrintf(
- "{"
- "\"enable_argument_filter\":false,"
- "\"enable_systrace\":false,"
- "\"included_categories\":["
- "\"%s\""
- "],"
- "\"memory_dump_config\":{"
- "\"allowed_dump_modes\":[\"background\",\"light\",\"detailed\"],"
- "\"heap_profiler_options\":{"
- "\"breakdown_threshold_bytes\":2048"
- "},"
- "\"triggers\":["
- "{"
- "\"mode\":\"light\","
- "\"periodic_interval_ms\":%d"
- "},"
- "{"
- "\"mode\":\"detailed\","
- "\"periodic_interval_ms\":%d"
- "}"
- "]"
- "},"
- "\"record_mode\":\"record-until-full\""
- "}",
- MemoryDumpManager::kTraceCategory, light_period, heavy_period);
- ;
- }
-
static std::string GetTraceConfig_PeriodicTriggers(int light_period,
int heavy_period) {
return StringPrintf(
"{"
- "\"enable_argument_filter\":false,"
- "\"enable_systrace\":false,"
- "\"included_categories\":["
- "\"%s\""
- "],"
- "\"memory_dump_config\":{"
- "\"allowed_dump_modes\":[\"background\",\"light\",\"detailed\"],"
- "\"heap_profiler_options\":{"
- "\"breakdown_threshold_bytes\":2048"
- "},"
- "\"triggers\":["
- "{"
- "\"min_time_between_dumps_ms\":%d,"
- "\"mode\":\"light\","
- "\"type\":\"periodic_interval\""
- "},"
- "{"
- "\"min_time_between_dumps_ms\":%d,"
- "\"mode\":\"detailed\","
- "\"type\":\"periodic_interval\""
- "}"
- "]"
- "},"
- "\"record_mode\":\"record-until-full\""
- "}",
- MemoryDumpManager::kTraceCategory, light_period, heavy_period);
+ "\"enable_argument_filter\":false,"
+ "\"enable_sampling\":false,"
+ "\"enable_systrace\":false,"
+ "\"included_categories\":["
+ "\"%s\""
+ "],"
+ "\"memory_dump_config\":{"
+ "\"allowed_dump_modes\":[\"background\",\"light\",\"detailed\"],"
+ "\"heap_profiler_options\":{"
+ "\"breakdown_threshold_bytes\":2048"
+ "},"
+ "\"triggers\":["
+ "{"
+ "\"mode\":\"light\","
+ "\"periodic_interval_ms\":%d"
+ "},"
+ "{"
+ "\"mode\":\"detailed\","
+ "\"periodic_interval_ms\":%d"
+ "}"
+ "]"
+ "},"
+ "\"record_mode\":\"record-until-full\""
+ "}", MemoryDumpManager::kTraceCategory, light_period, heavy_period);
}
static std::string GetTraceConfig_EmptyTriggers() {
return StringPrintf(
"{"
- "\"enable_argument_filter\":false,"
- "\"enable_systrace\":false,"
- "\"included_categories\":["
- "\"%s\""
- "],"
- "\"memory_dump_config\":{"
- "\"allowed_dump_modes\":[\"background\",\"light\",\"detailed\"],"
- "\"triggers\":["
- "]"
- "},"
- "\"record_mode\":\"record-until-full\""
- "}",
- MemoryDumpManager::kTraceCategory);
+ "\"enable_argument_filter\":false,"
+ "\"enable_sampling\":false,"
+ "\"enable_systrace\":false,"
+ "\"included_categories\":["
+ "\"%s\""
+ "],"
+ "\"memory_dump_config\":{"
+ "\"allowed_dump_modes\":[\"background\",\"light\",\"detailed\"],"
+ "\"triggers\":["
+ "]"
+ "},"
+ "\"record_mode\":\"record-until-full\""
+ "}", MemoryDumpManager::kTraceCategory);
}
static std::string GetTraceConfig_NoTriggers() {
return StringPrintf(
"{"
- "\"enable_argument_filter\":false,"
- "\"enable_systrace\":false,"
- "\"included_categories\":["
- "\"%s\""
- "],"
- "\"record_mode\":\"record-until-full\""
- "}",
- MemoryDumpManager::kTraceCategory);
+ "\"enable_argument_filter\":false,"
+ "\"enable_sampling\":false,"
+ "\"enable_systrace\":false,"
+ "\"included_categories\":["
+ "\"%s\""
+ "],"
+ "\"record_mode\":\"record-until-full\""
+ "}", MemoryDumpManager::kTraceCategory);
}
static std::string GetTraceConfig_BackgroundTrigger(int period_ms) {
return StringPrintf(
"{"
- "\"enable_argument_filter\":false,"
- "\"enable_systrace\":false,"
- "\"included_categories\":["
- "\"%s\""
- "],"
- "\"memory_dump_config\":{"
- "\"allowed_dump_modes\":[\"background\"],"
- "\"triggers\":["
- "{"
- "\"min_time_between_dumps_ms\":%d,"
- "\"mode\":\"background\","
- "\"type\":\"periodic_interval\""
- "}"
- "]"
- "},"
- "\"record_mode\":\"record-until-full\""
- "}",
- MemoryDumpManager::kTraceCategory, period_ms);
- }
-
- static std::string GetTraceConfig_PeakDetectionTrigger(int heavy_period) {
- return StringPrintf(
- "{"
- "\"enable_argument_filter\":false,"
- "\"enable_systrace\":false,"
- "\"included_categories\":["
- "\"%s\""
- "],"
- "\"memory_dump_config\":{"
- "\"allowed_dump_modes\":[\"background\",\"light\",\"detailed\"],"
- "\"triggers\":["
- "{"
- "\"min_time_between_dumps_ms\":%d,"
- "\"mode\":\"detailed\","
- "\"type\":\"peak_memory_usage\""
- "}"
- "]"
- "},"
- "\"record_mode\":\"record-until-full\""
- "}",
- MemoryDumpManager::kTraceCategory, heavy_period);
+ "\"enable_argument_filter\":false,"
+ "\"enable_sampling\":false,"
+ "\"enable_systrace\":false,"
+ "\"included_categories\":["
+ "\"%s\""
+ "],"
+ "\"memory_dump_config\":{"
+ "\"allowed_dump_modes\":[\"background\"],"
+ "\"triggers\":["
+ "{"
+ "\"mode\":\"background\","
+ "\"periodic_interval_ms\":%d"
+ "}"
+ "]"
+ "},"
+ "\"record_mode\":\"record-until-full\""
+ "}", MemoryDumpManager::kTraceCategory, period_ms);
}
};
diff --git a/base/trace_event/trace_config_unittest.cc b/base/trace_event/trace_config_unittest.cc
index 74aa7bdc63..4b46b2fefd 100644
--- a/base/trace_event/trace_config_unittest.cc
+++ b/base/trace_event/trace_config_unittest.cc
@@ -5,7 +5,6 @@
#include <stddef.h>
#include "base/json/json_reader.h"
-#include "base/json/json_writer.h"
#include "base/macros.h"
#include "base/trace_event/memory_dump_manager.h"
#include "base/trace_event/trace_config.h"
@@ -20,52 +19,38 @@ namespace {
const char kDefaultTraceConfigString[] =
"{"
"\"enable_argument_filter\":false,"
+ "\"enable_sampling\":false,"
"\"enable_systrace\":false,"
"\"record_mode\":\"record-until-full\""
"}";
const char kCustomTraceConfigString[] =
- "{"
+ "{"
"\"enable_argument_filter\":true,"
+ "\"enable_sampling\":true,"
"\"enable_systrace\":true,"
- "\"event_filters\":["
- "{"
- "\"excluded_categories\":[\"unfiltered_cat\"],"
- "\"filter_args\":{\"event_name_whitelist\":[\"a snake\",\"a dog\"]},"
- "\"filter_predicate\":\"event_whitelist_predicate\","
- "\"included_categories\":[\"*\"]"
- "}"
- "],"
"\"excluded_categories\":[\"excluded\",\"exc_pattern*\"],"
- "\"included_categories\":["
- "\"included\","
- "\"inc_pattern*\","
- "\"disabled-by-default-cc\","
- "\"disabled-by-default-memory-infra\"],"
+ "\"included_categories\":[\"included\","
+ "\"inc_pattern*\","
+ "\"disabled-by-default-cc\","
+ "\"disabled-by-default-memory-infra\"],"
"\"memory_dump_config\":{"
- "\"allowed_dump_modes\":[\"background\",\"light\",\"detailed\"],"
- "\"heap_profiler_options\":{"
- "\"breakdown_threshold_bytes\":10240"
- "},"
- "\"triggers\":["
- "{"
- "\"min_time_between_dumps_ms\":50,"
- "\"mode\":\"light\","
- "\"type\":\"periodic_interval\""
- "},"
- "{"
- "\"min_time_between_dumps_ms\":1000,"
- "\"mode\":\"detailed\","
- "\"type\":\"peak_memory_usage\""
- "}"
- "]"
+ "\"allowed_dump_modes\":[\"background\",\"light\",\"detailed\"],"
+ "\"heap_profiler_options\":{"
+ "\"breakdown_threshold_bytes\":10240"
+ "},"
+ "\"triggers\":["
+ "{\"mode\":\"light\",\"periodic_interval_ms\":50},"
+ "{\"mode\":\"detailed\",\"periodic_interval_ms\":1000}"
+ "]"
"},"
"\"record_mode\":\"record-continuously\","
"\"synthetic_delays\":[\"test.Delay1;16\",\"test.Delay2;32\"]"
- "}";
+ "}";
void CheckDefaultTraceConfigBehavior(const TraceConfig& tc) {
EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode());
+ EXPECT_FALSE(tc.IsSamplingEnabled());
EXPECT_FALSE(tc.IsSystraceEnabled());
EXPECT_FALSE(tc.IsArgumentFilterEnabled());
@@ -87,31 +72,44 @@ TEST(TraceConfigTest, TraceConfigFromValidLegacyFormat) {
// From trace options strings
TraceConfig config("", "record-until-full");
EXPECT_EQ(RECORD_UNTIL_FULL, config.GetTraceRecordMode());
+ EXPECT_FALSE(config.IsSamplingEnabled());
EXPECT_FALSE(config.IsSystraceEnabled());
EXPECT_FALSE(config.IsArgumentFilterEnabled());
EXPECT_STREQ("record-until-full", config.ToTraceOptionsString().c_str());
config = TraceConfig("", "record-continuously");
EXPECT_EQ(RECORD_CONTINUOUSLY, config.GetTraceRecordMode());
+ EXPECT_FALSE(config.IsSamplingEnabled());
EXPECT_FALSE(config.IsSystraceEnabled());
EXPECT_FALSE(config.IsArgumentFilterEnabled());
EXPECT_STREQ("record-continuously", config.ToTraceOptionsString().c_str());
config = TraceConfig("", "trace-to-console");
EXPECT_EQ(ECHO_TO_CONSOLE, config.GetTraceRecordMode());
+ EXPECT_FALSE(config.IsSamplingEnabled());
EXPECT_FALSE(config.IsSystraceEnabled());
EXPECT_FALSE(config.IsArgumentFilterEnabled());
EXPECT_STREQ("trace-to-console", config.ToTraceOptionsString().c_str());
config = TraceConfig("", "record-as-much-as-possible");
EXPECT_EQ(RECORD_AS_MUCH_AS_POSSIBLE, config.GetTraceRecordMode());
+ EXPECT_FALSE(config.IsSamplingEnabled());
EXPECT_FALSE(config.IsSystraceEnabled());
EXPECT_FALSE(config.IsArgumentFilterEnabled());
EXPECT_STREQ("record-as-much-as-possible",
config.ToTraceOptionsString().c_str());
+ config = TraceConfig("", "record-until-full, enable-sampling");
+ EXPECT_EQ(RECORD_UNTIL_FULL, config.GetTraceRecordMode());
+ EXPECT_TRUE(config.IsSamplingEnabled());
+ EXPECT_FALSE(config.IsSystraceEnabled());
+ EXPECT_FALSE(config.IsArgumentFilterEnabled());
+ EXPECT_STREQ("record-until-full,enable-sampling",
+ config.ToTraceOptionsString().c_str());
+
config = TraceConfig("", "enable-systrace, record-continuously");
EXPECT_EQ(RECORD_CONTINUOUSLY, config.GetTraceRecordMode());
+ EXPECT_FALSE(config.IsSamplingEnabled());
EXPECT_TRUE(config.IsSystraceEnabled());
EXPECT_FALSE(config.IsArgumentFilterEnabled());
EXPECT_STREQ("record-continuously,enable-systrace",
@@ -119,6 +117,7 @@ TEST(TraceConfigTest, TraceConfigFromValidLegacyFormat) {
config = TraceConfig("", "enable-argument-filter,record-as-much-as-possible");
EXPECT_EQ(RECORD_AS_MUCH_AS_POSSIBLE, config.GetTraceRecordMode());
+ EXPECT_FALSE(config.IsSamplingEnabled());
EXPECT_FALSE(config.IsSystraceEnabled());
EXPECT_TRUE(config.IsArgumentFilterEnabled());
EXPECT_STREQ("record-as-much-as-possible,enable-argument-filter",
@@ -126,17 +125,19 @@ TEST(TraceConfigTest, TraceConfigFromValidLegacyFormat) {
config = TraceConfig(
"",
- "enable-systrace,trace-to-console,enable-argument-filter");
+ "enable-systrace,trace-to-console,enable-sampling,enable-argument-filter");
EXPECT_EQ(ECHO_TO_CONSOLE, config.GetTraceRecordMode());
+ EXPECT_TRUE(config.IsSamplingEnabled());
EXPECT_TRUE(config.IsSystraceEnabled());
EXPECT_TRUE(config.IsArgumentFilterEnabled());
EXPECT_STREQ(
- "trace-to-console,enable-systrace,enable-argument-filter",
+ "trace-to-console,enable-sampling,enable-systrace,enable-argument-filter",
config.ToTraceOptionsString().c_str());
config = TraceConfig(
"", "record-continuously, record-until-full, trace-to-console");
EXPECT_EQ(ECHO_TO_CONSOLE, config.GetTraceRecordMode());
+ EXPECT_FALSE(config.IsSamplingEnabled());
EXPECT_FALSE(config.IsSystraceEnabled());
EXPECT_FALSE(config.IsArgumentFilterEnabled());
EXPECT_STREQ("trace-to-console", config.ToTraceOptionsString().c_str());
@@ -144,24 +145,28 @@ TEST(TraceConfigTest, TraceConfigFromValidLegacyFormat) {
// From TraceRecordMode
config = TraceConfig("", RECORD_UNTIL_FULL);
EXPECT_EQ(RECORD_UNTIL_FULL, config.GetTraceRecordMode());
+ EXPECT_FALSE(config.IsSamplingEnabled());
EXPECT_FALSE(config.IsSystraceEnabled());
EXPECT_FALSE(config.IsArgumentFilterEnabled());
EXPECT_STREQ("record-until-full", config.ToTraceOptionsString().c_str());
config = TraceConfig("", RECORD_CONTINUOUSLY);
EXPECT_EQ(RECORD_CONTINUOUSLY, config.GetTraceRecordMode());
+ EXPECT_FALSE(config.IsSamplingEnabled());
EXPECT_FALSE(config.IsSystraceEnabled());
EXPECT_FALSE(config.IsArgumentFilterEnabled());
EXPECT_STREQ("record-continuously", config.ToTraceOptionsString().c_str());
config = TraceConfig("", ECHO_TO_CONSOLE);
EXPECT_EQ(ECHO_TO_CONSOLE, config.GetTraceRecordMode());
+ EXPECT_FALSE(config.IsSamplingEnabled());
EXPECT_FALSE(config.IsSystraceEnabled());
EXPECT_FALSE(config.IsArgumentFilterEnabled());
EXPECT_STREQ("trace-to-console", config.ToTraceOptionsString().c_str());
config = TraceConfig("", RECORD_AS_MUCH_AS_POSSIBLE);
EXPECT_EQ(RECORD_AS_MUCH_AS_POSSIBLE, config.GetTraceRecordMode());
+ EXPECT_FALSE(config.IsSamplingEnabled());
EXPECT_FALSE(config.IsSystraceEnabled());
EXPECT_FALSE(config.IsArgumentFilterEnabled());
EXPECT_STREQ("record-as-much-as-possible",
@@ -193,30 +198,33 @@ TEST(TraceConfigTest, TraceConfigFromValidLegacyFormat) {
// From both trace options and category filter strings
config = TraceConfig("", "");
EXPECT_EQ(RECORD_UNTIL_FULL, config.GetTraceRecordMode());
+ EXPECT_FALSE(config.IsSamplingEnabled());
EXPECT_FALSE(config.IsSystraceEnabled());
EXPECT_FALSE(config.IsArgumentFilterEnabled());
EXPECT_STREQ("", config.ToCategoryFilterString().c_str());
EXPECT_STREQ("record-until-full", config.ToTraceOptionsString().c_str());
config = TraceConfig("included,-excluded,inc_pattern*,-exc_pattern*",
- "enable-systrace, trace-to-console");
+ "enable-systrace, trace-to-console, enable-sampling");
EXPECT_EQ(ECHO_TO_CONSOLE, config.GetTraceRecordMode());
+ EXPECT_TRUE(config.IsSamplingEnabled());
EXPECT_TRUE(config.IsSystraceEnabled());
EXPECT_FALSE(config.IsArgumentFilterEnabled());
EXPECT_STREQ("included,inc_pattern*,-excluded,-exc_pattern*",
config.ToCategoryFilterString().c_str());
- EXPECT_STREQ("trace-to-console,enable-systrace",
+ EXPECT_STREQ("trace-to-console,enable-sampling,enable-systrace",
config.ToTraceOptionsString().c_str());
// From both trace options and category filter strings with spaces.
config = TraceConfig(" included , -excluded, inc_pattern*, ,-exc_pattern* ",
- "enable-systrace, ,trace-to-console ");
+ "enable-systrace, ,trace-to-console, enable-sampling ");
EXPECT_EQ(ECHO_TO_CONSOLE, config.GetTraceRecordMode());
+ EXPECT_TRUE(config.IsSamplingEnabled());
EXPECT_TRUE(config.IsSystraceEnabled());
EXPECT_FALSE(config.IsArgumentFilterEnabled());
EXPECT_STREQ("included,inc_pattern*,-excluded,-exc_pattern*",
config.ToCategoryFilterString().c_str());
- EXPECT_STREQ("trace-to-console,enable-systrace",
+ EXPECT_STREQ("trace-to-console,enable-sampling,enable-systrace",
config.ToTraceOptionsString().c_str());
// From category filter string and TraceRecordMode
@@ -224,6 +232,7 @@ TEST(TraceConfigTest, TraceConfigFromValidLegacyFormat) {
RECORD_CONTINUOUSLY);
EXPECT_EQ(RECORD_CONTINUOUSLY, config.GetTraceRecordMode());
EXPECT_FALSE(config.IsSystraceEnabled());
+ EXPECT_FALSE(config.IsSamplingEnabled());
EXPECT_FALSE(config.IsArgumentFilterEnabled());
EXPECT_STREQ("included,inc_pattern*,-excluded,-exc_pattern*",
config.ToCategoryFilterString().c_str());
@@ -233,6 +242,7 @@ TEST(TraceConfigTest, TraceConfigFromValidLegacyFormat) {
TEST(TraceConfigTest, TraceConfigFromInvalidLegacyStrings) {
TraceConfig config("", "foo-bar-baz");
EXPECT_EQ(RECORD_UNTIL_FULL, config.GetTraceRecordMode());
+ EXPECT_FALSE(config.IsSamplingEnabled());
EXPECT_FALSE(config.IsSystraceEnabled());
EXPECT_FALSE(config.IsArgumentFilterEnabled());
EXPECT_STREQ("", config.ToCategoryFilterString().c_str());
@@ -240,6 +250,7 @@ TEST(TraceConfigTest, TraceConfigFromInvalidLegacyStrings) {
config = TraceConfig("arbitrary-category", "foo-bar-baz, enable-systrace");
EXPECT_EQ(RECORD_UNTIL_FULL, config.GetTraceRecordMode());
+ EXPECT_FALSE(config.IsSamplingEnabled());
EXPECT_TRUE(config.IsSystraceEnabled());
EXPECT_FALSE(config.IsArgumentFilterEnabled());
EXPECT_STREQ("arbitrary-category", config.ToCategoryFilterString().c_str());
@@ -319,7 +330,6 @@ TEST(TraceConfigTest, DisabledByDefaultCategoryFilterString) {
EXPECT_FALSE(tc.IsCategoryGroupEnabled("bar"));
EXPECT_FALSE(tc.IsCategoryGroupEnabled("disabled-by-default-bar"));
- EXPECT_TRUE(tc.event_filters().empty());
// Enabling only the disabled-by-default-* category means the default ones
// are also enabled.
tc = TraceConfig("disabled-by-default-foo", "");
@@ -336,6 +346,7 @@ TEST(TraceConfigTest, TraceConfigFromDict) {
TraceConfig tc(dict);
EXPECT_STREQ(kDefaultTraceConfigString, tc.ToString().c_str());
EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode());
+ EXPECT_FALSE(tc.IsSamplingEnabled());
EXPECT_FALSE(tc.IsSystraceEnabled());
EXPECT_FALSE(tc.IsArgumentFilterEnabled());
EXPECT_STREQ("", tc.ToCategoryFilterString().c_str());
@@ -349,6 +360,7 @@ TEST(TraceConfigTest, TraceConfigFromDict) {
TraceConfig default_tc(*default_dict);
EXPECT_STREQ(kDefaultTraceConfigString, default_tc.ToString().c_str());
EXPECT_EQ(RECORD_UNTIL_FULL, default_tc.GetTraceRecordMode());
+ EXPECT_FALSE(default_tc.IsSamplingEnabled());
EXPECT_FALSE(default_tc.IsSystraceEnabled());
EXPECT_FALSE(default_tc.IsArgumentFilterEnabled());
EXPECT_STREQ("", default_tc.ToCategoryFilterString().c_str());
@@ -362,6 +374,7 @@ TEST(TraceConfigTest, TraceConfigFromDict) {
TraceConfig custom_tc(*custom_dict);
EXPECT_STREQ(kCustomTraceConfigString, custom_tc.ToString().c_str());
EXPECT_EQ(RECORD_CONTINUOUSLY, custom_tc.GetTraceRecordMode());
+ EXPECT_TRUE(custom_tc.IsSamplingEnabled());
EXPECT_TRUE(custom_tc.IsSystraceEnabled());
EXPECT_TRUE(custom_tc.IsArgumentFilterEnabled());
EXPECT_STREQ("included,inc_pattern*,"
@@ -374,28 +387,22 @@ TEST(TraceConfigTest, TraceConfigFromDict) {
TEST(TraceConfigTest, TraceConfigFromValidString) {
// Using some non-empty config string.
const char config_string[] =
- "{"
+ "{"
"\"enable_argument_filter\":true,"
+ "\"enable_sampling\":true,"
"\"enable_systrace\":true,"
- "\"event_filters\":["
- "{"
- "\"excluded_categories\":[\"unfiltered_cat\"],"
- "\"filter_args\":{\"event_name_whitelist\":[\"a snake\",\"a dog\"]},"
- "\"filter_predicate\":\"event_whitelist_predicate\","
- "\"included_categories\":[\"*\"]"
- "}"
- "],"
"\"excluded_categories\":[\"excluded\",\"exc_pattern*\"],"
"\"included_categories\":[\"included\","
- "\"inc_pattern*\","
- "\"disabled-by-default-cc\"],"
+ "\"inc_pattern*\","
+ "\"disabled-by-default-cc\"],"
"\"record_mode\":\"record-continuously\","
"\"synthetic_delays\":[\"test.Delay1;16\",\"test.Delay2;32\"]"
- "}";
+ "}";
TraceConfig tc(config_string);
EXPECT_STREQ(config_string, tc.ToString().c_str());
EXPECT_EQ(RECORD_CONTINUOUSLY, tc.GetTraceRecordMode());
+ EXPECT_TRUE(tc.IsSamplingEnabled());
EXPECT_TRUE(tc.IsSystraceEnabled());
EXPECT_TRUE(tc.IsArgumentFilterEnabled());
EXPECT_STREQ("included,inc_pattern*,disabled-by-default-cc,-excluded,"
@@ -427,26 +434,6 @@ TEST(TraceConfigTest, TraceConfigFromValidString) {
EXPECT_STREQ("test.Delay1;16", tc.GetSyntheticDelayValues()[0].c_str());
EXPECT_STREQ("test.Delay2;32", tc.GetSyntheticDelayValues()[1].c_str());
- EXPECT_EQ(tc.event_filters().size(), 1u);
- const TraceConfig::EventFilterConfig& event_filter = tc.event_filters()[0];
- EXPECT_STREQ("event_whitelist_predicate",
- event_filter.predicate_name().c_str());
- EXPECT_EQ(1u, event_filter.included_categories().size());
- EXPECT_STREQ("*", event_filter.included_categories()[0].c_str());
- EXPECT_EQ(1u, event_filter.excluded_categories().size());
- EXPECT_STREQ("unfiltered_cat", event_filter.excluded_categories()[0].c_str());
- EXPECT_TRUE(event_filter.filter_args());
-
- std::string json_out;
- base::JSONWriter::Write(*event_filter.filter_args(), &json_out);
- EXPECT_STREQ(json_out.c_str(),
- "{\"event_name_whitelist\":[\"a snake\",\"a dog\"]}");
- std::unordered_set<std::string> filter_values;
- EXPECT_TRUE(event_filter.GetArgAsSet("event_name_whitelist", &filter_values));
- EXPECT_EQ(2u, filter_values.size());
- EXPECT_EQ(1u, filter_values.count("a snake"));
- EXPECT_EQ(1u, filter_values.count("a dog"));
-
const char config_string_2[] = "{\"included_categories\":[\"*\"]}";
TraceConfig tc2(config_string_2);
EXPECT_TRUE(tc2.IsCategoryEnabled("non-disabled-by-default-pattern"));
@@ -459,6 +446,7 @@ TEST(TraceConfigTest, TraceConfigFromValidString) {
EXPECT_STREQ(tc.ToString().c_str(),
"{"
"\"enable_argument_filter\":false,"
+ "\"enable_sampling\":false,"
"\"enable_systrace\":false,"
"\"record_mode\":\"record-until-full\""
"}");
@@ -470,6 +458,7 @@ TEST(TraceConfigTest, TraceConfigFromInvalidString) {
TraceConfig tc("");
EXPECT_STREQ(kDefaultTraceConfigString, tc.ToString().c_str());
EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode());
+ EXPECT_FALSE(tc.IsSamplingEnabled());
EXPECT_FALSE(tc.IsSystraceEnabled());
EXPECT_FALSE(tc.IsArgumentFilterEnabled());
EXPECT_STREQ("", tc.ToCategoryFilterString().c_str());
@@ -478,6 +467,7 @@ TEST(TraceConfigTest, TraceConfigFromInvalidString) {
tc = TraceConfig("This is an invalid config string.");
EXPECT_STREQ(kDefaultTraceConfigString, tc.ToString().c_str());
EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode());
+ EXPECT_FALSE(tc.IsSamplingEnabled());
EXPECT_FALSE(tc.IsSystraceEnabled());
EXPECT_FALSE(tc.IsArgumentFilterEnabled());
EXPECT_STREQ("", tc.ToCategoryFilterString().c_str());
@@ -486,6 +476,7 @@ TEST(TraceConfigTest, TraceConfigFromInvalidString) {
tc = TraceConfig("[\"This\", \"is\", \"not\", \"a\", \"dictionary\"]");
EXPECT_STREQ(kDefaultTraceConfigString, tc.ToString().c_str());
EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode());
+ EXPECT_FALSE(tc.IsSamplingEnabled());
EXPECT_FALSE(tc.IsSystraceEnabled());
EXPECT_FALSE(tc.IsArgumentFilterEnabled());
EXPECT_STREQ("", tc.ToCategoryFilterString().c_str());
@@ -494,6 +485,7 @@ TEST(TraceConfigTest, TraceConfigFromInvalidString) {
tc = TraceConfig("{\"record_mode\": invalid-value-needs-double-quote}");
EXPECT_STREQ(kDefaultTraceConfigString, tc.ToString().c_str());
EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode());
+ EXPECT_FALSE(tc.IsSamplingEnabled());
EXPECT_FALSE(tc.IsSystraceEnabled());
EXPECT_FALSE(tc.IsArgumentFilterEnabled());
EXPECT_STREQ("", tc.ToCategoryFilterString().c_str());
@@ -503,6 +495,7 @@ TEST(TraceConfigTest, TraceConfigFromInvalidString) {
// initialize TraceConfig with best effort.
tc = TraceConfig("{}");
EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode());
+ EXPECT_FALSE(tc.IsSamplingEnabled());
EXPECT_FALSE(tc.IsSystraceEnabled());
EXPECT_FALSE(tc.IsArgumentFilterEnabled());
EXPECT_STREQ("", tc.ToCategoryFilterString().c_str());
@@ -510,6 +503,7 @@ TEST(TraceConfigTest, TraceConfigFromInvalidString) {
tc = TraceConfig("{\"arbitrary-key\":\"arbitrary-value\"}");
EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode());
+ EXPECT_FALSE(tc.IsSamplingEnabled());
EXPECT_FALSE(tc.IsSystraceEnabled());
EXPECT_FALSE(tc.IsArgumentFilterEnabled());
EXPECT_STREQ("", tc.ToCategoryFilterString().c_str());
@@ -517,6 +511,7 @@ TEST(TraceConfigTest, TraceConfigFromInvalidString) {
const char invalid_config_string[] =
"{"
+ "\"enable_sampling\":\"true\","
"\"enable_systrace\":1,"
"\"excluded_categories\":[\"excluded\"],"
"\"included_categories\":\"not a list\","
@@ -527,6 +522,7 @@ TEST(TraceConfigTest, TraceConfigFromInvalidString) {
"}";
tc = TraceConfig(invalid_config_string);
EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode());
+ EXPECT_FALSE(tc.IsSamplingEnabled());
EXPECT_FALSE(tc.IsSystraceEnabled());
EXPECT_FALSE(tc.IsArgumentFilterEnabled());
EXPECT_STREQ("-excluded,DELAY(test.Delay1;16),DELAY(test.Delay2;32)",
@@ -551,6 +547,7 @@ TEST(TraceConfigTest, MergingTraceConfigs) {
tc.Merge(tc2);
EXPECT_STREQ("{"
"\"enable_argument_filter\":false,"
+ "\"enable_sampling\":false,"
"\"enable_systrace\":false,"
"\"excluded_categories\":[\"excluded\",\"exc_pattern*\"],"
"\"record_mode\":\"record-until-full\""
@@ -617,11 +614,15 @@ TEST(TraceConfigTest, IsEmptyOrContainsLeadingOrTrailingWhitespace) {
TEST(TraceConfigTest, SetTraceOptionValues) {
TraceConfig tc;
EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode());
+ EXPECT_FALSE(tc.IsSamplingEnabled());
EXPECT_FALSE(tc.IsSystraceEnabled());
tc.SetTraceRecordMode(RECORD_AS_MUCH_AS_POSSIBLE);
EXPECT_EQ(RECORD_AS_MUCH_AS_POSSIBLE, tc.GetTraceRecordMode());
+ tc.EnableSampling();
+ EXPECT_TRUE(tc.IsSamplingEnabled());
+
tc.EnableSystrace();
EXPECT_TRUE(tc.IsSystraceEnabled());
}
@@ -631,47 +632,30 @@ TEST(TraceConfigTest, TraceConfigFromMemoryConfigString) {
TraceConfigMemoryTestUtil::GetTraceConfig_PeriodicTriggers(200, 2000);
TraceConfig tc1(tc_str1);
EXPECT_EQ(tc_str1, tc1.ToString());
- TraceConfig tc2(
- TraceConfigMemoryTestUtil::GetTraceConfig_LegacyPeriodicTriggers(200,
- 2000));
- EXPECT_EQ(tc_str1, tc2.ToString());
-
EXPECT_TRUE(tc1.IsCategoryGroupEnabled(MemoryDumpManager::kTraceCategory));
ASSERT_EQ(2u, tc1.memory_dump_config_.triggers.size());
- EXPECT_EQ(200u,
- tc1.memory_dump_config_.triggers[0].min_time_between_dumps_ms);
+ EXPECT_EQ(200u, tc1.memory_dump_config_.triggers[0].periodic_interval_ms);
EXPECT_EQ(MemoryDumpLevelOfDetail::LIGHT,
tc1.memory_dump_config_.triggers[0].level_of_detail);
- EXPECT_EQ(2000u,
- tc1.memory_dump_config_.triggers[1].min_time_between_dumps_ms);
+ EXPECT_EQ(2000u, tc1.memory_dump_config_.triggers[1].periodic_interval_ms);
EXPECT_EQ(MemoryDumpLevelOfDetail::DETAILED,
tc1.memory_dump_config_.triggers[1].level_of_detail);
EXPECT_EQ(
2048u,
tc1.memory_dump_config_.heap_profiler_options.breakdown_threshold_bytes);
- std::string tc_str3 =
+ std::string tc_str2 =
TraceConfigMemoryTestUtil::GetTraceConfig_BackgroundTrigger(
1 /* period_ms */);
- TraceConfig tc3(tc_str3);
- EXPECT_EQ(tc_str3, tc3.ToString());
- EXPECT_TRUE(tc3.IsCategoryGroupEnabled(MemoryDumpManager::kTraceCategory));
- ASSERT_EQ(1u, tc3.memory_dump_config_.triggers.size());
- EXPECT_EQ(1u, tc3.memory_dump_config_.triggers[0].min_time_between_dumps_ms);
+ TraceConfig tc2(tc_str2);
+ EXPECT_EQ(tc_str2, tc2.ToString());
+ EXPECT_TRUE(tc2.IsCategoryGroupEnabled(MemoryDumpManager::kTraceCategory));
+ ASSERT_EQ(1u, tc2.memory_dump_config_.triggers.size());
+ EXPECT_EQ(1u, tc2.memory_dump_config_.triggers[0].periodic_interval_ms);
EXPECT_EQ(MemoryDumpLevelOfDetail::BACKGROUND,
- tc3.memory_dump_config_.triggers[0].level_of_detail);
-
- std::string tc_str4 =
- TraceConfigMemoryTestUtil::GetTraceConfig_PeakDetectionTrigger(
- 1 /*heavy_period */);
- TraceConfig tc4(tc_str4);
- EXPECT_EQ(tc_str4, tc4.ToString());
- ASSERT_EQ(1u, tc4.memory_dump_config_.triggers.size());
- EXPECT_EQ(1u, tc4.memory_dump_config_.triggers[0].min_time_between_dumps_ms);
- EXPECT_EQ(MemoryDumpLevelOfDetail::DETAILED,
- tc4.memory_dump_config_.triggers[0].level_of_detail);
+ tc2.memory_dump_config_.triggers[0].level_of_detail);
}
TEST(TraceConfigTest, EmptyMemoryDumpConfigTest) {
diff --git a/base/trace_event/trace_event.gypi b/base/trace_event/trace_event.gypi
new file mode 100644
index 0000000000..f915780de5
--- /dev/null
+++ b/base/trace_event/trace_event.gypi
@@ -0,0 +1,107 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+{
+ 'variables': {
+ 'trace_event_sources' : [
+ 'trace_event/blame_context.cc',
+ 'trace_event/blame_context.h',
+ 'trace_event/common/trace_event_common.h',
+ 'trace_event/heap_profiler.h',
+ 'trace_event/heap_profiler_allocation_context.cc',
+ 'trace_event/heap_profiler_allocation_context.h',
+ 'trace_event/heap_profiler_allocation_context_tracker.cc',
+ 'trace_event/heap_profiler_allocation_context_tracker.h',
+ 'trace_event/heap_profiler_allocation_register.cc',
+ 'trace_event/heap_profiler_allocation_register_posix.cc',
+ 'trace_event/heap_profiler_allocation_register_win.cc',
+ 'trace_event/heap_profiler_allocation_register.h',
+ 'trace_event/heap_profiler_heap_dump_writer.cc',
+ 'trace_event/heap_profiler_heap_dump_writer.h',
+ 'trace_event/heap_profiler_stack_frame_deduplicator.cc',
+ 'trace_event/heap_profiler_stack_frame_deduplicator.h',
+ 'trace_event/heap_profiler_type_name_deduplicator.cc',
+ 'trace_event/heap_profiler_type_name_deduplicator.h',
+ 'trace_event/java_heap_dump_provider_android.cc',
+ 'trace_event/java_heap_dump_provider_android.h',
+ 'trace_event/memory_allocator_dump.cc',
+ 'trace_event/memory_allocator_dump.h',
+ 'trace_event/memory_allocator_dump_guid.cc',
+ 'trace_event/memory_allocator_dump_guid.h',
+ 'trace_event/memory_dump_manager.cc',
+ 'trace_event/memory_dump_manager.h',
+ 'trace_event/memory_dump_provider.h',
+ 'trace_event/memory_dump_request_args.cc',
+ 'trace_event/memory_dump_request_args.h',
+ 'trace_event/memory_dump_session_state.cc',
+ 'trace_event/memory_dump_session_state.h',
+ 'trace_event/memory_infra_background_whitelist.cc',
+ 'trace_event/memory_infra_background_whitelist.h',
+ 'trace_event/process_memory_dump.cc',
+ 'trace_event/process_memory_dump.h',
+ 'trace_event/process_memory_maps.cc',
+ 'trace_event/process_memory_maps.h',
+ 'trace_event/process_memory_totals.cc',
+ 'trace_event/process_memory_totals.h',
+ 'trace_event/trace_buffer.cc',
+ 'trace_event/trace_buffer.h',
+ 'trace_event/trace_config.cc',
+ 'trace_event/trace_config.h',
+ 'trace_event/trace_event.h',
+ 'trace_event/trace_event_android.cc',
+ 'trace_event/trace_event_argument.cc',
+ 'trace_event/trace_event_argument.h',
+ 'trace_event/trace_event_etw_export_win.cc',
+ 'trace_event/trace_event_etw_export_win.h',
+ 'trace_event/trace_event_impl.cc',
+ 'trace_event/trace_event_impl.h',
+ 'trace_event/trace_event_memory_overhead.cc',
+ 'trace_event/trace_event_memory_overhead.h',
+ 'trace_event/trace_event_synthetic_delay.cc',
+ 'trace_event/trace_event_synthetic_delay.h',
+ 'trace_event/trace_event_system_stats_monitor.cc',
+ 'trace_event/trace_event_system_stats_monitor.h',
+ 'trace_event/trace_log.cc',
+ 'trace_event/trace_log.h',
+ 'trace_event/trace_log_constants.cc',
+ 'trace_event/trace_sampling_thread.cc',
+ 'trace_event/trace_sampling_thread.h',
+ 'trace_event/tracing_agent.cc',
+ 'trace_event/tracing_agent.h',
+ 'trace_event/winheap_dump_provider_win.cc',
+ 'trace_event/winheap_dump_provider_win.h',
+ ],
+ 'trace_event_test_sources' : [
+ 'trace_event/blame_context_unittest.cc',
+ 'trace_event/heap_profiler_allocation_context_tracker_unittest.cc',
+ 'trace_event/heap_profiler_allocation_register_unittest.cc',
+ 'trace_event/heap_profiler_heap_dump_writer_unittest.cc',
+ 'trace_event/heap_profiler_stack_frame_deduplicator_unittest.cc',
+ 'trace_event/heap_profiler_type_name_deduplicator_unittest.cc',
+ 'trace_event/java_heap_dump_provider_android_unittest.cc',
+ 'trace_event/memory_allocator_dump_unittest.cc',
+ 'trace_event/memory_dump_manager_unittest.cc',
+ 'trace_event/process_memory_dump_unittest.cc',
+ 'trace_event/trace_config_memory_test_util.h',
+ 'trace_event/trace_config_unittest.cc',
+ 'trace_event/trace_event_argument_unittest.cc',
+ 'trace_event/trace_event_synthetic_delay_unittest.cc',
+ 'trace_event/trace_event_system_stats_monitor_unittest.cc',
+ 'trace_event/trace_event_unittest.cc',
+ 'trace_event/winheap_dump_provider_win_unittest.cc',
+ ],
+ 'conditions': [
+ ['OS == "linux" or OS=="android" or OS=="mac" or OS=="ios"', {
+ 'trace_event_sources': [
+ 'trace_event/malloc_dump_provider.cc',
+ 'trace_event/malloc_dump_provider.h',
+ ],
+ }],
+ ['OS == "android"', {
+ 'trace_event_test_sources' : [
+ 'trace_event/trace_event_android_unittest.cc',
+ ],
+ }],
+ ],
+ },
+}
diff --git a/base/trace_event/trace_event.h b/base/trace_event/trace_event.h
index 51e6927cbd..a075898269 100644
--- a/base/trace_event/trace_event.h
+++ b/base/trace_event/trace_event.h
@@ -19,7 +19,6 @@
#include "base/time/time.h"
#include "base/trace_event/common/trace_event_common.h"
#include "base/trace_event/heap_profiler.h"
-#include "base/trace_event/trace_category.h"
#include "base/trace_event/trace_event_system_stats_monitor.h"
#include "base/trace_event/trace_log.h"
#include "build/build_config.h"
@@ -29,52 +28,55 @@
#define TRACE_STR_COPY(str) \
trace_event_internal::TraceStringWithCopy(str)
-// DEPRECATED: do not use: Consider using TRACE_ID_{GLOBAL, LOCAL} macros,
-// instead. By default, uint64_t ID argument values are not mangled with the
-// Process ID in TRACE_EVENT_ASYNC macros. Use this macro to force Process ID
-// mangling.
+// By default, uint64_t ID argument values are not mangled with the Process ID
+// in TRACE_EVENT_ASYNC macros. Use this macro to force Process ID mangling.
#define TRACE_ID_MANGLE(id) \
trace_event_internal::TraceID::ForceMangle(id)
-// DEPRECATED: do not use: Consider using TRACE_ID_{GLOBAL, LOCAL} macros,
-// instead. By default, pointers are mangled with the Process ID in
-// TRACE_EVENT_ASYNC macros. Use this macro to prevent Process ID mangling.
+// By default, pointers are mangled with the Process ID in TRACE_EVENT_ASYNC
+// macros. Use this macro to prevent Process ID mangling.
#define TRACE_ID_DONT_MANGLE(id) \
trace_event_internal::TraceID::DontMangle(id)
// By default, trace IDs are eventually converted to a single 64-bit number. Use
-// this macro to add a scope string. For example,
+// this macro to add a scope string.
+#define TRACE_ID_WITH_SCOPE(scope, id) \
+ trace_event_internal::TraceID::WithScope(scope, id)
+
+// Sets the current sample state to the given category and name (both must be
+// constant strings). These states are intended for a sampling profiler.
+// Implementation note: we store category and name together because we don't
+// want the inconsistency/expense of storing two pointers.
+// |thread_bucket| is [0..2] and is used to statically isolate samples in one
+// thread from others.
+#define TRACE_EVENT_SET_SAMPLING_STATE_FOR_BUCKET( \
+ bucket_number, category, name) \
+ trace_event_internal:: \
+ TraceEventSamplingStateScope<bucket_number>::Set(category "\0" name)
+
+// Returns a current sampling state of the given bucket.
+#define TRACE_EVENT_GET_SAMPLING_STATE_FOR_BUCKET(bucket_number) \
+ trace_event_internal::TraceEventSamplingStateScope<bucket_number>::Current()
+
+// Creates a scope of a sampling state of the given bucket.
//
-// TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(
-// "network", "ResourceLoad",
-// TRACE_ID_WITH_SCOPE("BlinkResourceID", resourceID));
-//
-// Also, it is possible to prepend the ID with another number, like the process
-// ID. This is useful in creatin IDs that are unique among all processes. To do
-// that, pass two numbers after the scope string instead of one. For example,
-//
-// TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(
-// "network", "ResourceLoad",
-// TRACE_ID_WITH_SCOPE("BlinkResourceID", pid, resourceID));
-#define TRACE_ID_WITH_SCOPE(scope, ...) \
- trace_event_internal::TraceID::WithScope(scope, ##__VA_ARGS__)
-
-#define TRACE_ID_GLOBAL(id) trace_event_internal::TraceID::GlobalId(id)
-#define TRACE_ID_LOCAL(id) trace_event_internal::TraceID::LocalId(id)
+// { // The sampling state is set within this scope.
+// TRACE_EVENT_SAMPLING_STATE_SCOPE_FOR_BUCKET(0, "category", "name");
+// ...;
+// }
+#define TRACE_EVENT_SCOPED_SAMPLING_STATE_FOR_BUCKET( \
+ bucket_number, category, name) \
+ trace_event_internal::TraceEventSamplingStateScope<bucket_number> \
+ traceEventSamplingScope(category "\0" name);
#define TRACE_EVENT_API_CURRENT_THREAD_ID \
static_cast<int>(base::PlatformThread::CurrentId())
#define INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE() \
UNLIKELY(*INTERNAL_TRACE_EVENT_UID(category_group_enabled) & \
- (base::trace_event::TraceCategory::ENABLED_FOR_RECORDING | \
- base::trace_event::TraceCategory::ENABLED_FOR_ETW_EXPORT))
-
-#define INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED() \
- UNLIKELY(*INTERNAL_TRACE_EVENT_UID(category_group_enabled) & \
- (base::trace_event::TraceCategory::ENABLED_FOR_RECORDING | \
- base::trace_event::TraceCategory::ENABLED_FOR_ETW_EXPORT | \
- base::trace_event::TraceCategory::ENABLED_FOR_FILTERING))
+ (base::trace_event::TraceLog::ENABLED_FOR_RECORDING | \
+ base::trace_event::TraceLog::ENABLED_FOR_EVENT_CALLBACK | \
+ base::trace_event::TraceLog::ENABLED_FOR_ETW_EXPORT))
////////////////////////////////////////////////////////////////////////////////
// Implementation specific tracing API definitions.
@@ -202,6 +204,13 @@
// Defines visibility for classes in trace_event.h
#define TRACE_EVENT_API_CLASS_EXPORT BASE_EXPORT
+// The thread buckets for the sampling profiler.
+TRACE_EVENT_API_CLASS_EXPORT extern \
+ TRACE_EVENT_API_ATOMIC_WORD g_trace_state[3];
+
+#define TRACE_EVENT_API_THREAD_BUCKET(thread_bucket) \
+ g_trace_state[thread_bucket]
+
////////////////////////////////////////////////////////////////////////////////
// Implementation detail: trace event macros create temporary variables
@@ -240,69 +249,69 @@
// Implementation detail: internal macro to create static category and add
// event if the category is enabled.
-#define INTERNAL_TRACE_EVENT_ADD(phase, category_group, name, flags, ...) \
- do { \
- INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
- if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED()) { \
- trace_event_internal::AddTraceEvent( \
- phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \
- trace_event_internal::kGlobalScope, trace_event_internal::kNoId, \
- flags, trace_event_internal::kNoId, ##__VA_ARGS__); \
- } \
- } while (0)
+#define INTERNAL_TRACE_EVENT_ADD(phase, category_group, name, flags, ...) \
+ do { \
+ INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
+ if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \
+ trace_event_internal::AddTraceEvent( \
+ phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \
+ trace_event_internal::kGlobalScope, trace_event_internal::kNoId, \
+ flags, trace_event_internal::kNoId, ##__VA_ARGS__); \
+ } \
+ } while (0)
// Implementation detail: internal macro to create static category and add begin
// event if the category is enabled. Also adds the end event when the scope
// ends.
-#define INTERNAL_TRACE_EVENT_ADD_SCOPED(category_group, name, ...) \
- INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
- trace_event_internal::ScopedTracer INTERNAL_TRACE_EVENT_UID(tracer); \
- if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED()) { \
- base::trace_event::TraceEventHandle h = \
- trace_event_internal::AddTraceEvent( \
- TRACE_EVENT_PHASE_COMPLETE, \
- INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \
- trace_event_internal::kGlobalScope, trace_event_internal::kNoId, \
- TRACE_EVENT_FLAG_NONE, trace_event_internal::kNoId, \
- ##__VA_ARGS__); \
- INTERNAL_TRACE_EVENT_UID(tracer).Initialize( \
- INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, h); \
- }
+#define INTERNAL_TRACE_EVENT_ADD_SCOPED(category_group, name, ...) \
+ INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
+ trace_event_internal::ScopedTracer INTERNAL_TRACE_EVENT_UID(tracer); \
+ if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \
+ base::trace_event::TraceEventHandle h = \
+ trace_event_internal::AddTraceEvent( \
+ TRACE_EVENT_PHASE_COMPLETE, \
+ INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \
+ trace_event_internal::kGlobalScope, trace_event_internal::kNoId, \
+ TRACE_EVENT_FLAG_NONE, trace_event_internal::kNoId, \
+ ##__VA_ARGS__); \
+ INTERNAL_TRACE_EVENT_UID(tracer).Initialize( \
+ INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, h); \
+ }
-#define INTERNAL_TRACE_EVENT_ADD_SCOPED_WITH_FLOW(category_group, name, \
- bind_id, flow_flags, ...) \
- INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
- trace_event_internal::ScopedTracer INTERNAL_TRACE_EVENT_UID(tracer); \
- if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED()) { \
- trace_event_internal::TraceID trace_event_bind_id((bind_id)); \
- unsigned int trace_event_flags = \
- flow_flags | trace_event_bind_id.id_flags(); \
- base::trace_event::TraceEventHandle h = \
- trace_event_internal::AddTraceEvent( \
- TRACE_EVENT_PHASE_COMPLETE, \
- INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \
+#define INTERNAL_TRACE_EVENT_ADD_SCOPED_WITH_FLOW( \
+ category_group, name, bind_id, flow_flags, ...) \
+ INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
+ trace_event_internal::ScopedTracer INTERNAL_TRACE_EVENT_UID(tracer); \
+ if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \
+ unsigned int trace_event_flags = flow_flags; \
+ trace_event_internal::TraceID trace_event_bind_id(bind_id, \
+ &trace_event_flags); \
+ base::trace_event::TraceEventHandle h = \
+ trace_event_internal::AddTraceEvent( \
+ TRACE_EVENT_PHASE_COMPLETE, \
+ INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \
trace_event_internal::kGlobalScope, trace_event_internal::kNoId, \
trace_event_flags, trace_event_bind_id.raw_id(), ##__VA_ARGS__); \
- INTERNAL_TRACE_EVENT_UID(tracer).Initialize( \
- INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, h); \
+ INTERNAL_TRACE_EVENT_UID(tracer).Initialize( \
+ INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, h); \
}
// Implementation detail: internal macro to create static category and add
// event if the category is enabled.
#define INTERNAL_TRACE_EVENT_ADD_WITH_ID(phase, category_group, name, id, \
- flags, ...) \
- do { \
- INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
- if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED()) { \
- trace_event_internal::TraceID trace_event_trace_id((id)); \
- unsigned int trace_event_flags = \
- flags | trace_event_trace_id.id_flags(); \
- trace_event_internal::AddTraceEvent( \
- phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \
- trace_event_trace_id.scope(), trace_event_trace_id.raw_id(), \
- trace_event_flags, trace_event_internal::kNoId, ##__VA_ARGS__); \
- } \
- } while (0)
+ flags, ...) \
+ do { \
+ INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
+ if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \
+ unsigned int trace_event_flags = flags | TRACE_EVENT_FLAG_HAS_ID; \
+ trace_event_internal::TraceID trace_event_trace_id( \
+ id, &trace_event_flags); \
+ trace_event_internal::AddTraceEvent( \
+ phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), \
+ name, trace_event_trace_id.scope(), trace_event_trace_id.raw_id(), \
+ trace_event_flags, trace_event_internal::kNoId, ##__VA_ARGS__); \
+ } \
+ } while (0)
// Implementation detail: internal macro to create static category and add
// event if the category is enabled.
@@ -310,11 +319,12 @@
timestamp, flags, ...) \
do { \
INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
- if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED()) { \
+ if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \
trace_event_internal::AddTraceEventWithThreadIdAndTimestamp( \
phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \
trace_event_internal::kGlobalScope, trace_event_internal::kNoId, \
- TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, \
+ TRACE_EVENT_API_CURRENT_THREAD_ID, \
+ base::TimeTicks::FromInternalValue(timestamp), \
flags | TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP, \
trace_event_internal::kNoId, ##__VA_ARGS__); \
} \
@@ -322,50 +332,33 @@
// Implementation detail: internal macro to create static category and add
// event if the category is enabled.
-#define INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \
- phase, category_group, name, id, thread_id, timestamp, flags, ...) \
- do { \
- INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
- if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED()) { \
- trace_event_internal::TraceID trace_event_trace_id((id)); \
- unsigned int trace_event_flags = \
- flags | trace_event_trace_id.id_flags(); \
- trace_event_internal::AddTraceEventWithThreadIdAndTimestamp( \
- phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \
- trace_event_trace_id.scope(), trace_event_trace_id.raw_id(), \
- thread_id, timestamp, \
- trace_event_flags | TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP, \
- trace_event_internal::kNoId, ##__VA_ARGS__); \
- } \
- } while (0)
-
-// The linked ID will not be mangled.
-#define INTERNAL_TRACE_EVENT_ADD_LINK_IDS(category_group, name, id1, id2) \
- do { \
- INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
- if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED()) { \
- trace_event_internal::TraceID source_id((id1)); \
- unsigned int source_flags = source_id.id_flags(); \
- trace_event_internal::TraceID target_id((id2)); \
- trace_event_internal::AddTraceEvent( \
- TRACE_EVENT_PHASE_LINK_IDS, \
- INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \
- source_id.scope(), source_id.raw_id(), source_flags, \
- trace_event_internal::kNoId, "linked_id", \
- target_id.AsConvertableToTraceFormat()); \
- } \
+#define INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \
+ phase, category_group, name, id, thread_id, timestamp, flags, ...) \
+ do { \
+ INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
+ if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \
+ unsigned int trace_event_flags = flags | TRACE_EVENT_FLAG_HAS_ID; \
+ trace_event_internal::TraceID trace_event_trace_id(id, \
+ &trace_event_flags); \
+ trace_event_internal::AddTraceEventWithThreadIdAndTimestamp( \
+ phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \
+ trace_event_trace_id.scope(), trace_event_trace_id.raw_id(), \
+ thread_id, base::TimeTicks::FromInternalValue(timestamp), \
+ trace_event_flags | TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP, \
+ trace_event_internal::kNoId, ##__VA_ARGS__); \
+ } \
} while (0)
// Implementation detail: internal macro to create static category and add
// metadata event if the category is enabled.
-#define INTERNAL_TRACE_EVENT_METADATA_ADD(category_group, name, ...) \
- do { \
- INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
- if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED()) { \
- TRACE_EVENT_API_ADD_METADATA_EVENT( \
- INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \
- ##__VA_ARGS__); \
- } \
+#define INTERNAL_TRACE_EVENT_METADATA_ADD(category_group, name, ...) \
+ do { \
+ INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
+ if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \
+ TRACE_EVENT_API_ADD_METADATA_EVENT( \
+ INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \
+ ##__VA_ARGS__); \
+ } \
} while (0)
// Implementation detail: internal macro to enter and leave a
@@ -388,7 +381,7 @@
void operator=(const INTERNAL_TRACE_EVENT_UID(ScopedContext)&) {}; \
}; \
INTERNAL_TRACE_EVENT_UID(ScopedContext) \
- INTERNAL_TRACE_EVENT_UID(scoped_context)(context);
+ INTERNAL_TRACE_EVENT_UID(scoped_context)(context.raw_id());
// Implementation detail: internal macro to trace a task execution with the
// location where it was posted from.
@@ -410,64 +403,19 @@ const unsigned long long kNoId = 0;
// TraceID encapsulates an ID that can either be an integer or pointer. Pointers
// are by default mangled with the Process ID so that they are unlikely to
// collide when the same pointer is used on different processes.
-class BASE_EXPORT TraceID {
+class TraceID {
public:
- // Can be combined with WithScope.
- class LocalId {
- public:
- explicit LocalId(unsigned long long raw_id) : raw_id_(raw_id) {}
- unsigned long long raw_id() const { return raw_id_; }
- private:
- unsigned long long raw_id_;
- };
-
- // Can be combined with WithScope.
- class GlobalId {
- public:
- explicit GlobalId(unsigned long long raw_id) : raw_id_(raw_id) {}
- unsigned long long raw_id() const { return raw_id_; }
- private:
- unsigned long long raw_id_;
- };
-
class WithScope {
public:
WithScope(const char* scope, unsigned long long raw_id)
: scope_(scope), raw_id_(raw_id) {}
- WithScope(const char* scope, LocalId local_id)
- : scope_(scope), raw_id_(local_id.raw_id()) {
- id_flags_ = TRACE_EVENT_FLAG_HAS_LOCAL_ID;
- }
- WithScope(const char* scope, GlobalId global_id)
- : scope_(scope), raw_id_(global_id.raw_id()) {
- id_flags_ = TRACE_EVENT_FLAG_HAS_GLOBAL_ID;
- }
- WithScope(const char* scope,
- unsigned long long prefix,
- unsigned long long raw_id)
- : scope_(scope), has_prefix_(true), prefix_(prefix), raw_id_(raw_id) {}
- WithScope(const char* scope, unsigned long long prefix, GlobalId global_id)
- : scope_(scope),
- has_prefix_(true),
- prefix_(prefix),
- raw_id_(global_id.raw_id()) {
- id_flags_ = TRACE_EVENT_FLAG_HAS_GLOBAL_ID;
- }
unsigned long long raw_id() const { return raw_id_; }
const char* scope() const { return scope_; }
- bool has_prefix() const { return has_prefix_; }
- unsigned long long prefix() const { return prefix_; }
- unsigned int id_flags() const { return id_flags_; }
-
private:
const char* scope_ = nullptr;
- bool has_prefix_ = false;
- unsigned long long prefix_;
unsigned long long raw_id_;
- unsigned int id_flags_ = TRACE_EVENT_FLAG_HAS_ID;
};
- // DEPRECATED: consider using LocalId or GlobalId, instead.
class DontMangle {
public:
explicit DontMangle(const void* raw_id)
@@ -488,12 +436,15 @@ class BASE_EXPORT TraceID {
: raw_id_(static_cast<unsigned long long>(raw_id)) {}
explicit DontMangle(signed char raw_id)
: raw_id_(static_cast<unsigned long long>(raw_id)) {}
+ explicit DontMangle(WithScope scoped_id)
+ : scope_(scoped_id.scope()), raw_id_(scoped_id.raw_id()) {}
+ const char* scope() const { return scope_; }
unsigned long long raw_id() const { return raw_id_; }
private:
+ const char* scope_ = nullptr;
unsigned long long raw_id_;
};
- // DEPRECATED: consider using LocalId or GlobalId, instead.
class ForceMangle {
public:
explicit ForceMangle(unsigned long long raw_id) : raw_id_(raw_id) {}
@@ -515,58 +466,50 @@ class BASE_EXPORT TraceID {
private:
unsigned long long raw_id_;
};
-
- TraceID(const void* raw_id) : raw_id_(static_cast<unsigned long long>(
- reinterpret_cast<uintptr_t>(raw_id))) {
- id_flags_ = TRACE_EVENT_FLAG_HAS_ID | TRACE_EVENT_FLAG_MANGLE_ID;
+ TraceID(const void* raw_id, unsigned int* flags)
+ : raw_id_(static_cast<unsigned long long>(
+ reinterpret_cast<uintptr_t>(raw_id))) {
+ *flags |= TRACE_EVENT_FLAG_MANGLE_ID;
+ }
+ TraceID(ForceMangle raw_id, unsigned int* flags) : raw_id_(raw_id.raw_id()) {
+ *flags |= TRACE_EVENT_FLAG_MANGLE_ID;
+ }
+ TraceID(DontMangle maybe_scoped_id, unsigned int* /*flags*/)
+ : scope_(maybe_scoped_id.scope()), raw_id_(maybe_scoped_id.raw_id()) {}
+ TraceID(unsigned long long raw_id, unsigned int* flags) : raw_id_(raw_id) {
+ (void)flags;
}
- TraceID(ForceMangle raw_id) : raw_id_(raw_id.raw_id()) {
- id_flags_ = TRACE_EVENT_FLAG_HAS_ID | TRACE_EVENT_FLAG_MANGLE_ID;
+ TraceID(unsigned long raw_id, unsigned int* flags) : raw_id_(raw_id) {
+ (void)flags;
}
- TraceID(DontMangle raw_id) : raw_id_(raw_id.raw_id()) {}
- TraceID(unsigned long long raw_id) : raw_id_(raw_id) {}
- TraceID(unsigned long raw_id) : raw_id_(raw_id) {}
- TraceID(unsigned int raw_id) : raw_id_(raw_id) {}
- TraceID(unsigned short raw_id) : raw_id_(raw_id) {}
- TraceID(unsigned char raw_id) : raw_id_(raw_id) {}
- TraceID(long long raw_id)
- : raw_id_(static_cast<unsigned long long>(raw_id)) {}
- TraceID(long raw_id)
- : raw_id_(static_cast<unsigned long long>(raw_id)) {}
- TraceID(int raw_id)
- : raw_id_(static_cast<unsigned long long>(raw_id)) {}
- TraceID(short raw_id)
- : raw_id_(static_cast<unsigned long long>(raw_id)) {}
- TraceID(signed char raw_id)
- : raw_id_(static_cast<unsigned long long>(raw_id)) {}
- TraceID(LocalId raw_id) : raw_id_(raw_id.raw_id()) {
- id_flags_ = TRACE_EVENT_FLAG_HAS_LOCAL_ID;
+ TraceID(unsigned int raw_id, unsigned int* flags) : raw_id_(raw_id) {
+ (void)flags;
}
- TraceID(GlobalId raw_id) : raw_id_(raw_id.raw_id()) {
- id_flags_ = TRACE_EVENT_FLAG_HAS_GLOBAL_ID;
+ TraceID(unsigned short raw_id, unsigned int* flags) : raw_id_(raw_id) {
+ (void)flags;
}
- TraceID(WithScope scoped_id)
- : scope_(scoped_id.scope()),
- has_prefix_(scoped_id.has_prefix()),
- prefix_(scoped_id.prefix()),
- raw_id_(scoped_id.raw_id()),
- id_flags_(scoped_id.id_flags()) {}
+ TraceID(unsigned char raw_id, unsigned int* flags) : raw_id_(raw_id) {
+ (void)flags;
+ }
+ TraceID(long long raw_id, unsigned int* flags)
+ : raw_id_(static_cast<unsigned long long>(raw_id)) { (void)flags; }
+ TraceID(long raw_id, unsigned int* flags)
+ : raw_id_(static_cast<unsigned long long>(raw_id)) { (void)flags; }
+ TraceID(int raw_id, unsigned int* flags)
+ : raw_id_(static_cast<unsigned long long>(raw_id)) { (void)flags; }
+ TraceID(short raw_id, unsigned int* flags)
+ : raw_id_(static_cast<unsigned long long>(raw_id)) { (void)flags; }
+ TraceID(signed char raw_id, unsigned int* flags)
+ : raw_id_(static_cast<unsigned long long>(raw_id)) { (void)flags; }
+ TraceID(WithScope scoped_id, unsigned int* /*flags*/)
+ : scope_(scoped_id.scope()), raw_id_(scoped_id.raw_id()) {}
unsigned long long raw_id() const { return raw_id_; }
const char* scope() const { return scope_; }
- bool has_prefix() const { return has_prefix_; }
- unsigned long long prefix() const { return prefix_; }
- unsigned int id_flags() const { return id_flags_; }
-
- std::unique_ptr<base::trace_event::ConvertableToTraceFormat>
- AsConvertableToTraceFormat() const;
private:
const char* scope_ = nullptr;
- bool has_prefix_ = false;
- unsigned long long prefix_;
unsigned long long raw_id_;
- unsigned int id_flags_ = TRACE_EVENT_FLAG_HAS_ID;
};
// Simple union to store various types as unsigned long long.
@@ -1030,10 +973,9 @@ class TRACE_EVENT_API_CLASS_EXPORT ScopedTracer {
ScopedTracer() : p_data_(NULL) {}
~ScopedTracer() {
- if (p_data_ && *data_.category_group_enabled) {
+ if (p_data_ && *data_.category_group_enabled)
TRACE_EVENT_API_UPDATE_TRACE_EVENT_DURATION(
data_.category_group_enabled, data_.name, data_.event_handle);
- }
}
void Initialize(const unsigned char* category_group_enabled,
@@ -1081,6 +1023,37 @@ class TRACE_EVENT_API_CLASS_EXPORT ScopedTraceBinaryEfficient {
trace_event_internal::ScopedTraceBinaryEfficient \
INTERNAL_TRACE_EVENT_UID(scoped_trace)(category_group, name);
+// TraceEventSamplingStateScope records the current sampling state
+// and sets a new sampling state. When the scope exists, it restores
+// the sampling state having recorded.
+template<size_t BucketNumber>
+class TraceEventSamplingStateScope {
+ public:
+ TraceEventSamplingStateScope(const char* category_and_name) {
+ previous_state_ = TraceEventSamplingStateScope<BucketNumber>::Current();
+ TraceEventSamplingStateScope<BucketNumber>::Set(category_and_name);
+ }
+
+ ~TraceEventSamplingStateScope() {
+ TraceEventSamplingStateScope<BucketNumber>::Set(previous_state_);
+ }
+
+ static inline const char* Current() {
+ return reinterpret_cast<const char*>(TRACE_EVENT_API_ATOMIC_LOAD(
+ g_trace_state[BucketNumber]));
+ }
+
+ static inline void Set(const char* category_and_name) {
+ TRACE_EVENT_API_ATOMIC_STORE(
+ g_trace_state[BucketNumber],
+ reinterpret_cast<TRACE_EVENT_API_ATOMIC_WORD>(
+ const_cast<char*>(category_and_name)));
+ }
+
+ private:
+ const char* previous_state_;
+};
+
} // namespace trace_event_internal
namespace base {
diff --git a/base/trace_event/trace_event_argument.cc b/base/trace_event/trace_event_argument.cc
index db702b6231..336d964bff 100644
--- a/base/trace_event/trace_event_argument.cc
+++ b/base/trace_event/trace_event_argument.cc
@@ -244,36 +244,36 @@ void TracedValue::SetBaseValueWithCopiedName(base::StringPiece name,
const base::Value& value) {
DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
switch (value.GetType()) {
- case base::Value::Type::NONE:
- case base::Value::Type::BINARY:
+ case base::Value::TYPE_NULL:
+ case base::Value::TYPE_BINARY:
NOTREACHED();
break;
- case base::Value::Type::BOOLEAN: {
+ case base::Value::TYPE_BOOLEAN: {
bool bool_value;
value.GetAsBoolean(&bool_value);
SetBooleanWithCopiedName(name, bool_value);
} break;
- case base::Value::Type::INTEGER: {
+ case base::Value::TYPE_INTEGER: {
int int_value;
value.GetAsInteger(&int_value);
SetIntegerWithCopiedName(name, int_value);
} break;
- case base::Value::Type::DOUBLE: {
+ case base::Value::TYPE_DOUBLE: {
double double_value;
value.GetAsDouble(&double_value);
SetDoubleWithCopiedName(name, double_value);
} break;
- case base::Value::Type::STRING: {
- const Value* string_value;
+ case base::Value::TYPE_STRING: {
+ const StringValue* string_value;
value.GetAsString(&string_value);
SetStringWithCopiedName(name, string_value->GetString());
} break;
- case base::Value::Type::DICTIONARY: {
+ case base::Value::TYPE_DICTIONARY: {
const DictionaryValue* dict_value;
value.GetAsDictionary(&dict_value);
BeginDictionaryWithCopiedName(name);
@@ -284,7 +284,7 @@ void TracedValue::SetBaseValueWithCopiedName(base::StringPiece name,
EndDictionary();
} break;
- case base::Value::Type::LIST: {
+ case base::Value::TYPE_LIST: {
const ListValue* list_value;
value.GetAsList(&list_value);
BeginArrayWithCopiedName(name);
@@ -298,36 +298,36 @@ void TracedValue::SetBaseValueWithCopiedName(base::StringPiece name,
void TracedValue::AppendBaseValue(const base::Value& value) {
DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
switch (value.GetType()) {
- case base::Value::Type::NONE:
- case base::Value::Type::BINARY:
+ case base::Value::TYPE_NULL:
+ case base::Value::TYPE_BINARY:
NOTREACHED();
break;
- case base::Value::Type::BOOLEAN: {
+ case base::Value::TYPE_BOOLEAN: {
bool bool_value;
value.GetAsBoolean(&bool_value);
AppendBoolean(bool_value);
} break;
- case base::Value::Type::INTEGER: {
+ case base::Value::TYPE_INTEGER: {
int int_value;
value.GetAsInteger(&int_value);
AppendInteger(int_value);
} break;
- case base::Value::Type::DOUBLE: {
+ case base::Value::TYPE_DOUBLE: {
double double_value;
value.GetAsDouble(&double_value);
AppendDouble(double_value);
} break;
- case base::Value::Type::STRING: {
- const Value* string_value;
+ case base::Value::TYPE_STRING: {
+ const StringValue* string_value;
value.GetAsString(&string_value);
AppendString(string_value->GetString());
} break;
- case base::Value::Type::DICTIONARY: {
+ case base::Value::TYPE_DICTIONARY: {
const DictionaryValue* dict_value;
value.GetAsDictionary(&dict_value);
BeginDictionary();
@@ -338,7 +338,7 @@ void TracedValue::AppendBaseValue(const base::Value& value) {
EndDictionary();
} break;
- case base::Value::Type::LIST: {
+ case base::Value::TYPE_LIST: {
const ListValue* list_value;
value.GetAsList(&list_value);
BeginArray();
diff --git a/base/trace_event/trace_event_argument_unittest.cc b/base/trace_event/trace_event_argument_unittest.cc
index aef8441c8e..61395f4d55 100644
--- a/base/trace_event/trace_event_argument_unittest.cc
+++ b/base/trace_event/trace_event_argument_unittest.cc
@@ -97,9 +97,9 @@ TEST(TraceEventArgumentTest, LongStrings) {
}
TEST(TraceEventArgumentTest, PassBaseValue) {
- Value int_value(42);
- Value bool_value(true);
- Value double_value(42.0f);
+ FundamentalValue int_value(42);
+ FundamentalValue bool_value(true);
+ FundamentalValue double_value(42.0f);
auto dict_value = WrapUnique(new DictionaryValue);
dict_value->SetBoolean("bool", true);
@@ -131,10 +131,10 @@ TEST(TraceEventArgumentTest, PassBaseValue) {
}
TEST(TraceEventArgumentTest, PassTracedValue) {
- auto dict_value = MakeUnique<TracedValue>();
+ auto dict_value = WrapUnique(new TracedValue());
dict_value->SetInteger("a", 1);
- auto nested_dict_value = MakeUnique<TracedValue>();
+ auto nested_dict_value = WrapUnique(new TracedValue());
nested_dict_value->SetInteger("b", 2);
nested_dict_value->BeginArray("c");
nested_dict_value->AppendString("foo");
diff --git a/base/trace_event/trace_event_filter.cc b/base/trace_event/trace_event_filter.cc
deleted file mode 100644
index d50c5fe251..0000000000
--- a/base/trace_event/trace_event_filter.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2016 The Chromium 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/compiler_specific.h"
-#include "base/trace_event/trace_event_filter.h"
-
-namespace base {
-namespace trace_event {
-
-TraceEventFilter::TraceEventFilter() {}
-TraceEventFilter::~TraceEventFilter() {}
-
-void TraceEventFilter::EndEvent(const char* category_name,
- const char* event_name) const {
- ALLOW_UNUSED_PARAM(category_name);
- ALLOW_UNUSED_PARAM(event_name);
-}
-
-} // namespace trace_event
-} // namespace base
diff --git a/base/trace_event/trace_event_filter.h b/base/trace_event/trace_event_filter.h
deleted file mode 100644
index 48c6711432..0000000000
--- a/base/trace_event/trace_event_filter.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2016 The Chromium 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_TRACE_EVENT_TRACE_EVENT_FILTER_H_
-#define BASE_TRACE_EVENT_TRACE_EVENT_FILTER_H_
-
-#include <memory>
-
-#include "base/base_export.h"
-#include "base/macros.h"
-
-namespace base {
-namespace trace_event {
-
-class TraceEvent;
-
-// TraceEventFilter is like iptables for TRACE_EVENT macros. Filters can be
-// enabled on a per-category basis, hence a single filter instance can serve
-// more than a TraceCategory. There are two use cases for filters:
-// 1. Snooping TRACE_EVENT macros without adding them to the TraceLog. This is
-// possible by setting the ENABLED_FOR_FILTERING flag on a category w/o
-// ENABLED_FOR_RECORDING (see TraceConfig for user-facing configuration).
-// 2. Filtering TRACE_EVENT macros before they are added to the TraceLog. This
-// requires both the ENABLED_FOR_FILTERING and ENABLED_FOR_RECORDING flags
-// on the category.
-// More importantly, filters must be thread-safe. The FilterTraceEvent and
-// EndEvent methods can be called concurrently as trace macros are hit on
-// different threads.
-class BASE_EXPORT TraceEventFilter {
- public:
- TraceEventFilter();
- virtual ~TraceEventFilter();
-
- // If the category is ENABLED_FOR_RECORDING, the event is added iff all the
- // filters enabled for the category return true. false causes the event to be
- // discarded.
- virtual bool FilterTraceEvent(const TraceEvent& trace_event) const = 0;
-
- // Notifies the end of a duration event when the RAII macro goes out of scope.
- virtual void EndEvent(const char* category_name,
- const char* event_name) const;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(TraceEventFilter);
-};
-
-} // namespace trace_event
-} // namespace base
-
-#endif // BASE_TRACE_EVENT_TRACE_EVENT_FILTER_H_
diff --git a/base/trace_event/trace_event_filter_test_utils.cc b/base/trace_event/trace_event_filter_test_utils.cc
deleted file mode 100644
index 06548b049a..0000000000
--- a/base/trace_event/trace_event_filter_test_utils.cc
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2016 The Chromium 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/trace_event/trace_event_filter_test_utils.h"
-
-#include "base/logging.h"
-
-namespace base {
-namespace trace_event {
-
-namespace {
-TestEventFilter::HitsCounter* g_hits_counter;
-} // namespace;
-
-// static
-const char TestEventFilter::kName[] = "testing_predicate";
-bool TestEventFilter::filter_return_value_;
-
-// static
-std::unique_ptr<TraceEventFilter> TestEventFilter::Factory(
- const std::string& predicate_name) {
- std::unique_ptr<TraceEventFilter> res;
- if (predicate_name == kName)
- res.reset(new TestEventFilter());
- return res;
-}
-
-TestEventFilter::TestEventFilter() {}
-TestEventFilter::~TestEventFilter() {}
-
-bool TestEventFilter::FilterTraceEvent(const TraceEvent& trace_event) const {
- if (g_hits_counter)
- g_hits_counter->filter_trace_event_hit_count++;
- return filter_return_value_;
-}
-
-void TestEventFilter::EndEvent(const char* category_name,
- const char* name) const {
- if (g_hits_counter)
- g_hits_counter->end_event_hit_count++;
-}
-
-TestEventFilter::HitsCounter::HitsCounter() {
- Reset();
- DCHECK(!g_hits_counter);
- g_hits_counter = this;
-}
-
-TestEventFilter::HitsCounter::~HitsCounter() {
- DCHECK(g_hits_counter);
- g_hits_counter = nullptr;
-}
-
-void TestEventFilter::HitsCounter::Reset() {
- filter_trace_event_hit_count = 0;
- end_event_hit_count = 0;
-}
-
-} // namespace trace_event
-} // namespace base
diff --git a/base/trace_event/trace_event_filter_test_utils.h b/base/trace_event/trace_event_filter_test_utils.h
deleted file mode 100644
index 419068b221..0000000000
--- a/base/trace_event/trace_event_filter_test_utils.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2016 The Chromium 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_TRACE_EVENT_TRACE_EVENT_FILTER_TEST_UTILS_H_
-#define BASE_TRACE_EVENT_TRACE_EVENT_FILTER_TEST_UTILS_H_
-
-#include <memory>
-#include <string>
-
-#include "base/macros.h"
-#include "base/trace_event/trace_event_filter.h"
-
-namespace base {
-namespace trace_event {
-
-class TestEventFilter : public TraceEventFilter {
- public:
- struct HitsCounter {
- HitsCounter();
- ~HitsCounter();
- void Reset();
- size_t filter_trace_event_hit_count;
- size_t end_event_hit_count;
- };
-
- static const char kName[];
-
- // Factory method for TraceLog::SetFilterFactoryForTesting().
- static std::unique_ptr<TraceEventFilter> Factory(
- const std::string& predicate_name);
-
- TestEventFilter();
- ~TestEventFilter() override;
-
- // TraceEventFilter implementation.
- bool FilterTraceEvent(const TraceEvent& trace_event) const override;
- void EndEvent(const char* category_name, const char* name) const override;
-
- static void set_filter_return_value(bool value) {
- filter_return_value_ = value;
- }
-
- private:
- static bool filter_return_value_;
-
- DISALLOW_COPY_AND_ASSIGN(TestEventFilter);
-};
-
-} // namespace trace_event
-} // namespace base
-
-#endif // BASE_TRACE_EVENT_TRACE_EVENT_FILTER_TEST_UTILS_H_
diff --git a/base/trace_event/trace_event_impl.cc b/base/trace_event/trace_event_impl.cc
index cb23eb474c..f469f2f6bc 100644
--- a/base/trace_event/trace_event_impl.cc
+++ b/base/trace_event/trace_event_impl.cc
@@ -8,7 +8,6 @@
#include "base/format_macros.h"
#include "base/json/string_escape.h"
-#include "base/memory/ptr_util.h"
#include "base/process/process_handle.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
@@ -16,7 +15,6 @@
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/trace_event/trace_event.h"
-#include "base/trace_event/trace_event_argument.h"
#include "base/trace_event/trace_log.h"
namespace base {
@@ -360,33 +358,10 @@ void TraceEvent::AppendAsJSON(
// If id_ is set, print it out as a hex string so we don't loose any
// bits (it might be a 64-bit pointer).
- unsigned int id_flags_ = flags_ & (TRACE_EVENT_FLAG_HAS_ID |
- TRACE_EVENT_FLAG_HAS_LOCAL_ID |
- TRACE_EVENT_FLAG_HAS_GLOBAL_ID);
- if (id_flags_) {
+ if (flags_ & TRACE_EVENT_FLAG_HAS_ID) {
if (scope_ != trace_event_internal::kGlobalScope)
StringAppendF(out, ",\"scope\":\"%s\"", scope_);
-
- switch (id_flags_) {
- case TRACE_EVENT_FLAG_HAS_ID:
- StringAppendF(out, ",\"id\":\"0x%" PRIx64 "\"",
- static_cast<uint64_t>(id_));
- break;
-
- case TRACE_EVENT_FLAG_HAS_LOCAL_ID:
- StringAppendF(out, ",\"id2\":{\"local\":\"0x%" PRIx64 "\"}",
- static_cast<uint64_t>(id_));
- break;
-
- case TRACE_EVENT_FLAG_HAS_GLOBAL_ID:
- StringAppendF(out, ",\"id2\":{\"global\":\"0x%" PRIx64 "\"}",
- static_cast<uint64_t>(id_));
- break;
-
- default:
- NOTREACHED() << "More than one of the ID flags are set";
- break;
- }
+ StringAppendF(out, ",\"id\":\"0x%" PRIx64 "\"", static_cast<uint64_t>(id_));
}
if (flags_ & TRACE_EVENT_FLAG_BIND_TO_ENCLOSING)
@@ -449,42 +424,3 @@ void TraceEvent::AppendPrettyPrinted(std::ostringstream* out) const {
} // namespace trace_event
} // namespace base
-
-namespace trace_event_internal {
-
-std::unique_ptr<base::trace_event::ConvertableToTraceFormat>
-TraceID::AsConvertableToTraceFormat() const {
- auto value = base::MakeUnique<base::trace_event::TracedValue>();
-
- if (scope_ != kGlobalScope)
- value->SetString("scope", scope_);
-
- const char* id_field_name = "id";
- if (id_flags_ == TRACE_EVENT_FLAG_HAS_GLOBAL_ID) {
- id_field_name = "global";
- value->BeginDictionary("id2");
- } else if (id_flags_ == TRACE_EVENT_FLAG_HAS_LOCAL_ID) {
- id_field_name = "local";
- value->BeginDictionary("id2");
- } else if (id_flags_ != TRACE_EVENT_FLAG_HAS_ID) {
- NOTREACHED() << "Unrecognized ID flag";
- }
-
- if (has_prefix_) {
- value->SetString(id_field_name,
- base::StringPrintf("0x%" PRIx64 "/0x%" PRIx64,
- static_cast<uint64_t>(prefix_),
- static_cast<uint64_t>(raw_id_)));
- } else {
- value->SetString(
- id_field_name,
- base::StringPrintf("0x%" PRIx64, static_cast<uint64_t>(raw_id_)));
- }
-
- if (id_flags_ != TRACE_EVENT_FLAG_HAS_ID)
- value->EndDictionary();
-
- return std::move(value);
-}
-
-} // namespace trace_event_internal
diff --git a/base/trace_event/trace_event_impl.h b/base/trace_event/trace_event_impl.h
index 5eef702fb9..4382217881 100644
--- a/base/trace_event/trace_event_impl.h
+++ b/base/trace_event/trace_event_impl.h
@@ -23,11 +23,16 @@
#include "base/strings/string_util.h"
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
+#include "base/threading/thread.h"
#include "base/threading/thread_local.h"
#include "base/trace_event/trace_event_memory_overhead.h"
#include "build/build_config.h"
namespace base {
+
+class WaitableEvent;
+class MessageLoop;
+
namespace trace_event {
typedef base::Callback<bool(const char* arg_name)> ArgumentNameFilterPredicate;
diff --git a/base/trace_event/trace_event_memory_overhead.cc b/base/trace_event/trace_event_memory_overhead.cc
index 8d56e1d80e..23579cbb22 100644
--- a/base/trace_event/trace_event_memory_overhead.cc
+++ b/base/trace_event/trace_event_memory_overhead.cc
@@ -69,27 +69,27 @@ void TraceEventMemoryOverhead::AddRefCountedString(
void TraceEventMemoryOverhead::AddValue(const Value& value) {
switch (value.GetType()) {
- case Value::Type::NONE:
- case Value::Type::BOOLEAN:
- case Value::Type::INTEGER:
- case Value::Type::DOUBLE:
+ case Value::TYPE_NULL:
+ case Value::TYPE_BOOLEAN:
+ case Value::TYPE_INTEGER:
+ case Value::TYPE_DOUBLE:
Add("FundamentalValue", sizeof(Value));
break;
- case Value::Type::STRING: {
- const Value* string_value = nullptr;
+ case Value::TYPE_STRING: {
+ const StringValue* string_value = nullptr;
value.GetAsString(&string_value);
- Add("StringValue", sizeof(Value));
+ Add("StringValue", sizeof(StringValue));
AddString(string_value->GetString());
} break;
- case Value::Type::BINARY: {
+ case Value::TYPE_BINARY: {
const BinaryValue* binary_value = nullptr;
value.GetAsBinary(&binary_value);
Add("BinaryValue", sizeof(BinaryValue) + binary_value->GetSize());
} break;
- case Value::Type::DICTIONARY: {
+ case Value::TYPE_DICTIONARY: {
const DictionaryValue* dictionary_value = nullptr;
value.GetAsDictionary(&dictionary_value);
Add("DictionaryValue", sizeof(DictionaryValue));
@@ -100,7 +100,7 @@ void TraceEventMemoryOverhead::AddValue(const Value& value) {
}
} break;
- case Value::Type::LIST: {
+ case Value::TYPE_LIST: {
const ListValue* list_value = nullptr;
value.GetAsList(&list_value);
Add("ListValue", sizeof(ListValue));
diff --git a/base/trace_event/trace_event_synthetic_delay.h b/base/trace_event/trace_event_synthetic_delay.h
index e86f9eee2c..59e2842f71 100644
--- a/base/trace_event/trace_event_synthetic_delay.h
+++ b/base/trace_event/trace_event_synthetic_delay.h
@@ -62,6 +62,9 @@
trace_event_internal::GetOrCreateDelay(name, &impl_ptr)->End(); \
} while (false)
+template <typename Type>
+struct DefaultSingletonTraits;
+
namespace base {
namespace trace_event {
diff --git a/base/trace_event/trace_event_unittest.cc b/base/trace_event/trace_event_unittest.cc
index 82a552aa4e..ff8ec2de78 100644
--- a/base/trace_event/trace_event_unittest.cc
+++ b/base/trace_event/trace_event_unittest.cc
@@ -18,7 +18,6 @@
#include "base/json/json_writer.h"
#include "base/location.h"
#include "base/macros.h"
-#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted_memory.h"
#include "base/memory/singleton.h"
#include "base/process/process_handle.h"
@@ -30,12 +29,7 @@
#include "base/threading/platform_thread.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
-#include "base/trace_event/event_name_filter.h"
-#include "base/trace_event/heap_profiler_event_filter.h"
#include "base/trace_event/trace_buffer.h"
-#include "base/trace_event/trace_event.h"
-#include "base/trace_event/trace_event_filter.h"
-#include "base/trace_event/trace_event_filter_test_utils.h"
#include "base/trace_event/trace_event_synthetic_delay.h"
#include "base/values.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -73,6 +67,9 @@ class TraceEventTestFixture : public testing::Test {
WaitableEvent* flush_complete_event,
const scoped_refptr<base::RefCountedString>& events_str,
bool has_more_events);
+ void OnWatchEventMatched() {
+ ++event_watch_notification_;
+ }
DictionaryValue* FindMatchingTraceEntry(const JsonKeyValue* key_values);
DictionaryValue* FindNamePhase(const char* name, const char* phase);
DictionaryValue* FindNamePhaseKeyValue(const char* name,
@@ -94,6 +91,7 @@ class TraceEventTestFixture : public testing::Test {
}
void BeginSpecificTrace(const std::string& filter) {
+ event_watch_notification_ = 0;
TraceLog::GetInstance()->SetEnabled(TraceConfig(filter, ""),
TraceLog::RECORDING_MODE);
}
@@ -137,8 +135,7 @@ class TraceEventTestFixture : public testing::Test {
}
void EndTraceAndFlushAsync(WaitableEvent* flush_complete_event) {
- TraceLog::GetInstance()->SetDisabled(TraceLog::RECORDING_MODE |
- TraceLog::FILTERING_MODE);
+ TraceLog::GetInstance()->SetDisabled();
TraceLog::GetInstance()->Flush(
base::Bind(&TraceEventTestFixture::OnTraceDataCollected,
base::Unretained(static_cast<TraceEventTestFixture*>(this)),
@@ -154,6 +151,7 @@ class TraceEventTestFixture : public testing::Test {
ASSERT_TRUE(tracelog);
ASSERT_FALSE(tracelog->IsEnabled());
trace_buffer_.SetOutputCallback(json_output_.GetCallback());
+ event_watch_notification_ = 0;
num_flush_callbacks_ = 0;
}
void TearDown() override {
@@ -170,6 +168,7 @@ class TraceEventTestFixture : public testing::Test {
ListValue trace_parsed_;
TraceResultBuffer trace_buffer_;
TraceResultBuffer::SimpleOutput json_output_;
+ int event_watch_notification_;
size_t num_flush_callbacks_;
private:
@@ -264,7 +263,7 @@ DictionaryValue* TraceEventTestFixture::FindMatchingTraceEntry(
for (size_t i = 0; i < trace_parsed_count; i++) {
Value* value = NULL;
trace_parsed_.Get(i, &value);
- if (!value || value->GetType() != Value::Type::DICTIONARY)
+ if (!value || value->GetType() != Value::TYPE_DICTIONARY)
continue;
DictionaryValue* dict = static_cast<DictionaryValue*>(value);
@@ -282,7 +281,7 @@ void TraceEventTestFixture::DropTracedMetadataRecords() {
for (size_t i = 0; i < old_trace_parsed_size; i++) {
Value* value = nullptr;
old_trace_parsed->Get(i, &value);
- if (!value || value->GetType() != Value::Type::DICTIONARY) {
+ if (!value || value->GetType() != Value::TYPE_DICTIONARY) {
trace_parsed_.Append(value->CreateDeepCopy());
continue;
}
@@ -371,7 +370,7 @@ const DictionaryValue* FindTraceEntry(
match_after_this_item = NULL;
continue;
}
- if (!value || value->GetType() != Value::Type::DICTIONARY)
+ if (!value || value->GetType() != Value::TYPE_DICTIONARY)
continue;
const DictionaryValue* dict = static_cast<const DictionaryValue*>(value);
@@ -389,7 +388,7 @@ std::vector<const DictionaryValue*> FindTraceEntries(
for (size_t i = 0; i < trace_parsed_count; i++) {
const Value* value = NULL;
trace_parsed.Get(i, &value);
- if (!value || value->GetType() != Value::Type::DICTIONARY)
+ if (!value || value->GetType() != Value::TYPE_DICTIONARY)
continue;
const DictionaryValue* dict = static_cast<const DictionaryValue*>(value);
@@ -461,10 +460,9 @@ void TraceWithAllMacroVariants(WaitableEvent* task_complete_event) {
"b", 1415);
TRACE_COUNTER_WITH_TIMESTAMP1("all", "TRACE_COUNTER_WITH_TIMESTAMP1 call",
- TimeTicks::FromInternalValue(42), 31415);
+ 42, 31415);
TRACE_COUNTER_WITH_TIMESTAMP2("all", "TRACE_COUNTER_WITH_TIMESTAMP2 call",
- TimeTicks::FromInternalValue(42),
- "a", 30000, "b", 1415);
+ 42, "a", 30000, "b", 1415);
TRACE_COUNTER_ID1("all", "TRACE_COUNTER_ID1 call", 0x319009, 31415);
TRACE_COUNTER_ID2("all", "TRACE_COUNTER_ID2 call", 0x319009,
@@ -472,14 +470,14 @@ void TraceWithAllMacroVariants(WaitableEvent* task_complete_event) {
TRACE_EVENT_COPY_BEGIN_WITH_ID_TID_AND_TIMESTAMP0("all",
"TRACE_EVENT_COPY_BEGIN_WITH_ID_TID_AND_TIMESTAMP0 call",
- kAsyncId, kThreadId, TimeTicks::FromInternalValue(12345));
+ kAsyncId, kThreadId, 12345);
TRACE_EVENT_COPY_END_WITH_ID_TID_AND_TIMESTAMP0("all",
"TRACE_EVENT_COPY_END_WITH_ID_TID_AND_TIMESTAMP0 call",
- kAsyncId, kThreadId, TimeTicks::FromInternalValue(23456));
+ kAsyncId, kThreadId, 23456);
TRACE_EVENT_BEGIN_WITH_ID_TID_AND_TIMESTAMP0("all",
"TRACE_EVENT_BEGIN_WITH_ID_TID_AND_TIMESTAMP0 call",
- kAsyncId2, kThreadId, TimeTicks::FromInternalValue(34567));
+ kAsyncId2, kThreadId, 34567);
TRACE_EVENT_ASYNC_STEP_PAST0("all", "TRACE_EVENT_ASYNC_STEP_PAST0 call",
kAsyncId2, "step_end1");
TRACE_EVENT_ASYNC_STEP_PAST1("all", "TRACE_EVENT_ASYNC_STEP_PAST1 call",
@@ -487,7 +485,7 @@ void TraceWithAllMacroVariants(WaitableEvent* task_complete_event) {
TRACE_EVENT_END_WITH_ID_TID_AND_TIMESTAMP0("all",
"TRACE_EVENT_END_WITH_ID_TID_AND_TIMESTAMP0 call",
- kAsyncId2, kThreadId, TimeTicks::FromInternalValue(45678));
+ kAsyncId2, kThreadId, 45678);
TRACE_EVENT_OBJECT_CREATED_WITH_ID("all", "tracked object 1", 0x42);
TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
@@ -519,24 +517,6 @@ void TraceWithAllMacroVariants(WaitableEvent* task_complete_event) {
context_id);
TRACE_EVENT_SCOPED_CONTEXT("all", "TRACE_EVENT_SCOPED_CONTEXT call",
context_id);
-
- TRACE_LINK_IDS("all", "TRACE_LINK_IDS simple call", 0x1000, 0x2000);
- TRACE_LINK_IDS("all", "TRACE_LINK_IDS scoped call",
- TRACE_ID_WITH_SCOPE("scope 1", 0x1000),
- TRACE_ID_WITH_SCOPE("scope 2", 0x2000));
- TRACE_LINK_IDS("all", "TRACE_LINK_IDS to a local ID", 0x1000,
- TRACE_ID_LOCAL(0x2000));
- TRACE_LINK_IDS("all", "TRACE_LINK_IDS to a global ID", 0x1000,
- TRACE_ID_GLOBAL(0x2000));
- TRACE_LINK_IDS("all", "TRACE_LINK_IDS to a composite ID", 0x1000,
- TRACE_ID_WITH_SCOPE("scope 1", 0x2000, 0x3000));
-
- TRACE_EVENT_ASYNC_BEGIN0("all", "async default process scope", 0x1000);
- TRACE_EVENT_ASYNC_BEGIN0("all", "async local id", TRACE_ID_LOCAL(0x2000));
- TRACE_EVENT_ASYNC_BEGIN0("all", "async global id", TRACE_ID_GLOBAL(0x3000));
- TRACE_EVENT_ASYNC_BEGIN0("all", "async global id with scope string",
- TRACE_ID_WITH_SCOPE("scope string",
- TRACE_ID_GLOBAL(0x4000)));
} // Scope close causes TRACE_EVENT0 etc to send their END events.
if (task_complete_event)
@@ -977,144 +957,6 @@ void ValidateAllTraceMacrosCreatedData(const ListValue& trace_parsed) {
EXPECT_TRUE((item && item->GetString("id", &id)));
EXPECT_EQ("0x20151021", id);
}
-
- EXPECT_FIND_("TRACE_LINK_IDS simple call");
- {
- std::string ph;
- EXPECT_TRUE((item && item->GetString("ph", &ph)));
- EXPECT_EQ("=", ph);
-
- EXPECT_FALSE((item && item->HasKey("scope")));
- std::string id1;
- EXPECT_TRUE((item && item->GetString("id", &id1)));
- EXPECT_EQ("0x1000", id1);
-
- EXPECT_FALSE((item && item->HasKey("args.linked_id.scope")));
- std::string id2;
- EXPECT_TRUE((item && item->GetString("args.linked_id.id", &id2)));
- EXPECT_EQ("0x2000", id2);
- }
-
- EXPECT_FIND_("TRACE_LINK_IDS scoped call");
- {
- std::string ph;
- EXPECT_TRUE((item && item->GetString("ph", &ph)));
- EXPECT_EQ("=", ph);
-
- std::string scope1;
- EXPECT_TRUE((item && item->GetString("scope", &scope1)));
- EXPECT_EQ("scope 1", scope1);
- std::string id1;
- EXPECT_TRUE((item && item->GetString("id", &id1)));
- EXPECT_EQ("0x1000", id1);
-
- std::string scope2;
- EXPECT_TRUE((item && item->GetString("args.linked_id.scope", &scope2)));
- EXPECT_EQ("scope 2", scope2);
- std::string id2;
- EXPECT_TRUE((item && item->GetString("args.linked_id.id", &id2)));
- EXPECT_EQ("0x2000", id2);
- }
-
- EXPECT_FIND_("TRACE_LINK_IDS to a local ID");
- {
- std::string ph;
- EXPECT_TRUE((item && item->GetString("ph", &ph)));
- EXPECT_EQ("=", ph);
-
- EXPECT_FALSE((item && item->HasKey("scope")));
- std::string id1;
- EXPECT_TRUE((item && item->GetString("id", &id1)));
- EXPECT_EQ("0x1000", id1);
-
- EXPECT_FALSE((item && item->HasKey("args.linked_id.scope")));
- std::string id2;
- EXPECT_TRUE((item && item->GetString("args.linked_id.id2.local", &id2)));
- EXPECT_EQ("0x2000", id2);
- }
-
- EXPECT_FIND_("TRACE_LINK_IDS to a global ID");
- {
- std::string ph;
- EXPECT_TRUE((item && item->GetString("ph", &ph)));
- EXPECT_EQ("=", ph);
-
- EXPECT_FALSE((item && item->HasKey("scope")));
- std::string id1;
- EXPECT_TRUE((item && item->GetString("id", &id1)));
- EXPECT_EQ("0x1000", id1);
-
- EXPECT_FALSE((item && item->HasKey("args.linked_id.scope")));
- std::string id2;
- EXPECT_TRUE((item && item->GetString("args.linked_id.id2.global", &id2)));
- EXPECT_EQ("0x2000", id2);
- }
-
- EXPECT_FIND_("TRACE_LINK_IDS to a composite ID");
- {
- std::string ph;
- EXPECT_TRUE((item && item->GetString("ph", &ph)));
- EXPECT_EQ("=", ph);
-
- EXPECT_FALSE(item->HasKey("scope"));
- std::string id1;
- EXPECT_TRUE(item->GetString("id", &id1));
- EXPECT_EQ("0x1000", id1);
-
- std::string scope;
- EXPECT_TRUE(item->GetString("args.linked_id.scope", &scope));
- EXPECT_EQ("scope 1", scope);
- std::string id2;
- EXPECT_TRUE(item->GetString("args.linked_id.id", &id2));
- EXPECT_EQ(id2, "0x2000/0x3000");
- }
-
- EXPECT_FIND_("async default process scope");
- {
- std::string ph;
- EXPECT_TRUE((item && item->GetString("ph", &ph)));
- EXPECT_EQ("S", ph);
-
- std::string id;
- EXPECT_TRUE((item && item->GetString("id", &id)));
- EXPECT_EQ("0x1000", id);
- }
-
- EXPECT_FIND_("async local id");
- {
- std::string ph;
- EXPECT_TRUE((item && item->GetString("ph", &ph)));
- EXPECT_EQ("S", ph);
-
- std::string id;
- EXPECT_TRUE((item && item->GetString("id2.local", &id)));
- EXPECT_EQ("0x2000", id);
- }
-
- EXPECT_FIND_("async global id");
- {
- std::string ph;
- EXPECT_TRUE((item && item->GetString("ph", &ph)));
- EXPECT_EQ("S", ph);
-
- std::string id;
- EXPECT_TRUE((item && item->GetString("id2.global", &id)));
- EXPECT_EQ("0x3000", id);
- }
-
- EXPECT_FIND_("async global id with scope string");
- {
- std::string ph;
- EXPECT_TRUE((item && item->GetString("ph", &ph)));
- EXPECT_EQ("S", ph);
-
- std::string id;
- EXPECT_TRUE((item && item->GetString("id2.global", &id)));
- EXPECT_EQ("0x4000", id);
- std::string scope;
- EXPECT_TRUE((item && item->GetString("scope", &scope)));
- EXPECT_EQ("scope string", scope);
- }
}
void TraceManyInstantEvents(int thread_id, int num_events,
@@ -1139,7 +981,7 @@ void ValidateInstantEventPresentOnEveryThread(const ListValue& trace_parsed,
for (size_t i = 0; i < trace_parsed_count; i++) {
const Value* value = NULL;
trace_parsed.Get(i, &value);
- if (!value || value->GetType() != Value::Type::DICTIONARY)
+ if (!value || value->GetType() != Value::TYPE_DICTIONARY)
continue;
const DictionaryValue* dict = static_cast<const DictionaryValue*>(value);
std::string name;
@@ -1588,6 +1430,59 @@ TEST_F(TraceEventTestFixture, Categories) {
}
+// Test EVENT_WATCH_NOTIFICATION
+TEST_F(TraceEventTestFixture, EventWatchNotification) {
+ // Basic one occurrence.
+ BeginTrace();
+ TraceLog::WatchEventCallback callback =
+ base::Bind(&TraceEventTestFixture::OnWatchEventMatched,
+ base::Unretained(this));
+ TraceLog::GetInstance()->SetWatchEvent("cat", "event", callback);
+ TRACE_EVENT_INSTANT0("cat", "event", TRACE_EVENT_SCOPE_THREAD);
+ EndTraceAndFlush();
+ EXPECT_EQ(event_watch_notification_, 1);
+
+ // Auto-reset after end trace.
+ BeginTrace();
+ TraceLog::GetInstance()->SetWatchEvent("cat", "event", callback);
+ EndTraceAndFlush();
+ BeginTrace();
+ TRACE_EVENT_INSTANT0("cat", "event", TRACE_EVENT_SCOPE_THREAD);
+ EndTraceAndFlush();
+ EXPECT_EQ(event_watch_notification_, 0);
+
+ // Multiple occurrence.
+ BeginTrace();
+ int num_occurrences = 5;
+ TraceLog::GetInstance()->SetWatchEvent("cat", "event", callback);
+ for (int i = 0; i < num_occurrences; ++i)
+ TRACE_EVENT_INSTANT0("cat", "event", TRACE_EVENT_SCOPE_THREAD);
+ EndTraceAndFlush();
+ EXPECT_EQ(event_watch_notification_, num_occurrences);
+
+ // Wrong category.
+ BeginTrace();
+ TraceLog::GetInstance()->SetWatchEvent("cat", "event", callback);
+ TRACE_EVENT_INSTANT0("wrong_cat", "event", TRACE_EVENT_SCOPE_THREAD);
+ EndTraceAndFlush();
+ EXPECT_EQ(event_watch_notification_, 0);
+
+ // Wrong name.
+ BeginTrace();
+ TraceLog::GetInstance()->SetWatchEvent("cat", "event", callback);
+ TRACE_EVENT_INSTANT0("cat", "wrong_event", TRACE_EVENT_SCOPE_THREAD);
+ EndTraceAndFlush();
+ EXPECT_EQ(event_watch_notification_, 0);
+
+ // Canceled.
+ BeginTrace();
+ TraceLog::GetInstance()->SetWatchEvent("cat", "event", callback);
+ TraceLog::GetInstance()->CancelWatchEvent();
+ TRACE_EVENT_INSTANT0("cat", "event", TRACE_EVENT_SCOPE_THREAD);
+ EndTraceAndFlush();
+ EXPECT_EQ(event_watch_notification_, 0);
+}
+
// Test ASYNC_BEGIN/END events
TEST_F(TraceEventTestFixture, AsyncBeginEndEvents) {
BeginTrace();
@@ -2158,6 +2053,55 @@ TEST_F(TraceEventTestFixture, TraceWithDisabledByDefaultCategoryFilters) {
trace_log->SetDisabled();
}
+TEST_F(TraceEventTestFixture, TraceSampling) {
+ TraceLog::GetInstance()->SetEnabled(
+ TraceConfig(kRecordAllCategoryFilter, "record-until-full,enable-sampling"),
+ TraceLog::RECORDING_MODE);
+
+ TRACE_EVENT_SET_SAMPLING_STATE_FOR_BUCKET(1, "cc", "Stuff");
+ TraceLog::GetInstance()->WaitSamplingEventForTesting();
+ TRACE_EVENT_SET_SAMPLING_STATE_FOR_BUCKET(1, "cc", "Things");
+ TraceLog::GetInstance()->WaitSamplingEventForTesting();
+
+ EndTraceAndFlush();
+
+ // Make sure we hit at least once.
+ EXPECT_TRUE(FindNamePhase("Stuff", "P"));
+ EXPECT_TRUE(FindNamePhase("Things", "P"));
+}
+
+TEST_F(TraceEventTestFixture, TraceSamplingScope) {
+ TraceLog::GetInstance()->SetEnabled(
+ TraceConfig(kRecordAllCategoryFilter, "record-until-full,enable-sampling"),
+ TraceLog::RECORDING_MODE);
+
+ TRACE_EVENT_SCOPED_SAMPLING_STATE("AAA", "name");
+ TraceLog::GetInstance()->WaitSamplingEventForTesting();
+ {
+ EXPECT_STREQ(TRACE_EVENT_GET_SAMPLING_STATE(), "AAA");
+ TRACE_EVENT_SCOPED_SAMPLING_STATE("BBB", "name");
+ TraceLog::GetInstance()->WaitSamplingEventForTesting();
+ EXPECT_STREQ(TRACE_EVENT_GET_SAMPLING_STATE(), "BBB");
+ }
+ TraceLog::GetInstance()->WaitSamplingEventForTesting();
+ {
+ EXPECT_STREQ(TRACE_EVENT_GET_SAMPLING_STATE(), "AAA");
+ TRACE_EVENT_SCOPED_SAMPLING_STATE("CCC", "name");
+ TraceLog::GetInstance()->WaitSamplingEventForTesting();
+ EXPECT_STREQ(TRACE_EVENT_GET_SAMPLING_STATE(), "CCC");
+ }
+ TraceLog::GetInstance()->WaitSamplingEventForTesting();
+ {
+ EXPECT_STREQ(TRACE_EVENT_GET_SAMPLING_STATE(), "AAA");
+ TRACE_EVENT_SET_SAMPLING_STATE("DDD", "name");
+ TraceLog::GetInstance()->WaitSamplingEventForTesting();
+ EXPECT_STREQ(TRACE_EVENT_GET_SAMPLING_STATE(), "DDD");
+ }
+ TraceLog::GetInstance()->WaitSamplingEventForTesting();
+ EXPECT_STREQ(TRACE_EVENT_GET_SAMPLING_STATE(), "DDD");
+
+ EndTraceAndFlush();
+}
class MyData : public ConvertableToTraceFormat {
public:
@@ -2346,7 +2290,7 @@ TEST_F(TraceEventTestFixture, PrimitiveArgs) {
dict->GetDictionary("args", &args_dict);
ASSERT_TRUE(args_dict);
EXPECT_TRUE(args_dict->Get("float_one", &value));
- EXPECT_TRUE(value->IsType(Value::Type::DOUBLE));
+ EXPECT_TRUE(value->IsType(Value::TYPE_DOUBLE));
EXPECT_TRUE(value->GetAsDouble(&double_value));
EXPECT_EQ(1, double_value);
@@ -2356,7 +2300,7 @@ TEST_F(TraceEventTestFixture, PrimitiveArgs) {
dict->GetDictionary("args", &args_dict);
ASSERT_TRUE(args_dict);
EXPECT_TRUE(args_dict->Get("float_half", &value));
- EXPECT_TRUE(value->IsType(Value::Type::DOUBLE));
+ EXPECT_TRUE(value->IsType(Value::TYPE_DOUBLE));
EXPECT_TRUE(value->GetAsDouble(&double_value));
EXPECT_EQ(0.5, double_value);
@@ -2366,7 +2310,7 @@ TEST_F(TraceEventTestFixture, PrimitiveArgs) {
dict->GetDictionary("args", &args_dict);
ASSERT_TRUE(args_dict);
EXPECT_TRUE(args_dict->Get("float_neghalf", &value));
- EXPECT_TRUE(value->IsType(Value::Type::DOUBLE));
+ EXPECT_TRUE(value->IsType(Value::TYPE_DOUBLE));
EXPECT_TRUE(value->GetAsDouble(&double_value));
EXPECT_EQ(-0.5, double_value);
@@ -2536,6 +2480,233 @@ TEST_F(TraceEventTestFixture, ArgsWhitelisting) {
EXPECT_EQ(args_string, "__stripped__");
}
+class TraceEventCallbackTest : public TraceEventTestFixture {
+ public:
+ void SetUp() override {
+ TraceEventTestFixture::SetUp();
+ ASSERT_EQ(NULL, s_instance);
+ s_instance = this;
+ }
+ void TearDown() override {
+ TraceLog::GetInstance()->SetDisabled();
+ ASSERT_TRUE(s_instance);
+ s_instance = NULL;
+ TraceEventTestFixture::TearDown();
+ }
+
+ protected:
+ // For TraceEventCallbackAndRecordingX tests.
+ void VerifyCallbackAndRecordedEvents(size_t expected_callback_count,
+ size_t expected_recorded_count) {
+ // Callback events.
+ EXPECT_EQ(expected_callback_count, collected_events_names_.size());
+ for (size_t i = 0; i < collected_events_names_.size(); ++i) {
+ EXPECT_EQ("callback", collected_events_categories_[i]);
+ EXPECT_EQ("yes", collected_events_names_[i]);
+ }
+
+ // Recorded events.
+ EXPECT_EQ(expected_recorded_count, trace_parsed_.GetSize());
+ EXPECT_TRUE(FindTraceEntry(trace_parsed_, "recording"));
+ EXPECT_FALSE(FindTraceEntry(trace_parsed_, "callback"));
+ EXPECT_TRUE(FindTraceEntry(trace_parsed_, "yes"));
+ EXPECT_FALSE(FindTraceEntry(trace_parsed_, "no"));
+ }
+
+ void VerifyCollectedEvent(size_t i,
+ unsigned phase,
+ const std::string& category,
+ const std::string& name) {
+ EXPECT_EQ(phase, collected_events_phases_[i]);
+ EXPECT_EQ(category, collected_events_categories_[i]);
+ EXPECT_EQ(name, collected_events_names_[i]);
+ }
+
+ std::vector<std::string> collected_events_categories_;
+ std::vector<std::string> collected_events_names_;
+ std::vector<unsigned char> collected_events_phases_;
+ std::vector<TimeTicks> collected_events_timestamps_;
+
+ static TraceEventCallbackTest* s_instance;
+ static void Callback(TimeTicks timestamp,
+ char phase,
+ const unsigned char* category_group_enabled,
+ const char* name,
+ const char* scope,
+ unsigned long long id,
+ int num_args,
+ const char* const arg_names[],
+ const unsigned char arg_types[],
+ const unsigned long long arg_values[],
+ unsigned int flags) {
+ s_instance->collected_events_phases_.push_back(phase);
+ s_instance->collected_events_categories_.push_back(
+ TraceLog::GetCategoryGroupName(category_group_enabled));
+ s_instance->collected_events_names_.push_back(name);
+ s_instance->collected_events_timestamps_.push_back(timestamp);
+ }
+};
+
+TraceEventCallbackTest* TraceEventCallbackTest::s_instance;
+
+TEST_F(TraceEventCallbackTest, TraceEventCallback) {
+ TRACE_EVENT_INSTANT0("all", "before enable", TRACE_EVENT_SCOPE_THREAD);
+ TraceLog::GetInstance()->SetEventCallbackEnabled(
+ TraceConfig(kRecordAllCategoryFilter, ""), Callback);
+ TRACE_EVENT_INSTANT0("all", "event1", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("all", "event2", TRACE_EVENT_SCOPE_GLOBAL);
+ {
+ TRACE_EVENT0("all", "duration");
+ TRACE_EVENT_INSTANT0("all", "event3", TRACE_EVENT_SCOPE_GLOBAL);
+ }
+ TraceLog::GetInstance()->SetEventCallbackDisabled();
+ TRACE_EVENT_INSTANT0("all", "after callback removed",
+ TRACE_EVENT_SCOPE_GLOBAL);
+ ASSERT_EQ(5u, collected_events_names_.size());
+ EXPECT_EQ("event1", collected_events_names_[0]);
+ EXPECT_EQ(TRACE_EVENT_PHASE_INSTANT, collected_events_phases_[0]);
+ EXPECT_EQ("event2", collected_events_names_[1]);
+ EXPECT_EQ(TRACE_EVENT_PHASE_INSTANT, collected_events_phases_[1]);
+ EXPECT_EQ("duration", collected_events_names_[2]);
+ EXPECT_EQ(TRACE_EVENT_PHASE_BEGIN, collected_events_phases_[2]);
+ EXPECT_EQ("event3", collected_events_names_[3]);
+ EXPECT_EQ(TRACE_EVENT_PHASE_INSTANT, collected_events_phases_[3]);
+ EXPECT_EQ("duration", collected_events_names_[4]);
+ EXPECT_EQ(TRACE_EVENT_PHASE_END, collected_events_phases_[4]);
+ for (size_t i = 1; i < collected_events_timestamps_.size(); i++) {
+ EXPECT_LE(collected_events_timestamps_[i - 1],
+ collected_events_timestamps_[i]);
+ }
+}
+
+TEST_F(TraceEventCallbackTest, TraceEventCallbackWhileFull) {
+ TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""),
+ TraceLog::RECORDING_MODE);
+ do {
+ TRACE_EVENT_INSTANT0("all", "badger badger", TRACE_EVENT_SCOPE_GLOBAL);
+ } while (!TraceLog::GetInstance()->BufferIsFull());
+ TraceLog::GetInstance()->SetEventCallbackEnabled(
+ TraceConfig(kRecordAllCategoryFilter, ""), Callback);
+ TRACE_EVENT_INSTANT0("all", "a snake", TRACE_EVENT_SCOPE_GLOBAL);
+ TraceLog::GetInstance()->SetEventCallbackDisabled();
+ ASSERT_EQ(1u, collected_events_names_.size());
+ EXPECT_EQ("a snake", collected_events_names_[0]);
+}
+
+// 1: Enable callback, enable recording, disable callback, disable recording.
+TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording1) {
+ TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TraceLog::GetInstance()->SetEventCallbackEnabled(TraceConfig("callback", ""),
+ Callback);
+ TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ TraceLog::GetInstance()->SetEnabled(TraceConfig("recording", ""),
+ TraceLog::RECORDING_MODE);
+ TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ TraceLog::GetInstance()->SetEventCallbackDisabled();
+ TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ EndTraceAndFlush();
+ TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
+
+ DropTracedMetadataRecords();
+ VerifyCallbackAndRecordedEvents(2, 2);
+}
+
+// 2: Enable callback, enable recording, disable recording, disable callback.
+TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording2) {
+ TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TraceLog::GetInstance()->SetEventCallbackEnabled(TraceConfig("callback", ""),
+ Callback);
+ TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ TraceLog::GetInstance()->SetEnabled(TraceConfig("recording", ""),
+ TraceLog::RECORDING_MODE);
+ TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ EndTraceAndFlush();
+ TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ TraceLog::GetInstance()->SetEventCallbackDisabled();
+ TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
+
+ DropTracedMetadataRecords();
+ VerifyCallbackAndRecordedEvents(3, 1);
+}
+
+// 3: Enable recording, enable callback, disable callback, disable recording.
+TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording3) {
+ TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TraceLog::GetInstance()->SetEnabled(TraceConfig("recording", ""),
+ TraceLog::RECORDING_MODE);
+ TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TraceLog::GetInstance()->SetEventCallbackEnabled(TraceConfig("callback", ""),
+ Callback);
+ TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ TraceLog::GetInstance()->SetEventCallbackDisabled();
+ TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ EndTraceAndFlush();
+ TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
+
+ DropTracedMetadataRecords();
+ VerifyCallbackAndRecordedEvents(1, 3);
+}
+
+// 4: Enable recording, enable callback, disable recording, disable callback.
+TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording4) {
+ TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TraceLog::GetInstance()->SetEnabled(TraceConfig("recording", ""),
+ TraceLog::RECORDING_MODE);
+ TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TraceLog::GetInstance()->SetEventCallbackEnabled(TraceConfig("callback", ""),
+ Callback);
+ TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ EndTraceAndFlush();
+ TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ TraceLog::GetInstance()->SetEventCallbackDisabled();
+ TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
+
+ DropTracedMetadataRecords();
+ VerifyCallbackAndRecordedEvents(2, 2);
+}
+
+TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecordingDuration) {
+ TraceLog::GetInstance()->SetEventCallbackEnabled(
+ TraceConfig(kRecordAllCategoryFilter, ""), Callback);
+ {
+ TRACE_EVENT0("callback", "duration1");
+ TraceLog::GetInstance()->SetEnabled(
+ TraceConfig(kRecordAllCategoryFilter, ""), TraceLog::RECORDING_MODE);
+ TRACE_EVENT0("callback", "duration2");
+ EndTraceAndFlush();
+ TRACE_EVENT0("callback", "duration3");
+ }
+ TraceLog::GetInstance()->SetEventCallbackDisabled();
+
+ ASSERT_EQ(6u, collected_events_names_.size());
+ VerifyCollectedEvent(0, TRACE_EVENT_PHASE_BEGIN, "callback", "duration1");
+ VerifyCollectedEvent(1, TRACE_EVENT_PHASE_BEGIN, "callback", "duration2");
+ VerifyCollectedEvent(2, TRACE_EVENT_PHASE_BEGIN, "callback", "duration3");
+ VerifyCollectedEvent(3, TRACE_EVENT_PHASE_END, "callback", "duration3");
+ VerifyCollectedEvent(4, TRACE_EVENT_PHASE_END, "callback", "duration2");
+ VerifyCollectedEvent(5, TRACE_EVENT_PHASE_END, "callback", "duration1");
+}
+
TEST_F(TraceEventTestFixture, TraceBufferVectorReportFull) {
TraceLog* trace_log = TraceLog::GetInstance();
trace_log->SetEnabled(
@@ -2544,9 +2715,9 @@ TEST_F(TraceEventTestFixture, TraceBufferVectorReportFull) {
TraceBuffer::CreateTraceBufferVectorOfSize(100));
do {
TRACE_EVENT_BEGIN_WITH_ID_TID_AND_TIMESTAMP0(
- "all", "with_timestamp", 0, 0, TimeTicks::Now());
+ "all", "with_timestamp", 0, 0, TimeTicks::Now().ToInternalValue());
TRACE_EVENT_END_WITH_ID_TID_AND_TIMESTAMP0(
- "all", "with_timestamp", 0, 0, TimeTicks::Now());
+ "all", "with_timestamp", 0, 0, TimeTicks::Now().ToInternalValue());
} while (!trace_log->BufferIsFull());
EndTraceAndFlush();
@@ -2757,9 +2928,29 @@ TEST_F(TraceEventTestFixture, ConvertTraceConfigToInternalOptions) {
trace_log->GetInternalOptionsFromTraceConfig(
TraceConfig(kRecordAllCategoryFilter, ECHO_TO_CONSOLE)));
- EXPECT_EQ(TraceLog::kInternalEchoToConsole,
- trace_log->GetInternalOptionsFromTraceConfig(
- TraceConfig("*", "trace-to-console,enable-systrace")));
+ EXPECT_EQ(
+ TraceLog::kInternalRecordUntilFull | TraceLog::kInternalEnableSampling,
+ trace_log->GetInternalOptionsFromTraceConfig(
+ TraceConfig(kRecordAllCategoryFilter,
+ "record-until-full,enable-sampling")));
+
+ EXPECT_EQ(
+ TraceLog::kInternalRecordContinuously | TraceLog::kInternalEnableSampling,
+ trace_log->GetInternalOptionsFromTraceConfig(
+ TraceConfig(kRecordAllCategoryFilter,
+ "record-continuously,enable-sampling")));
+
+ EXPECT_EQ(
+ TraceLog::kInternalEchoToConsole | TraceLog::kInternalEnableSampling,
+ trace_log->GetInternalOptionsFromTraceConfig(
+ TraceConfig(kRecordAllCategoryFilter,
+ "trace-to-console,enable-sampling")));
+
+ EXPECT_EQ(
+ TraceLog::kInternalEchoToConsole | TraceLog::kInternalEnableSampling,
+ trace_log->GetInternalOptionsFromTraceConfig(
+ TraceConfig("*",
+ "trace-to-console,enable-sampling,enable-systrace")));
}
void SetBlockingFlagAndBlockUntilStopped(WaitableEvent* task_start_event,
@@ -2918,9 +3109,9 @@ TEST_F(TraceEventTestFixture, TimeOffset) {
TRACE_EVENT0("all", "duration2");
}
TRACE_EVENT_BEGIN_WITH_ID_TID_AND_TIMESTAMP0(
- "all", "with_timestamp", 0, 0, TimeTicks::Now());
+ "all", "with_timestamp", 0, 0, TimeTicks::Now().ToInternalValue());
TRACE_EVENT_END_WITH_ID_TID_AND_TIMESTAMP0(
- "all", "with_timestamp", 0, 0, TimeTicks::Now());
+ "all", "with_timestamp", 0, 0, TimeTicks::Now().ToInternalValue());
EndTraceAndFlush();
DropTracedMetadataRecords();
@@ -2982,213 +3173,6 @@ TEST_F(TraceEventTestFixture, SyntheticDelayConfigurationToString) {
EXPECT_EQ(filter, config.ToCategoryFilterString());
}
-TEST_F(TraceEventTestFixture, TraceFilteringMode) {
- const char config_json[] =
- "{"
- " \"event_filters\": ["
- " {"
- " \"filter_predicate\": \"testing_predicate\", "
- " \"included_categories\": [\"*\"]"
- " }"
- " ]"
- "}";
-
- // Run RECORDING_MODE within FILTERING_MODE:
- TestEventFilter::HitsCounter filter_hits_counter;
- TestEventFilter::set_filter_return_value(true);
- TraceLog::GetInstance()->SetFilterFactoryForTesting(TestEventFilter::Factory);
-
- // Only filtering mode is enabled with test filters.
- TraceLog::GetInstance()->SetEnabled(TraceConfig(config_json),
- TraceLog::FILTERING_MODE);
- EXPECT_EQ(TraceLog::FILTERING_MODE, TraceLog::GetInstance()->enabled_modes());
- {
- void* ptr = this;
- TRACE_EVENT0("c0", "name0");
- TRACE_EVENT_ASYNC_BEGIN0("c1", "name1", ptr);
- TRACE_EVENT_INSTANT0("c0", "name0", TRACE_EVENT_SCOPE_THREAD);
- TRACE_EVENT_ASYNC_END0("c1", "name1", ptr);
- }
-
- // Recording mode is enabled when filtering mode is turned on.
- TraceLog::GetInstance()->SetEnabled(TraceConfig("", ""),
- TraceLog::RECORDING_MODE);
- EXPECT_EQ(TraceLog::RECORDING_MODE | TraceLog::FILTERING_MODE,
- TraceLog::GetInstance()->enabled_modes());
- {
- TRACE_EVENT0("c2", "name2");
- }
- // Only recording mode is disabled and filtering mode will continue to run.
- TraceLog::GetInstance()->SetDisabled(TraceLog::RECORDING_MODE);
- EXPECT_EQ(TraceLog::FILTERING_MODE, TraceLog::GetInstance()->enabled_modes());
-
- {
- TRACE_EVENT0("c0", "name0");
- }
- // Filtering mode is disabled and no tracing mode should be enabled.
- TraceLog::GetInstance()->SetDisabled(TraceLog::FILTERING_MODE);
- EXPECT_EQ(0, TraceLog::GetInstance()->enabled_modes());
-
- EndTraceAndFlush();
- EXPECT_FALSE(FindMatchingValue("cat", "c0"));
- EXPECT_FALSE(FindMatchingValue("cat", "c1"));
- EXPECT_FALSE(FindMatchingValue("name", "name0"));
- EXPECT_FALSE(FindMatchingValue("name", "name1"));
- EXPECT_TRUE(FindMatchingValue("cat", "c2"));
- EXPECT_TRUE(FindMatchingValue("name", "name2"));
- EXPECT_EQ(6u, filter_hits_counter.filter_trace_event_hit_count);
- EXPECT_EQ(3u, filter_hits_counter.end_event_hit_count);
- Clear();
- filter_hits_counter.Reset();
-
- // Run FILTERING_MODE within RECORDING_MODE:
- // Only recording mode is enabled and all events must be recorded.
- TraceLog::GetInstance()->SetEnabled(TraceConfig("", ""),
- TraceLog::RECORDING_MODE);
- EXPECT_EQ(TraceLog::RECORDING_MODE, TraceLog::GetInstance()->enabled_modes());
- {
- TRACE_EVENT0("c0", "name0");
- }
-
- // Filtering mode is also enabled and all events must be filtered-out.
- TestEventFilter::set_filter_return_value(false);
- TraceLog::GetInstance()->SetEnabled(TraceConfig(config_json),
- TraceLog::FILTERING_MODE);
- EXPECT_EQ(TraceLog::RECORDING_MODE | TraceLog::FILTERING_MODE,
- TraceLog::GetInstance()->enabled_modes());
- {
- TRACE_EVENT0("c1", "name1");
- }
- // Only filtering mode is disabled and recording mode should continue to run
- // with all events being recorded.
- TraceLog::GetInstance()->SetDisabled(TraceLog::FILTERING_MODE);
- EXPECT_EQ(TraceLog::RECORDING_MODE, TraceLog::GetInstance()->enabled_modes());
-
- {
- TRACE_EVENT0("c2", "name2");
- }
- // Recording mode is disabled and no tracing mode should be enabled.
- TraceLog::GetInstance()->SetDisabled(TraceLog::RECORDING_MODE);
- EXPECT_EQ(0, TraceLog::GetInstance()->enabled_modes());
-
- EndTraceAndFlush();
- EXPECT_TRUE(FindMatchingValue("cat", "c0"));
- EXPECT_TRUE(FindMatchingValue("cat", "c2"));
- EXPECT_TRUE(FindMatchingValue("name", "name0"));
- EXPECT_TRUE(FindMatchingValue("name", "name2"));
- EXPECT_FALSE(FindMatchingValue("cat", "c1"));
- EXPECT_FALSE(FindMatchingValue("name", "name1"));
- EXPECT_EQ(1u, filter_hits_counter.filter_trace_event_hit_count);
- EXPECT_EQ(1u, filter_hits_counter.end_event_hit_count);
- Clear();
-}
-
-TEST_F(TraceEventTestFixture, EventFiltering) {
- const char config_json[] =
- "{"
- " \"included_categories\": ["
- " \"filtered_cat\","
- " \"unfiltered_cat\"],"
- " \"event_filters\": ["
- " {"
- " \"filter_predicate\": \"testing_predicate\", "
- " \"included_categories\": [\"filtered_cat\"]"
- " }"
- " "
- " ]"
- "}";
-
- TestEventFilter::HitsCounter filter_hits_counter;
- TestEventFilter::set_filter_return_value(true);
- TraceLog::GetInstance()->SetFilterFactoryForTesting(TestEventFilter::Factory);
-
- TraceConfig trace_config(config_json);
- TraceLog::GetInstance()->SetEnabled(
- trace_config, TraceLog::RECORDING_MODE | TraceLog::FILTERING_MODE);
- ASSERT_TRUE(TraceLog::GetInstance()->IsEnabled());
-
- TRACE_EVENT0("filtered_cat", "a snake");
- TRACE_EVENT0("filtered_cat", "a mushroom");
- TRACE_EVENT0("unfiltered_cat", "a horse");
-
- // This is scoped so we can test the end event being filtered.
- { TRACE_EVENT0("filtered_cat", "another cat whoa"); }
-
- EndTraceAndFlush();
-
- EXPECT_EQ(3u, filter_hits_counter.filter_trace_event_hit_count);
- EXPECT_EQ(1u, filter_hits_counter.end_event_hit_count);
-}
-
-TEST_F(TraceEventTestFixture, EventWhitelistFiltering) {
- std::string config_json = StringPrintf(
- "{"
- " \"included_categories\": ["
- " \"filtered_cat\","
- " \"unfiltered_cat\"],"
- " \"event_filters\": ["
- " {"
- " \"filter_predicate\": \"%s\", "
- " \"included_categories\": [\"*\"], "
- " \"excluded_categories\": [\"unfiltered_cat\"], "
- " \"filter_args\": {"
- " \"event_name_whitelist\": [\"a snake\", \"a dog\"]"
- " }"
- " }"
- " "
- " ]"
- "}",
- EventNameFilter::kName);
-
- TraceConfig trace_config(config_json);
- TraceLog::GetInstance()->SetEnabled(
- trace_config, TraceLog::RECORDING_MODE | TraceLog::FILTERING_MODE);
- EXPECT_TRUE(TraceLog::GetInstance()->IsEnabled());
-
- TRACE_EVENT0("filtered_cat", "a snake");
- TRACE_EVENT0("filtered_cat", "a mushroom");
- TRACE_EVENT0("unfiltered_cat", "a cat");
-
- EndTraceAndFlush();
-
- EXPECT_TRUE(FindMatchingValue("name", "a snake"));
- EXPECT_FALSE(FindMatchingValue("name", "a mushroom"));
- EXPECT_TRUE(FindMatchingValue("name", "a cat"));
-}
-
-TEST_F(TraceEventTestFixture, HeapProfilerFiltering) {
- std::string config_json = StringPrintf(
- "{"
- " \"included_categories\": ["
- " \"filtered_cat\","
- " \"unfiltered_cat\"],"
- " \"excluded_categories\": [\"excluded_cat\"],"
- " \"event_filters\": ["
- " {"
- " \"filter_predicate\": \"%s\", "
- " \"included_categories\": [\"*\"]"
- " }"
- " ]"
- "}",
- HeapProfilerEventFilter::kName);
-
- TraceConfig trace_config(config_json);
- TraceLog::GetInstance()->SetEnabled(
- trace_config, TraceLog::RECORDING_MODE | TraceLog::FILTERING_MODE);
- EXPECT_TRUE(TraceLog::GetInstance()->IsEnabled());
-
- TRACE_EVENT0("filtered_cat", "a snake");
- TRACE_EVENT0("excluded_cat", "a mushroom");
- TRACE_EVENT0("unfiltered_cat", "a cat");
-
- EndTraceAndFlush();
-
- // The predicate should not change behavior of the trace events.
- EXPECT_TRUE(FindMatchingValue("name", "a snake"));
- EXPECT_FALSE(FindMatchingValue("name", "a mushroom"));
- EXPECT_TRUE(FindMatchingValue("name", "a cat"));
-}
-
TEST_F(TraceEventTestFixture, ClockSyncEventsAreAlwaysAddedToTrace) {
BeginSpecificTrace("-*");
TRACE_EVENT_CLOCK_SYNC_RECEIVER(1);
diff --git a/base/trace_event/trace_log.cc b/base/trace_event/trace_log.cc
index 10b090ae57..12cebc6f65 100644
--- a/base/trace_event/trace_log.cc
+++ b/base/trace_event/trace_log.cc
@@ -13,43 +13,41 @@
#include "base/bind.h"
#include "base/command_line.h"
#include "base/debug/leak_annotations.h"
+#include "base/lazy_instance.h"
#include "base/location.h"
#include "base/macros.h"
-#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted_memory.h"
#include "base/memory/singleton.h"
-#include "base/message_loop/message_loop.h"
#include "base/process/process_metrics.h"
#include "base/stl_util.h"
#include "base/strings/string_split.h"
#include "base/strings/string_tokenizer.h"
#include "base/strings/stringprintf.h"
#include "base/sys_info.h"
-// post_task.h pulls in a lot of code not needed on Arc++.
-#if 0
-#include "base/task_scheduler/post_task.h"
-#endif
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread_id_name_manager.h"
#include "base/threading/thread_task_runner_handle.h"
+#include "base/threading/worker_pool.h"
#include "base/time/time.h"
-#include "base/trace_event/category_registry.h"
-#include "base/trace_event/event_name_filter.h"
#include "base/trace_event/heap_profiler.h"
#include "base/trace_event/heap_profiler_allocation_context_tracker.h"
-#include "base/trace_event/heap_profiler_event_filter.h"
#include "base/trace_event/memory_dump_manager.h"
#include "base/trace_event/memory_dump_provider.h"
#include "base/trace_event/process_memory_dump.h"
#include "base/trace_event/trace_buffer.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/trace_event_synthetic_delay.h"
+#include "base/trace_event/trace_sampling_thread.h"
#include "build/build_config.h"
#if defined(OS_WIN)
#include "base/trace_event/trace_event_etw_export_win.h"
#endif
+// The thread buckets for the sampling profiler.
+BASE_EXPORT TRACE_EVENT_API_ATOMIC_WORD g_trace_state[3];
+
namespace base {
namespace internal {
@@ -88,13 +86,35 @@ const size_t kEchoToConsoleTraceEventBufferChunks = 256;
const size_t kTraceEventBufferSizeInBytes = 100 * 1024;
const int kThreadFlushTimeoutMs = 3000;
-#define MAX_TRACE_EVENT_FILTERS 32
-
-// List of TraceEventFilter objects from the most recent tracing session.
-std::vector<std::unique_ptr<TraceEventFilter>>& GetCategoryGroupFilters() {
- static auto* filters = new std::vector<std::unique_ptr<TraceEventFilter>>();
- return *filters;
-}
+#define MAX_CATEGORY_GROUPS 200
+
+// Parallel arrays g_category_groups and g_category_group_enabled are separate
+// so that a pointer to a member of g_category_group_enabled can be easily
+// converted to an index into g_category_groups. This allows macros to deal
+// only with char enabled pointers from g_category_group_enabled, and we can
+// convert internally to determine the category name from the char enabled
+// pointer.
+const char* g_category_groups[MAX_CATEGORY_GROUPS] = {
+ "toplevel",
+ "tracing already shutdown",
+ "tracing categories exhausted; must increase MAX_CATEGORY_GROUPS",
+ "__metadata"};
+
+// The enabled flag is char instead of bool so that the API can be used from C.
+unsigned char g_category_group_enabled[MAX_CATEGORY_GROUPS] = {0};
+// Indexes here have to match the g_category_groups array indexes above.
+const int g_category_already_shutdown = 1;
+const int g_category_categories_exhausted = 2;
+const int g_category_metadata = 3;
+const int g_num_builtin_categories = 4;
+// Skip default categories.
+base::subtle::AtomicWord g_category_index = g_num_builtin_categories;
+
+// The name of the current thread. This is used to decide if the current
+// thread name has changed. We combine all the seen thread names into the
+// output name for the thread.
+LazyInstance<ThreadLocalPointer<const char>>::Leaky g_current_thread_name =
+ LAZY_INSTANCE_INITIALIZER;
ThreadTicks ThreadNow() {
return ThreadTicks::IsSupported() ? ThreadTicks::Now() : ThreadTicks();
@@ -118,7 +138,7 @@ void InitializeMetadataEvent(TraceEvent* trace_event,
TimeTicks(),
ThreadTicks(),
TRACE_EVENT_PHASE_METADATA,
- CategoryRegistry::kCategoryMetadata->state_ptr(),
+ &g_category_group_enabled[g_category_metadata],
metadata_name,
trace_event_internal::kGlobalScope, // scope
trace_event_internal::kNoId, // id
@@ -154,24 +174,11 @@ void MakeHandle(uint32_t chunk_seq,
DCHECK(chunk_seq);
DCHECK(chunk_index <= TraceBufferChunk::kMaxChunkIndex);
DCHECK(event_index < TraceBufferChunk::kTraceBufferChunkSize);
- DCHECK(chunk_index <= std::numeric_limits<uint16_t>::max());
handle->chunk_seq = chunk_seq;
handle->chunk_index = static_cast<uint16_t>(chunk_index);
handle->event_index = static_cast<uint16_t>(event_index);
}
-template <typename Function>
-void ForEachCategoryFilter(const unsigned char* category_group_enabled,
- Function filter_fn) {
- const TraceCategory* category =
- CategoryRegistry::GetCategoryByStatePtr(category_group_enabled);
- uint32_t filter_bitmap = category->enabled_filters();
- for (int index = 0; filter_bitmap != 0; filter_bitmap >>= 1, index++) {
- if (filter_bitmap & 1 && GetCategoryGroupFilters()[index])
- filter_fn(GetCategoryGroupFilters()[index].get());
- }
-}
-
} // namespace
// A helper class that allows the lock to be acquired in the middle of the scope
@@ -345,20 +352,33 @@ TraceLog* TraceLog::GetInstance() {
}
TraceLog::TraceLog()
- : enabled_modes_(0),
+ : mode_(DISABLED),
num_traces_recorded_(0),
+ event_callback_(0),
dispatching_to_observer_list_(false),
process_sort_index_(0),
process_id_hash_(0),
process_id_(0),
+ watch_category_(0),
trace_options_(kInternalRecordUntilFull),
+ sampling_thread_handle_(0),
trace_config_(TraceConfig()),
+ event_callback_trace_config_(TraceConfig()),
thread_shared_chunk_index_(0),
generation_(0),
- use_worker_thread_(false),
- filter_factory_for_testing_(nullptr) {
- CategoryRegistry::Initialize();
-
+ use_worker_thread_(false) {
+ // Trace is enabled or disabled on one thread while other threads are
+ // accessing the enabled flag. We don't care whether edge-case events are
+ // traced or not, so we allow races on the enabled flag to keep the trace
+ // macros fast.
+ // TODO(jbates): ANNOTATE_BENIGN_RACE_SIZED crashes windows TSAN bots:
+ // ANNOTATE_BENIGN_RACE_SIZED(g_category_group_enabled,
+ // sizeof(g_category_group_enabled),
+ // "trace_event category enabled");
+ for (int i = 0; i < MAX_CATEGORY_GROUPS; ++i) {
+ ANNOTATE_BENIGN_RACE(&g_category_group_enabled[i],
+ "trace_event category enabled");
+ }
#if defined(OS_NACL) // NaCl shouldn't expose the process id.
SetProcessID(0);
#else
@@ -394,9 +414,7 @@ void TraceLog::InitializeThreadLocalEventBufferIfSupported() {
}
}
-bool TraceLog::OnMemoryDump(const MemoryDumpArgs& args,
- ProcessMemoryDump* pmd) {
- ALLOW_UNUSED_PARAM(args);
+bool TraceLog::OnMemoryDump(const MemoryDumpArgs&, ProcessMemoryDump* pmd) {
// TODO(ssid): Use MemoryDumpArgs to create light dumps when requested
// (crbug.com/499731).
TraceEventMemoryOverhead overhead;
@@ -418,111 +436,61 @@ const unsigned char* TraceLog::GetCategoryGroupEnabled(
const char* category_group) {
TraceLog* tracelog = GetInstance();
if (!tracelog) {
- DCHECK(!CategoryRegistry::kCategoryAlreadyShutdown->is_enabled());
- return CategoryRegistry::kCategoryAlreadyShutdown->state_ptr();
- }
- TraceCategory* category = CategoryRegistry::GetCategoryByName(category_group);
- if (!category) {
- // Slow path: in the case of a new category we have to repeat the check
- // holding the lock, as multiple threads might have reached this point
- // at the same time.
- auto category_initializer = [](TraceCategory* category) {
- TraceLog::GetInstance()->UpdateCategoryState(category);
- };
- AutoLock lock(tracelog->lock_);
- CategoryRegistry::GetOrCreateCategoryLocked(
- category_group, category_initializer, &category);
+ DCHECK(!g_category_group_enabled[g_category_already_shutdown]);
+ return &g_category_group_enabled[g_category_already_shutdown];
}
- DCHECK(category->state_ptr());
- return category->state_ptr();
+ return tracelog->GetCategoryGroupEnabledInternal(category_group);
}
const char* TraceLog::GetCategoryGroupName(
const unsigned char* category_group_enabled) {
- return CategoryRegistry::GetCategoryByStatePtr(category_group_enabled)
- ->name();
+ // Calculate the index of the category group by finding
+ // category_group_enabled in g_category_group_enabled array.
+ uintptr_t category_begin =
+ reinterpret_cast<uintptr_t>(g_category_group_enabled);
+ uintptr_t category_ptr = reinterpret_cast<uintptr_t>(category_group_enabled);
+ DCHECK(category_ptr >= category_begin &&
+ category_ptr < reinterpret_cast<uintptr_t>(g_category_group_enabled +
+ MAX_CATEGORY_GROUPS))
+ << "out of bounds category pointer";
+ uintptr_t category_index =
+ (category_ptr - category_begin) / sizeof(g_category_group_enabled[0]);
+ return g_category_groups[category_index];
}
-void TraceLog::UpdateCategoryState(TraceCategory* category) {
- lock_.AssertAcquired();
- DCHECK(category->is_valid());
- unsigned char state_flags = 0;
- if (enabled_modes_ & RECORDING_MODE &&
- trace_config_.IsCategoryGroupEnabled(category->name())) {
- state_flags |= TraceCategory::ENABLED_FOR_RECORDING;
+void TraceLog::UpdateCategoryGroupEnabledFlag(size_t category_index) {
+ unsigned char enabled_flag = 0;
+ const char* category_group = g_category_groups[category_index];
+ if (mode_ == RECORDING_MODE &&
+ trace_config_.IsCategoryGroupEnabled(category_group)) {
+ enabled_flag |= ENABLED_FOR_RECORDING;
}
- // TODO(primiano): this is a temporary workaround for catapult:#2341,
- // to guarantee that metadata events are always added even if the category
- // filter is "-*". See crbug.com/618054 for more details and long-term fix.
- if (enabled_modes_ & RECORDING_MODE &&
- category == CategoryRegistry::kCategoryMetadata) {
- state_flags |= TraceCategory::ENABLED_FOR_RECORDING;
+ if (event_callback_ &&
+ event_callback_trace_config_.IsCategoryGroupEnabled(category_group)) {
+ enabled_flag |= ENABLED_FOR_EVENT_CALLBACK;
}
#if defined(OS_WIN)
if (base::trace_event::TraceEventETWExport::IsCategoryGroupEnabled(
- category->name())) {
- state_flags |= TraceCategory::ENABLED_FOR_ETW_EXPORT;
+ category_group)) {
+ enabled_flag |= ENABLED_FOR_ETW_EXPORT;
}
#endif
- uint32_t enabled_filters_bitmap = 0;
- int index = 0;
- for (const auto& event_filter : enabled_event_filters_) {
- if (event_filter.IsCategoryGroupEnabled(category->name())) {
- state_flags |= TraceCategory::ENABLED_FOR_FILTERING;
- DCHECK(GetCategoryGroupFilters()[index]);
- enabled_filters_bitmap |= 1 << index;
- }
- if (index++ >= MAX_TRACE_EVENT_FILTERS) {
- NOTREACHED();
- break;
- }
- }
- category->set_enabled_filters(enabled_filters_bitmap);
- category->set_state(state_flags);
-}
+ // TODO(primiano): this is a temporary workaround for catapult:#2341,
+ // to guarantee that metadata events are always added even if the category
+ // filter is "-*". See crbug.com/618054 for more details and long-term fix.
+ if (mode_ == RECORDING_MODE && !strcmp(category_group, "__metadata"))
+ enabled_flag |= ENABLED_FOR_RECORDING;
-void TraceLog::UpdateCategoryRegistry() {
- lock_.AssertAcquired();
- CreateFiltersForTraceConfig();
- for (TraceCategory& category : CategoryRegistry::GetAllCategories()) {
- UpdateCategoryState(&category);
- }
+ g_category_group_enabled[category_index] = enabled_flag;
}
-void TraceLog::CreateFiltersForTraceConfig() {
- if (!(enabled_modes_ & FILTERING_MODE))
- return;
-
- // Filters were already added and tracing could be enabled. Filters list
- // cannot be changed when trace events are using them.
- if (GetCategoryGroupFilters().size())
- return;
-
- for (auto& filter_config : enabled_event_filters_) {
- if (GetCategoryGroupFilters().size() >= MAX_TRACE_EVENT_FILTERS) {
- NOTREACHED()
- << "Too many trace event filters installed in the current session";
- break;
- }
-
- std::unique_ptr<TraceEventFilter> new_filter;
- const std::string& predicate_name = filter_config.predicate_name();
- if (predicate_name == EventNameFilter::kName) {
- auto whitelist = MakeUnique<std::unordered_set<std::string>>();
- CHECK(filter_config.GetArgAsSet("event_name_whitelist", &*whitelist));
- new_filter = MakeUnique<EventNameFilter>(std::move(whitelist));
- } else if (predicate_name == HeapProfilerEventFilter::kName) {
- new_filter = MakeUnique<HeapProfilerEventFilter>();
- } else {
- if (filter_factory_for_testing_)
- new_filter = filter_factory_for_testing_(predicate_name);
- CHECK(new_filter) << "Unknown trace filter " << predicate_name;
- }
- GetCategoryGroupFilters().push_back(std::move(new_filter));
- }
+void TraceLog::UpdateCategoryGroupEnabledFlags() {
+ size_t category_index = base::subtle::NoBarrier_Load(&g_category_index);
+ for (size_t i = 0; i < category_index; i++)
+ UpdateCategoryGroupEnabledFlag(i);
}
void TraceLog::UpdateSyntheticDelaysFromTraceConfig() {
@@ -554,16 +522,67 @@ void TraceLog::UpdateSyntheticDelaysFromTraceConfig() {
}
}
+const unsigned char* TraceLog::GetCategoryGroupEnabledInternal(
+ const char* category_group) {
+ DCHECK(!strchr(category_group, '"'))
+ << "Category groups may not contain double quote";
+ // The g_category_groups is append only, avoid using a lock for the fast path.
+ size_t current_category_index = base::subtle::Acquire_Load(&g_category_index);
+
+ // Search for pre-existing category group.
+ for (size_t i = 0; i < current_category_index; ++i) {
+ if (strcmp(g_category_groups[i], category_group) == 0) {
+ return &g_category_group_enabled[i];
+ }
+ }
+
+ unsigned char* category_group_enabled = NULL;
+ // This is the slow path: the lock is not held in the case above, so more
+ // than one thread could have reached here trying to add the same category.
+ // Only hold to lock when actually appending a new category, and
+ // check the categories groups again.
+ AutoLock lock(lock_);
+ size_t category_index = base::subtle::Acquire_Load(&g_category_index);
+ for (size_t i = 0; i < category_index; ++i) {
+ if (strcmp(g_category_groups[i], category_group) == 0) {
+ return &g_category_group_enabled[i];
+ }
+ }
+
+ // Create a new category group.
+ DCHECK(category_index < MAX_CATEGORY_GROUPS)
+ << "must increase MAX_CATEGORY_GROUPS";
+ if (category_index < MAX_CATEGORY_GROUPS) {
+ // Don't hold on to the category_group pointer, so that we can create
+ // category groups with strings not known at compile time (this is
+ // required by SetWatchEvent).
+ const char* new_group = strdup(category_group);
+ ANNOTATE_LEAKING_OBJECT_PTR(new_group);
+ g_category_groups[category_index] = new_group;
+ DCHECK(!g_category_group_enabled[category_index]);
+ // Note that if both included and excluded patterns in the
+ // TraceConfig are empty, we exclude nothing,
+ // thereby enabling this category group.
+ UpdateCategoryGroupEnabledFlag(category_index);
+ category_group_enabled = &g_category_group_enabled[category_index];
+ // Update the max index now.
+ base::subtle::Release_Store(&g_category_index, category_index + 1);
+ } else {
+ category_group_enabled =
+ &g_category_group_enabled[g_category_categories_exhausted];
+ }
+ return category_group_enabled;
+}
+
void TraceLog::GetKnownCategoryGroups(
std::vector<std::string>* category_groups) {
- for (const auto& category : CategoryRegistry::GetAllCategories()) {
- if (!CategoryRegistry::IsBuiltinCategory(&category))
- category_groups->push_back(category.name());
- }
+ AutoLock lock(lock_);
+ size_t category_index = base::subtle::NoBarrier_Load(&g_category_index);
+ for (size_t i = g_num_builtin_categories; i < category_index; i++)
+ category_groups->push_back(g_category_groups[i]);
}
-void TraceLog::SetEnabled(const TraceConfig& trace_config,
- uint8_t modes_to_enable) {
+void TraceLog::SetEnabled(const TraceConfig& trace_config, Mode mode) {
std::vector<EnabledStateObserver*> observer_list;
std::map<AsyncEnabledStateObserver*, RegisteredAsyncObserver> observer_map;
{
@@ -577,58 +596,28 @@ void TraceLog::SetEnabled(const TraceConfig& trace_config,
InternalTraceOptions old_options = trace_options();
- if (dispatching_to_observer_list_) {
- // TODO(ssid): Change to NOTREACHED after fixing crbug.com/625170.
- DLOG(ERROR)
- << "Cannot manipulate TraceLog::Enabled state from an observer.";
- return;
- }
+ if (IsEnabled()) {
+ if (new_options != old_options) {
+ DLOG(ERROR) << "Attempting to re-enable tracing with a different "
+ << "set of options.";
+ }
- // Clear all filters from previous tracing session. These filters are not
- // cleared at the end of tracing because some threads which hit trace event
- // when disabling, could try to use the filters.
- if (!enabled_modes_)
- GetCategoryGroupFilters().clear();
-
- // Update trace config for recording.
- const bool already_recording = enabled_modes_ & RECORDING_MODE;
- if (modes_to_enable & RECORDING_MODE) {
- if (already_recording) {
- // TODO(ssid): Stop suporting enabling of RECODING_MODE when already
- // enabled crbug.com/625170.
- DCHECK_EQ(new_options, old_options) << "Attempting to re-enable "
- "tracing with a different set "
- "of options.";
- trace_config_.Merge(trace_config);
- } else {
- trace_config_ = trace_config;
+ if (mode != mode_) {
+ DLOG(ERROR) << "Attempting to re-enable tracing with a different mode.";
}
- }
- // Update event filters.
- if (modes_to_enable & FILTERING_MODE) {
- DCHECK(!trace_config.event_filters().empty())
- << "Attempting to enable filtering without any filters";
- DCHECK(enabled_event_filters_.empty()) << "Attempting to re-enable "
- "filtering when filters are "
- "already enabled.";
-
- // Use the given event filters only if filtering was not enabled.
- if (enabled_event_filters_.empty())
- enabled_event_filters_ = trace_config.event_filters();
+ trace_config_.Merge(trace_config);
+ UpdateCategoryGroupEnabledFlags();
+ return;
}
- // Keep the |trace_config_| updated with only enabled filters in case anyone
- // tries to read it using |GetCurrentTraceConfig| (even if filters are
- // empty).
- trace_config_.SetEventFilters(enabled_event_filters_);
-
- enabled_modes_ |= modes_to_enable;
- UpdateCategoryRegistry();
- // Do not notify observers or create trace buffer if only enabled for
- // filtering or if recording was already enabled.
- if (!(modes_to_enable & RECORDING_MODE) || already_recording)
+ if (dispatching_to_observer_list_) {
+ DLOG(ERROR)
+ << "Cannot manipulate TraceLog::Enabled state from an observer.";
return;
+ }
+
+ mode_ = mode;
if (new_options != old_options) {
subtle::NoBarrier_Store(&trace_options_, new_options);
@@ -637,16 +626,34 @@ void TraceLog::SetEnabled(const TraceConfig& trace_config,
num_traces_recorded_++;
- UpdateCategoryRegistry();
+ trace_config_ = TraceConfig(trace_config);
+ UpdateCategoryGroupEnabledFlags();
UpdateSyntheticDelaysFromTraceConfig();
+ if (new_options & kInternalEnableSampling) {
+ sampling_thread_.reset(new TraceSamplingThread);
+ sampling_thread_->RegisterSampleBucket(
+ &g_trace_state[0], "bucket0",
+ Bind(&TraceSamplingThread::DefaultSamplingCallback));
+ sampling_thread_->RegisterSampleBucket(
+ &g_trace_state[1], "bucket1",
+ Bind(&TraceSamplingThread::DefaultSamplingCallback));
+ sampling_thread_->RegisterSampleBucket(
+ &g_trace_state[2], "bucket2",
+ Bind(&TraceSamplingThread::DefaultSamplingCallback));
+ if (!PlatformThread::Create(0, sampling_thread_.get(),
+ &sampling_thread_handle_)) {
+ DCHECK(false) << "failed to create thread";
+ }
+ }
+
dispatching_to_observer_list_ = true;
observer_list = enabled_state_observer_list_;
observer_map = async_observers_;
}
// Notify observers outside the lock in case they trigger trace events.
- for (EnabledStateObserver* observer : observer_list)
- observer->OnTraceLogEnabled();
+ for (size_t i = 0; i < observer_list.size(); ++i)
+ observer_list[i]->OnTraceLogEnabled();
for (const auto& it : observer_map) {
it.second.task_runner->PostTask(
FROM_HERE, Bind(&AsyncEnabledStateObserver::OnTraceLogEnabled,
@@ -669,9 +676,10 @@ void TraceLog::SetArgumentFilterPredicate(
TraceLog::InternalTraceOptions TraceLog::GetInternalOptionsFromTraceConfig(
const TraceConfig& config) {
- InternalTraceOptions ret = config.IsArgumentFilterEnabled()
- ? kInternalEnableArgumentFilter
- : kInternalNone;
+ InternalTraceOptions ret =
+ config.IsSamplingEnabled() ? kInternalEnableSampling : kInternalNone;
+ if (config.IsArgumentFilterEnabled())
+ ret |= kInternalEnableArgumentFilter;
switch (config.GetTraceRecordMode()) {
case RECORD_UNTIL_FULL:
return ret | kInternalRecordUntilFull;
@@ -693,44 +701,37 @@ TraceConfig TraceLog::GetCurrentTraceConfig() const {
void TraceLog::SetDisabled() {
AutoLock lock(lock_);
- SetDisabledWhileLocked(RECORDING_MODE);
-}
-
-void TraceLog::SetDisabled(uint8_t modes_to_disable) {
- AutoLock lock(lock_);
- SetDisabledWhileLocked(modes_to_disable);
+ SetDisabledWhileLocked();
}
-void TraceLog::SetDisabledWhileLocked(uint8_t modes_to_disable) {
+void TraceLog::SetDisabledWhileLocked() {
lock_.AssertAcquired();
- if (!(enabled_modes_ & modes_to_disable))
+ if (!IsEnabled())
return;
if (dispatching_to_observer_list_) {
- // TODO(ssid): Change to NOTREACHED after fixing crbug.com/625170.
DLOG(ERROR)
<< "Cannot manipulate TraceLog::Enabled state from an observer.";
return;
}
- bool is_recording_mode_disabled =
- (enabled_modes_ & RECORDING_MODE) && (modes_to_disable & RECORDING_MODE);
- enabled_modes_ &= ~modes_to_disable;
-
- if (modes_to_disable & FILTERING_MODE)
- enabled_event_filters_.clear();
-
- if (modes_to_disable & RECORDING_MODE)
- trace_config_.Clear();
+ mode_ = DISABLED;
- UpdateCategoryRegistry();
-
- // Add metadata events and notify observers only if recording mode was
- // disabled now.
- if (!is_recording_mode_disabled)
- return;
+ if (sampling_thread_.get()) {
+ // Stop the sampling thread.
+ sampling_thread_->Stop();
+ lock_.Release();
+ PlatformThread::Join(sampling_thread_handle_);
+ lock_.Acquire();
+ sampling_thread_handle_ = PlatformThreadHandle();
+ sampling_thread_.reset();
+ }
+ trace_config_.Clear();
+ subtle::NoBarrier_Store(&watch_category_, 0);
+ watch_event_name_ = "";
+ UpdateCategoryGroupEnabledFlags();
AddMetadataEventsWhileLocked();
// Remove metadata events so they will not get added to a subsequent trace.
@@ -746,8 +747,8 @@ void TraceLog::SetDisabledWhileLocked(uint8_t modes_to_disable) {
// Dispatch to observers outside the lock in case the observer triggers a
// trace event.
AutoUnlock unlock(lock_);
- for (EnabledStateObserver* observer : observer_list)
- observer->OnTraceLogDisabled();
+ for (size_t i = 0; i < observer_list.size(); ++i)
+ observer_list[i]->OnTraceLogDisabled();
for (const auto& it : observer_map) {
it.second.task_runner->PostTask(
FROM_HERE, Bind(&AsyncEnabledStateObserver::OnTraceLogDisabled,
@@ -830,10 +831,25 @@ void TraceLog::CheckIfBufferIsFullWhileLocked() {
if (buffer_limit_reached_timestamp_.is_null()) {
buffer_limit_reached_timestamp_ = OffsetNow();
}
- SetDisabledWhileLocked(RECORDING_MODE);
+ SetDisabledWhileLocked();
}
}
+void TraceLog::SetEventCallbackEnabled(const TraceConfig& trace_config,
+ EventCallback cb) {
+ AutoLock lock(lock_);
+ subtle::NoBarrier_Store(&event_callback_,
+ reinterpret_cast<subtle::AtomicWord>(cb));
+ event_callback_trace_config_ = trace_config;
+ UpdateCategoryGroupEnabledFlags();
+}
+
+void TraceLog::SetEventCallbackDisabled() {
+ AutoLock lock(lock_);
+ subtle::NoBarrier_Store(&event_callback_, 0);
+ UpdateCategoryGroupEnabledFlags();
+}
+
// Flush() works as the following:
// 1. Flush() is called in thread A whose task runner is saved in
// flush_task_runner_;
@@ -870,7 +886,7 @@ void TraceLog::FlushInternal(const TraceLog::OutputCallback& cb,
return;
}
- int gen = generation();
+ int generation = this->generation();
// Copy of thread_message_loops_ to be used without locking.
std::vector<scoped_refptr<SingleThreadTaskRunner>>
thread_message_loop_task_runners;
@@ -888,24 +904,29 @@ void TraceLog::FlushInternal(const TraceLog::OutputCallback& cb,
std::move(thread_shared_chunk_));
}
- for (MessageLoop* loop : thread_message_loops_)
- thread_message_loop_task_runners.push_back(loop->task_runner());
+ if (thread_message_loops_.size()) {
+ for (hash_set<MessageLoop*>::const_iterator it =
+ thread_message_loops_.begin();
+ it != thread_message_loops_.end(); ++it) {
+ thread_message_loop_task_runners.push_back((*it)->task_runner());
+ }
+ }
}
- if (!thread_message_loop_task_runners.empty()) {
- for (auto& task_runner : thread_message_loop_task_runners) {
- task_runner->PostTask(
+ if (thread_message_loop_task_runners.size()) {
+ for (size_t i = 0; i < thread_message_loop_task_runners.size(); ++i) {
+ thread_message_loop_task_runners[i]->PostTask(
FROM_HERE, Bind(&TraceLog::FlushCurrentThread, Unretained(this),
- gen, discard_events));
+ generation, discard_events));
}
flush_task_runner_->PostDelayedTask(
- FROM_HERE, Bind(&TraceLog::OnFlushTimeout, Unretained(this), gen,
+ FROM_HERE, Bind(&TraceLog::OnFlushTimeout, Unretained(this), generation,
discard_events),
TimeDelta::FromMilliseconds(kThreadFlushTimeoutMs));
return;
}
- FinishFlush(gen, discard_events);
+ FinishFlush(generation, discard_events);
}
// Usually it runs on a different thread.
@@ -969,21 +990,13 @@ void TraceLog::FinishFlush(int generation, bool discard_events) {
return;
}
- if (use_worker_thread_) {
-#if 0
- base::PostTaskWithTraits(
- FROM_HERE, base::TaskTraits()
- .MayBlock()
- .WithPriority(base::TaskPriority::BACKGROUND)
- .WithShutdownBehavior(
- base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN),
- Bind(&TraceLog::ConvertTraceEventsToTraceFormat,
- Passed(&previous_logged_events), flush_output_callback,
- argument_filter_predicate));
+ if (use_worker_thread_ &&
+ WorkerPool::PostTask(
+ FROM_HERE, Bind(&TraceLog::ConvertTraceEventsToTraceFormat,
+ Passed(&previous_logged_events),
+ flush_output_callback, argument_filter_predicate),
+ true)) {
return;
-#else
- NOTREACHED();
-#endif
}
ConvertTraceEventsToTraceFormat(std::move(previous_logged_events),
@@ -1006,7 +1019,7 @@ void TraceLog::FlushCurrentThread(int generation, bool discard_events) {
AutoLock lock(lock_);
if (!CheckGeneration(generation) || !flush_task_runner_ ||
- !thread_message_loops_.empty())
+ thread_message_loops_.size())
return;
flush_task_runner_->PostTask(
@@ -1210,13 +1223,10 @@ TraceEventHandle TraceLog::AddTraceEventWithThreadIdAndTimestamp(
TimeTicks offset_event_timestamp = OffsetTimestamp(timestamp);
ThreadTicks thread_now = ThreadNow();
- ThreadLocalEventBuffer* thread_local_event_buffer = nullptr;
- if (*category_group_enabled & RECORDING_MODE) {
- // |thread_local_event_buffer_| can be null if the current thread doesn't
- // have a message loop or the message loop is blocked.
- InitializeThreadLocalEventBufferIfSupported();
- thread_local_event_buffer = thread_local_event_buffer_.Get();
- }
+ // |thread_local_event_buffer_| can be null if the current thread doesn't have
+ // a message loop or the message loop is blocked.
+ InitializeThreadLocalEventBufferIfSupported();
+ auto* thread_local_event_buffer = thread_local_event_buffer_.Get();
// Check and update the current thread name only if the event is for the
// current thread to avoid locks in most cases.
@@ -1227,9 +1237,9 @@ TraceEventHandle TraceLog::AddTraceEventWithThreadIdAndTimestamp(
// call (if any), but don't bother if the new name is empty. Note this will
// not detect a thread name change within the same char* buffer address: we
// favor common case performance over corner case correctness.
- static auto* current_thread_name = new ThreadLocalPointer<const char>();
- if (new_name != current_thread_name->Get() && new_name && *new_name) {
- current_thread_name->Set(new_name);
+ if (new_name != g_current_thread_name.Get().Get() && new_name &&
+ *new_name) {
+ g_current_thread_name.Get().Set(new_name);
AutoLock thread_info_lock(thread_info_lock_);
@@ -1247,7 +1257,7 @@ TraceEventHandle TraceLog::AddTraceEventWithThreadIdAndTimestamp(
bool found = std::find(existing_names.begin(), existing_names.end(),
new_name) != existing_names.end();
if (!found) {
- if (!existing_names.empty())
+ if (existing_names.size())
existing_name->second.push_back(',');
existing_name->second.append(new_name);
}
@@ -1258,37 +1268,14 @@ TraceEventHandle TraceLog::AddTraceEventWithThreadIdAndTimestamp(
#if defined(OS_WIN)
// This is done sooner rather than later, to avoid creating the event and
// acquiring the lock, which is not needed for ETW as it's already threadsafe.
- if (*category_group_enabled & TraceCategory::ENABLED_FOR_ETW_EXPORT)
+ if (*category_group_enabled & ENABLED_FOR_ETW_EXPORT)
TraceEventETWExport::AddEvent(phase, category_group_enabled, name, id,
num_args, arg_names, arg_types, arg_values,
convertable_values);
#endif // OS_WIN
std::string console_message;
- std::unique_ptr<TraceEvent> filtered_trace_event;
- bool disabled_by_filters = false;
- if (*category_group_enabled & TraceCategory::ENABLED_FOR_FILTERING) {
- std::unique_ptr<TraceEvent> new_trace_event(new TraceEvent);
- new_trace_event->Initialize(thread_id, offset_event_timestamp, thread_now,
- phase, category_group_enabled, name, scope, id,
- bind_id, num_args, arg_names, arg_types,
- arg_values, convertable_values, flags);
-
- disabled_by_filters = true;
- ForEachCategoryFilter(
- category_group_enabled, [&new_trace_event, &disabled_by_filters](
- TraceEventFilter* trace_event_filter) {
- if (trace_event_filter->FilterTraceEvent(*new_trace_event))
- disabled_by_filters = false;
- });
- if (!disabled_by_filters)
- filtered_trace_event = std::move(new_trace_event);
- }
-
- // If enabled for recording, the event should be added only if one of the
- // filters indicates or category is not enabled for filtering.
- if ((*category_group_enabled & TraceCategory::ENABLED_FOR_RECORDING) &&
- !disabled_by_filters) {
+ if (*category_group_enabled & ENABLED_FOR_RECORDING) {
OptionalAutoLock lock(&lock_);
TraceEvent* trace_event = NULL;
@@ -1300,14 +1287,21 @@ TraceEventHandle TraceLog::AddTraceEventWithThreadIdAndTimestamp(
}
if (trace_event) {
- if (filtered_trace_event) {
- trace_event->MoveFrom(std::move(filtered_trace_event));
- } else {
- trace_event->Initialize(thread_id, offset_event_timestamp, thread_now,
- phase, category_group_enabled, name, scope, id,
- bind_id, num_args, arg_names, arg_types,
- arg_values, convertable_values, flags);
- }
+ trace_event->Initialize(thread_id,
+ offset_event_timestamp,
+ thread_now,
+ phase,
+ category_group_enabled,
+ name,
+ scope,
+ id,
+ bind_id,
+ num_args,
+ arg_names,
+ arg_types,
+ arg_values,
+ convertable_values,
+ flags);
#if defined(OS_ANDROID)
trace_event->SendToATrace();
@@ -1321,9 +1315,53 @@ TraceEventHandle TraceLog::AddTraceEventWithThreadIdAndTimestamp(
}
}
- if (!console_message.empty())
+ if (console_message.size())
LOG(ERROR) << console_message;
+ if (reinterpret_cast<const unsigned char*>(
+ subtle::NoBarrier_Load(&watch_category_)) == category_group_enabled) {
+ bool event_name_matches;
+ WatchEventCallback watch_event_callback_copy;
+ {
+ AutoLock lock(lock_);
+ event_name_matches = watch_event_name_ == name;
+ watch_event_callback_copy = watch_event_callback_;
+ }
+ if (event_name_matches) {
+ if (!watch_event_callback_copy.is_null())
+ watch_event_callback_copy.Run();
+ }
+ }
+
+ if (*category_group_enabled & ENABLED_FOR_EVENT_CALLBACK) {
+ EventCallback event_callback = reinterpret_cast<EventCallback>(
+ subtle::NoBarrier_Load(&event_callback_));
+ if (event_callback) {
+ event_callback(
+ offset_event_timestamp,
+ phase == TRACE_EVENT_PHASE_COMPLETE ? TRACE_EVENT_PHASE_BEGIN : phase,
+ category_group_enabled, name, scope, id, num_args, arg_names,
+ arg_types, arg_values, flags);
+ }
+ }
+
+ // TODO(primiano): Add support for events with copied name crbug.com/581078
+ if (!(flags & TRACE_EVENT_FLAG_COPY)) {
+ if (AllocationContextTracker::capture_mode() ==
+ AllocationContextTracker::CaptureMode::PSEUDO_STACK) {
+ if (phase == TRACE_EVENT_PHASE_BEGIN ||
+ phase == TRACE_EVENT_PHASE_COMPLETE) {
+ AllocationContextTracker::GetInstanceForCurrentThread()
+ ->PushPseudoStackFrame(name);
+ } else if (phase == TRACE_EVENT_PHASE_END) {
+ // The pop for |TRACE_EVENT_PHASE_COMPLETE| events
+ // is in |TraceLog::UpdateTraceEventDuration|.
+ AllocationContextTracker::GetInstanceForCurrentThread()
+ ->PopPseudoStackFrame(name);
+ }
+ }
+ }
+
return handle;
}
@@ -1381,9 +1419,9 @@ std::string TraceLog::EventToConsoleMessage(unsigned char phase,
thread_colors_[thread_name]);
size_t depth = 0;
- auto it = thread_event_start_times_.find(thread_id);
- if (it != thread_event_start_times_.end())
- depth = it->second.size();
+ if (thread_event_start_times_.find(thread_id) !=
+ thread_event_start_times_.end())
+ depth = thread_event_start_times_[thread_id].size();
for (size_t i = 0; i < depth; ++i)
log << "| ";
@@ -1401,18 +1439,6 @@ std::string TraceLog::EventToConsoleMessage(unsigned char phase,
return log.str();
}
-void TraceLog::EndFilteredEvent(const unsigned char* category_group_enabled,
- const char* name,
- TraceEventHandle handle) {
- ALLOW_UNUSED_PARAM(handle);
- const char* category_name = GetCategoryGroupName(category_group_enabled);
- ForEachCategoryFilter(
- category_group_enabled,
- [name, category_name](TraceEventFilter* trace_event_filter) {
- trace_event_filter->EndEvent(category_name, name);
- });
-}
-
void TraceLog::UpdateTraceEventDuration(
const unsigned char* category_group_enabled,
const char* name,
@@ -1434,29 +1460,17 @@ void TraceLog::UpdateTraceEventDuration(
#if defined(OS_WIN)
// Generate an ETW event that marks the end of a complete event.
- if (category_group_enabled_local & TraceCategory::ENABLED_FOR_ETW_EXPORT)
+ if (category_group_enabled_local & ENABLED_FOR_ETW_EXPORT)
TraceEventETWExport::AddCompleteEndEvent(name);
#endif // OS_WIN
std::string console_message;
- if (category_group_enabled_local & TraceCategory::ENABLED_FOR_RECORDING) {
+ if (category_group_enabled_local & ENABLED_FOR_RECORDING) {
OptionalAutoLock lock(&lock_);
TraceEvent* trace_event = GetEventByHandleInternal(handle, &lock);
if (trace_event) {
DCHECK(trace_event->phase() == TRACE_EVENT_PHASE_COMPLETE);
- // TEMP(oysteine) to debug crbug.com/638744
- if (trace_event->duration().ToInternalValue() != -1) {
- DVLOG(1) << "TraceHandle: chunk_seq " << handle.chunk_seq
- << ", chunk_index " << handle.chunk_index << ", event_index "
- << handle.event_index;
-
- std::string serialized_event;
- trace_event->AppendAsJSON(&serialized_event, ArgumentFilterPredicate());
- DVLOG(1) << "TraceEvent: " << serialized_event;
- lock_.AssertAcquired();
- }
-
trace_event->UpdateDuration(now, thread_now);
#if defined(OS_ANDROID)
trace_event->SendToATrace();
@@ -1467,13 +1481,47 @@ void TraceLog::UpdateTraceEventDuration(
console_message =
EventToConsoleMessage(TRACE_EVENT_PHASE_END, now, trace_event);
}
+
+ if (AllocationContextTracker::capture_mode() ==
+ AllocationContextTracker::CaptureMode::PSEUDO_STACK) {
+ // The corresponding push is in |AddTraceEventWithThreadIdAndTimestamp|.
+ AllocationContextTracker::GetInstanceForCurrentThread()
+ ->PopPseudoStackFrame(name);
+ }
}
- if (!console_message.empty())
+ if (console_message.size())
LOG(ERROR) << console_message;
- if (category_group_enabled_local & TraceCategory::ENABLED_FOR_FILTERING)
- EndFilteredEvent(category_group_enabled, name, handle);
+ if (category_group_enabled_local & ENABLED_FOR_EVENT_CALLBACK) {
+ EventCallback event_callback = reinterpret_cast<EventCallback>(
+ subtle::NoBarrier_Load(&event_callback_));
+ if (event_callback) {
+ event_callback(
+ now, TRACE_EVENT_PHASE_END, category_group_enabled, name,
+ trace_event_internal::kGlobalScope, trace_event_internal::kNoId, 0,
+ nullptr, nullptr, nullptr, TRACE_EVENT_FLAG_NONE);
+ }
+ }
+}
+
+void TraceLog::SetWatchEvent(const std::string& category_name,
+ const std::string& event_name,
+ const WatchEventCallback& callback) {
+ const unsigned char* category =
+ GetCategoryGroupEnabled(category_name.c_str());
+ AutoLock lock(lock_);
+ subtle::NoBarrier_Store(&watch_category_,
+ reinterpret_cast<subtle::AtomicWord>(category));
+ watch_event_name_ = event_name;
+ watch_event_callback_ = callback;
+}
+
+void TraceLog::CancelWatchEvent() {
+ AutoLock lock(lock_);
+ subtle::NoBarrier_Store(&watch_category_, 0);
+ watch_event_name_ = "";
+ watch_event_callback_.Reset();
}
uint64_t TraceLog::MangleEventId(uint64_t id) {
@@ -1503,37 +1551,42 @@ void TraceLog::AddMetadataEventsWhileLocked() {
"sort_index", process_sort_index_);
}
- if (!process_name_.empty()) {
+ if (process_name_.size()) {
InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false),
current_thread_id, "process_name", "name",
process_name_);
}
- if (!process_labels_.empty()) {
+ if (process_labels_.size() > 0) {
std::vector<std::string> labels;
- for (const auto& it : process_labels_)
- labels.push_back(it.second);
+ for (base::hash_map<int, std::string>::iterator it =
+ process_labels_.begin();
+ it != process_labels_.end(); it++) {
+ labels.push_back(it->second);
+ }
InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false),
current_thread_id, "process_labels", "labels",
base::JoinString(labels, ","));
}
// Thread sort indices.
- for (const auto& it : thread_sort_indices_) {
- if (it.second == 0)
+ for (hash_map<int, int>::iterator it = thread_sort_indices_.begin();
+ it != thread_sort_indices_.end(); it++) {
+ if (it->second == 0)
continue;
InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false),
- it.first, "thread_sort_index", "sort_index",
- it.second);
+ it->first, "thread_sort_index", "sort_index",
+ it->second);
}
// Thread names.
AutoLock thread_info_lock(thread_info_lock_);
- for (const auto& it : thread_names_) {
- if (it.second.empty())
+ for (hash_map<int, std::string>::iterator it = thread_names_.begin();
+ it != thread_names_.end(); it++) {
+ if (it->second.empty())
continue;
InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false),
- it.first, "thread_name", "name", it.second);
+ it->first, "thread_name", "name", it->second);
}
// If buffer is full, add a metadata record to report this.
@@ -1545,9 +1598,14 @@ void TraceLog::AddMetadataEventsWhileLocked() {
}
}
+void TraceLog::WaitSamplingEventForTesting() {
+ if (!sampling_thread_)
+ return;
+ sampling_thread_->WaitSamplingEventForTesting();
+}
+
void TraceLog::DeleteForTesting() {
internal::DeleteTraceLogForTesting::Delete();
- CategoryRegistry::ResetForTesting();
}
TraceEvent* TraceLog::GetEventByHandle(TraceEventHandle handle) {
@@ -1559,10 +1617,6 @@ TraceEvent* TraceLog::GetEventByHandleInternal(TraceEventHandle handle,
if (!handle.chunk_seq)
return NULL;
- DCHECK(handle.chunk_seq);
- DCHECK(handle.chunk_index <= TraceBufferChunk::kMaxChunkIndex);
- DCHECK(handle.event_index < TraceBufferChunk::kTraceBufferChunkSize);
-
if (thread_local_event_buffer_.Get()) {
TraceEvent* trace_event =
thread_local_event_buffer_.Get()->GetEventByHandle(handle);
@@ -1589,10 +1643,10 @@ void TraceLog::SetProcessID(int process_id) {
process_id_ = process_id;
// Create a FNV hash from the process ID for XORing.
// See http://isthe.com/chongo/tech/comp/fnv/ for algorithm details.
- const unsigned long long kOffsetBasis = 14695981039346656037ull;
- const unsigned long long kFnvPrime = 1099511628211ull;
- const unsigned long long pid = static_cast<unsigned long long>(process_id_);
- process_id_hash_ = (kOffsetBasis ^ pid) * kFnvPrime;
+ unsigned long long offset_basis = 14695981039346656037ull;
+ unsigned long long fnv_prime = 1099511628211ull;
+ unsigned long long pid = static_cast<unsigned long long>(process_id_);
+ process_id_hash_ = (offset_basis ^ pid) * fnv_prime;
}
void TraceLog::SetProcessSortIndex(int sort_index) {
@@ -1600,7 +1654,7 @@ void TraceLog::SetProcessSortIndex(int sort_index) {
process_sort_index_ = sort_index;
}
-void TraceLog::SetProcessName(const char* process_name) {
+void TraceLog::SetProcessName(const std::string& process_name) {
AutoLock lock(lock_);
process_name_ = process_name;
}
@@ -1616,7 +1670,12 @@ void TraceLog::UpdateProcessLabel(int label_id,
void TraceLog::RemoveProcessLabel(int label_id) {
AutoLock lock(lock_);
- process_labels_.erase(label_id);
+ base::hash_map<int, std::string>::iterator it =
+ process_labels_.find(label_id);
+ if (it == process_labels_.end())
+ return;
+
+ process_labels_.erase(it);
}
void TraceLog::SetThreadSortIndex(PlatformThreadId thread_id, int sort_index) {
@@ -1634,39 +1693,42 @@ size_t TraceLog::GetObserverCountForTest() const {
void TraceLog::SetCurrentThreadBlocksMessageLoop() {
thread_blocks_message_loop_.Set(true);
- // This will flush the thread local buffer.
- delete thread_local_event_buffer_.Get();
+ if (thread_local_event_buffer_.Get()) {
+ // This will flush the thread local buffer.
+ delete thread_local_event_buffer_.Get();
+ }
}
TraceBuffer* TraceLog::CreateTraceBuffer() {
HEAP_PROFILER_SCOPED_IGNORE;
InternalTraceOptions options = trace_options();
- if (options & kInternalRecordContinuously) {
+ if (options & kInternalRecordContinuously)
return TraceBuffer::CreateTraceBufferRingBuffer(
kTraceEventRingBufferChunks);
- }
- if (options & kInternalEchoToConsole) {
+ else if (options & kInternalEchoToConsole)
return TraceBuffer::CreateTraceBufferRingBuffer(
kEchoToConsoleTraceEventBufferChunks);
- }
- if (options & kInternalRecordAsMuchAsPossible) {
+ else if (options & kInternalRecordAsMuchAsPossible)
return TraceBuffer::CreateTraceBufferVectorOfSize(
kTraceEventVectorBigBufferChunks);
- }
return TraceBuffer::CreateTraceBufferVectorOfSize(
kTraceEventVectorBufferChunks);
}
#if defined(OS_WIN)
void TraceLog::UpdateETWCategoryGroupEnabledFlags() {
+ AutoLock lock(lock_);
+ size_t category_index = base::subtle::NoBarrier_Load(&g_category_index);
// Go through each category and set/clear the ETW bit depending on whether the
// category is enabled.
- for (TraceCategory& category : CategoryRegistry::GetAllCategories()) {
+ for (size_t i = 0; i < category_index; i++) {
+ const char* category_group = g_category_groups[i];
+ DCHECK(category_group);
if (base::trace_event::TraceEventETWExport::IsCategoryGroupEnabled(
- category.name())) {
- category.set_state_flag(TraceCategory::ENABLED_FOR_ETW_EXPORT);
+ category_group)) {
+ g_category_group_enabled[i] |= ENABLED_FOR_ETW_EXPORT;
} else {
- category.clear_state_flag(TraceCategory::ENABLED_FOR_ETW_EXPORT);
+ g_category_group_enabled[i] &= ~ENABLED_FOR_ETW_EXPORT;
}
}
}
diff --git a/base/trace_event/trace_log.h b/base/trace_event/trace_log.h
index 88b6e588e4..e4407e81bd 100644
--- a/base/trace_event/trace_log.h
+++ b/base/trace_event/trace_log.h
@@ -26,17 +26,15 @@ namespace base {
template <typename Type>
struct DefaultSingletonTraits;
-class MessageLoop;
class RefCountedString;
namespace trace_event {
-struct TraceCategory;
class TraceBuffer;
class TraceBufferChunk;
class TraceEvent;
-class TraceEventFilter;
class TraceEventMemoryOverhead;
+class TraceSamplingThread;
struct BASE_EXPORT TraceLogStatus {
TraceLogStatus();
@@ -47,14 +45,22 @@ struct BASE_EXPORT TraceLogStatus {
class BASE_EXPORT TraceLog : public MemoryDumpProvider {
public:
- // Argument passed to TraceLog::SetEnabled.
- enum Mode : uint8_t {
- // Enables normal tracing (recording trace events in the trace buffer).
- RECORDING_MODE = 1 << 0,
-
- // Trace events are enabled just for filtering but not for recording. Only
- // event filters config of |trace_config| argument is used.
- FILTERING_MODE = 1 << 1
+ enum Mode {
+ DISABLED = 0,
+ RECORDING_MODE
+ };
+
+ // The pointer returned from GetCategoryGroupEnabledInternal() points to a
+ // value with zero or more of the following bits. Used in this class only.
+ // The TRACE_EVENT macros should only use the value as a bool.
+ // These values must be in sync with macro values in TraceEvent.h in Blink.
+ enum CategoryGroupEnabledFlags {
+ // Category group enabled for the recording mode.
+ ENABLED_FOR_RECORDING = 1 << 0,
+ // Category group enabled by SetEventCallbackEnabled().
+ ENABLED_FOR_EVENT_CALLBACK = 1 << 2,
+ // Category group enabled to export events to ETW.
+ ENABLED_FOR_ETW_EXPORT = 1 << 3
};
static TraceLog* GetInstance();
@@ -70,30 +76,16 @@ class BASE_EXPORT TraceLog : public MemoryDumpProvider {
// if the current thread supports that (has a message loop).
void InitializeThreadLocalEventBufferIfSupported();
- // See TraceConfig comments for details on how to control which categories
- // will be traced. SetDisabled must be called distinctly for each mode that is
- // enabled. If tracing has already been enabled for recording, category filter
- // (enabled and disabled categories) will be merged into the current category
- // filter. Enabling RECORDING_MODE does not enable filters. Trace event
- // filters will be used only if FILTERING_MODE is set on |modes_to_enable|.
- // Conversely to RECORDING_MODE, FILTERING_MODE doesn't support upgrading,
- // i.e. filters can only be enabled if not previously enabled.
- void SetEnabled(const TraceConfig& trace_config, uint8_t modes_to_enable);
-
- // TODO(ssid): Remove the default SetEnabled and IsEnabled. They should take
- // Mode as argument.
-
- // Disables tracing for all categories for the specified |modes_to_disable|
- // only. Only RECORDING_MODE is taken as default |modes_to_disable|.
- void SetDisabled();
- void SetDisabled(uint8_t modes_to_disable);
+ // Enables normal tracing (recording trace events in the trace buffer).
+ // See TraceConfig comments for details on how to control what categories
+ // will be traced. If tracing has already been enabled, |category_filter| will
+ // be merged into the current category filter.
+ void SetEnabled(const TraceConfig& trace_config, Mode mode);
- // Returns true if TraceLog is enabled on recording mode.
- // Note: Returns false even if FILTERING_MODE is enabled.
- bool IsEnabled() { return enabled_modes_ & RECORDING_MODE; }
+ // Disables normal tracing for all categories.
+ void SetDisabled();
- // Returns a bitmap of enabled modes from TraceLog::Mode.
- uint8_t enabled_modes() { return enabled_modes_; }
+ bool IsEnabled() { return mode_ != DISABLED; }
// The number of times we have begun recording traces. If tracing is off,
// returns -1. If tracing is on, then it returns the number of times we have
@@ -156,6 +148,31 @@ class BASE_EXPORT TraceLog : public MemoryDumpProvider {
// objects.
void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead);
+ // Not using base::Callback because of its limited by 7 parameters.
+ // Also, using primitive type allows directly passing callback from WebCore.
+ // WARNING: It is possible for the previously set callback to be called
+ // after a call to SetEventCallbackEnabled() that replaces or a call to
+ // SetEventCallbackDisabled() that disables the callback.
+ // This callback may be invoked on any thread.
+ // For TRACE_EVENT_PHASE_COMPLETE events, the client will still receive pairs
+ // of TRACE_EVENT_PHASE_BEGIN and TRACE_EVENT_PHASE_END events to keep the
+ // interface simple.
+ typedef void (*EventCallback)(TimeTicks timestamp,
+ char phase,
+ const unsigned char* category_group_enabled,
+ const char* name,
+ const char* scope,
+ unsigned long long id,
+ int num_args,
+ const char* const arg_names[],
+ const unsigned char arg_types[],
+ const unsigned long long arg_values[],
+ unsigned int flags);
+
+ // Enable tracing for EventCallback.
+ void SetEventCallbackEnabled(const TraceConfig& trace_config,
+ EventCallback cb);
+ void SetEventCallbackDisabled();
void SetArgumentFilterPredicate(
const ArgumentFilterPredicate& argument_filter_predicate);
@@ -269,9 +286,14 @@ class BASE_EXPORT TraceLog : public MemoryDumpProvider {
const char* name,
TraceEventHandle handle);
- void EndFilteredEvent(const unsigned char* category_group_enabled,
- const char* name,
- TraceEventHandle handle);
+ // For every matching event, the callback will be called.
+ typedef base::Callback<void()> WatchEventCallback;
+ void SetWatchEvent(const std::string& category_name,
+ const std::string& event_name,
+ const WatchEventCallback& callback);
+ // Cancel the watch event. If tracing is enabled, this may race with the
+ // watch event notification firing.
+ void CancelWatchEvent();
int process_id() const { return process_id_; }
@@ -279,12 +301,7 @@ class BASE_EXPORT TraceLog : public MemoryDumpProvider {
// Exposed for unittesting:
- // Testing factory for TraceEventFilter.
- typedef std::unique_ptr<TraceEventFilter> (*FilterFactoryForTesting)(
- const std::string& /* predicate_name */);
- void SetFilterFactoryForTesting(FilterFactoryForTesting factory) {
- filter_factory_for_testing_ = factory;
- }
+ void WaitSamplingEventForTesting();
// Allows deleting our singleton instance.
static void DeleteForTesting();
@@ -299,9 +316,8 @@ class BASE_EXPORT TraceLog : public MemoryDumpProvider {
// on their sort index, ascending, then by their name, and then tid.
void SetProcessSortIndex(int sort_index);
- // Sets the name of the process. |process_name| should be a string literal
- // since it is a whitelisted argument for background field trials.
- void SetProcessName(const char* process_name);
+ // Sets the name of the process.
+ void SetProcessName(const std::string& process_name);
// Processes can have labels in addition to their names. Use labels, for
// instance, to list out the web page titles that a process is handling.
@@ -355,14 +371,12 @@ class BASE_EXPORT TraceLog : public MemoryDumpProvider {
ProcessMemoryDump* pmd) override;
// Enable/disable each category group based on the current mode_,
- // category_filter_ and event_filters_enabled_.
- // Enable the category group in the recording mode if category_filter_ matches
- // the category group, is not null. Enable category for filtering if any
- // filter in event_filters_enabled_ enables it.
- void UpdateCategoryRegistry();
- void UpdateCategoryState(TraceCategory* category);
-
- void CreateFiltersForTraceConfig();
+ // category_filter_, event_callback_ and event_callback_category_filter_.
+ // Enable the category group in the enabled mode if category_filter_ matches
+ // the category group, or event_callback_ is not null and
+ // event_callback_category_filter_ matches the category group.
+ void UpdateCategoryGroupEnabledFlags();
+ void UpdateCategoryGroupEnabledFlag(size_t category_index);
// Configure synthetic delays based on the values set in the current
// trace config.
@@ -377,6 +391,7 @@ class BASE_EXPORT TraceLog : public MemoryDumpProvider {
TraceLog();
~TraceLog() override;
+ const unsigned char* GetCategoryGroupEnabledInternal(const char* name);
void AddMetadataEventsWhileLocked();
InternalTraceOptions trace_options() const {
@@ -394,7 +409,7 @@ class BASE_EXPORT TraceLog : public MemoryDumpProvider {
TraceEvent* AddEventToThreadSharedChunkWhileLocked(TraceEventHandle* handle,
bool check_buffer_is_full);
void CheckIfBufferIsFullWhileLocked();
- void SetDisabledWhileLocked(uint8_t modes);
+ void SetDisabledWhileLocked();
TraceEvent* GetEventByHandleInternal(TraceEventHandle handle,
OptionalAutoLock* lock);
@@ -433,6 +448,7 @@ class BASE_EXPORT TraceLog : public MemoryDumpProvider {
static const InternalTraceOptions kInternalRecordUntilFull;
static const InternalTraceOptions kInternalRecordContinuously;
static const InternalTraceOptions kInternalEchoToConsole;
+ static const InternalTraceOptions kInternalEnableSampling;
static const InternalTraceOptions kInternalRecordAsMuchAsPossible;
static const InternalTraceOptions kInternalEnableArgumentFilter;
@@ -442,10 +458,11 @@ class BASE_EXPORT TraceLog : public MemoryDumpProvider {
// This lock protects accesses to thread_names_, thread_event_start_times_
// and thread_colors_.
Lock thread_info_lock_;
- uint8_t enabled_modes_; // See TraceLog::Mode.
+ Mode mode_;
int num_traces_recorded_;
std::unique_ptr<TraceBuffer> logged_events_;
std::vector<std::unique_ptr<TraceEvent>> metadata_events_;
+ subtle::AtomicWord /* EventCallback */ event_callback_;
bool dispatching_to_observer_list_;
std::vector<EnabledStateObserver*> enabled_state_observer_list_;
std::map<AsyncEnabledStateObserver*, RegisteredAsyncObserver>
@@ -470,10 +487,19 @@ class BASE_EXPORT TraceLog : public MemoryDumpProvider {
TimeDelta time_offset_;
+ // Allow tests to wake up when certain events occur.
+ WatchEventCallback watch_event_callback_;
+ subtle::AtomicWord /* const unsigned char* */ watch_category_;
+ std::string watch_event_name_;
+
subtle::AtomicWord /* Options */ trace_options_;
+ // Sampling thread handles.
+ std::unique_ptr<TraceSamplingThread> sampling_thread_;
+ PlatformThreadHandle sampling_thread_handle_;
+
TraceConfig trace_config_;
- TraceConfig::EventFilters enabled_event_filters_;
+ TraceConfig event_callback_trace_config_;
ThreadLocalPointer<ThreadLocalEventBuffer> thread_local_event_buffer_;
ThreadLocalBoolean thread_blocks_message_loop_;
@@ -496,8 +522,6 @@ class BASE_EXPORT TraceLog : public MemoryDumpProvider {
subtle::AtomicWord generation_;
bool use_worker_thread_;
- FilterFactoryForTesting filter_factory_for_testing_;
-
DISALLOW_COPY_AND_ASSIGN(TraceLog);
};
diff --git a/base/trace_event/trace_log_constants.cc b/base/trace_event/trace_log_constants.cc
index 65dca2e4d6..cd2ff0dad3 100644
--- a/base/trace_event/trace_log_constants.cc
+++ b/base/trace_event/trace_log_constants.cc
@@ -14,7 +14,8 @@ const TraceLog::InternalTraceOptions
TraceLog::kInternalRecordUntilFull = 1 << 0;
const TraceLog::InternalTraceOptions
TraceLog::kInternalRecordContinuously = 1 << 1;
-// 1 << 2 is reserved for the DEPRECATED kInternalEnableSampling. DO NOT USE.
+const TraceLog::InternalTraceOptions
+ TraceLog::kInternalEnableSampling = 1 << 2;
const TraceLog::InternalTraceOptions
TraceLog::kInternalEchoToConsole = 1 << 3;
const TraceLog::InternalTraceOptions
diff --git a/base/trace_event/trace_sampling_thread.cc b/base/trace_event/trace_sampling_thread.cc
new file mode 100644
index 0000000000..5a0d2f8a02
--- /dev/null
+++ b/base/trace_event/trace_sampling_thread.cc
@@ -0,0 +1,107 @@
+// Copyright 2015 The Chromium 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 <stddef.h>
+
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_impl.h"
+#include "base/trace_event/trace_log.h"
+#include "base/trace_event/trace_sampling_thread.h"
+
+namespace base {
+namespace trace_event {
+
+class TraceBucketData {
+ public:
+ TraceBucketData(base::subtle::AtomicWord* bucket,
+ const char* name,
+ TraceSampleCallback callback);
+ ~TraceBucketData();
+
+ TRACE_EVENT_API_ATOMIC_WORD* bucket;
+ const char* bucket_name;
+ TraceSampleCallback callback;
+};
+
+TraceSamplingThread::TraceSamplingThread()
+ : thread_running_(false),
+ waitable_event_for_testing_(WaitableEvent::ResetPolicy::AUTOMATIC,
+ WaitableEvent::InitialState::NOT_SIGNALED) {}
+
+TraceSamplingThread::~TraceSamplingThread() {}
+
+void TraceSamplingThread::ThreadMain() {
+ PlatformThread::SetName("Sampling Thread");
+ thread_running_ = true;
+ const int kSamplingFrequencyMicroseconds = 1000;
+ while (!cancellation_flag_.IsSet()) {
+ PlatformThread::Sleep(
+ TimeDelta::FromMicroseconds(kSamplingFrequencyMicroseconds));
+ GetSamples();
+ waitable_event_for_testing_.Signal();
+ }
+}
+
+// static
+void TraceSamplingThread::DefaultSamplingCallback(
+ TraceBucketData* bucket_data) {
+ TRACE_EVENT_API_ATOMIC_WORD category_and_name =
+ TRACE_EVENT_API_ATOMIC_LOAD(*bucket_data->bucket);
+ if (!category_and_name)
+ return;
+ const char* const combined =
+ reinterpret_cast<const char* const>(category_and_name);
+ const char* category_group;
+ const char* name;
+ ExtractCategoryAndName(combined, &category_group, &name);
+ TRACE_EVENT_API_ADD_TRACE_EVENT(
+ TRACE_EVENT_PHASE_SAMPLE,
+ TraceLog::GetCategoryGroupEnabled(category_group), name,
+ trace_event_internal::kGlobalScope, trace_event_internal::kNoId, 0,
+ NULL, NULL, NULL, NULL, 0);
+}
+
+void TraceSamplingThread::GetSamples() {
+ for (size_t i = 0; i < sample_buckets_.size(); ++i) {
+ TraceBucketData* bucket_data = &sample_buckets_[i];
+ bucket_data->callback.Run(bucket_data);
+ }
+}
+
+void TraceSamplingThread::RegisterSampleBucket(
+ TRACE_EVENT_API_ATOMIC_WORD* bucket,
+ const char* const name,
+ TraceSampleCallback callback) {
+ // Access to sample_buckets_ doesn't cause races with the sampling thread
+ // that uses the sample_buckets_, because it is guaranteed that
+ // RegisterSampleBucket is called before the sampling thread is created.
+ DCHECK(!thread_running_);
+ sample_buckets_.push_back(TraceBucketData(bucket, name, callback));
+}
+
+// static
+void TraceSamplingThread::ExtractCategoryAndName(const char* combined,
+ const char** category,
+ const char** name) {
+ *category = combined;
+ *name = &combined[strlen(combined) + 1];
+}
+
+void TraceSamplingThread::Stop() {
+ cancellation_flag_.Set();
+}
+
+void TraceSamplingThread::WaitSamplingEventForTesting() {
+ waitable_event_for_testing_.Wait();
+}
+
+TraceBucketData::TraceBucketData(base::subtle::AtomicWord* bucket,
+ const char* name,
+ TraceSampleCallback callback)
+ : bucket(bucket), bucket_name(name), callback(callback) {}
+
+TraceBucketData::~TraceBucketData() {}
+
+} // namespace trace_event
+} // namespace base
diff --git a/base/trace_event/trace_sampling_thread.h b/base/trace_event/trace_sampling_thread.h
new file mode 100644
index 0000000000..f976a80e07
--- /dev/null
+++ b/base/trace_event/trace_sampling_thread.h
@@ -0,0 +1,54 @@
+// Copyright 2015 The Chromium 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_TRACE_EVENT_TRACE_SAMPLING_THREAD_H_
+#define BASE_TRACE_EVENT_TRACE_SAMPLING_THREAD_H_
+
+#include "base/synchronization/cancellation_flag.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/trace_event/trace_event.h"
+
+namespace base {
+namespace trace_event {
+
+class TraceBucketData;
+typedef base::Callback<void(TraceBucketData*)> TraceSampleCallback;
+
+// This object must be created on the IO thread.
+class TraceSamplingThread : public PlatformThread::Delegate {
+ public:
+ TraceSamplingThread();
+ ~TraceSamplingThread() override;
+
+ // Implementation of PlatformThread::Delegate:
+ void ThreadMain() override;
+
+ static void DefaultSamplingCallback(TraceBucketData* bucket_data);
+
+ void Stop();
+ void WaitSamplingEventForTesting();
+
+ private:
+ friend class TraceLog;
+
+ void GetSamples();
+ // Not thread-safe. Once the ThreadMain has been called, this can no longer
+ // be called.
+ void RegisterSampleBucket(TRACE_EVENT_API_ATOMIC_WORD* bucket,
+ const char* const name,
+ TraceSampleCallback callback);
+ // Splits a combined "category\0name" into the two component parts.
+ static void ExtractCategoryAndName(const char* combined,
+ const char** category,
+ const char** name);
+ std::vector<TraceBucketData> sample_buckets_;
+ bool thread_running_;
+ CancellationFlag cancellation_flag_;
+ WaitableEvent waitable_event_for_testing_;
+};
+
+} // namespace trace_event
+} // namespace base
+
+#endif // BASE_TRACE_EVENT_TRACE_SAMPLING_THREAD_H_
diff --git a/base/tracked_objects.cc b/base/tracked_objects.cc
index 131af14a3a..487fd19098 100644
--- a/base/tracked_objects.cc
+++ b/base/tracked_objects.cc
@@ -4,7 +4,6 @@
#include "base/tracked_objects.h"
-#include <ctype.h>
#include <limits.h>
#include <stdlib.h>
@@ -14,10 +13,8 @@
#include "base/compiler_specific.h"
#include "base/debug/leak_annotations.h"
#include "base/logging.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/numerics/safe_conversions.h"
-#include "base/numerics/safe_math.h"
#include "base/process/process_handle.h"
+#include "base/strings/stringprintf.h"
#include "base/third_party/valgrind/memcheck.h"
#include "base/threading/worker_pool.h"
#include "base/tracking_info.h"
@@ -32,9 +29,6 @@ class TimeDelta;
namespace tracked_objects {
namespace {
-
-constexpr char kWorkerThreadSanitizedName[] = "WorkerThread-*";
-
// When ThreadData is first initialized, should we start in an ACTIVE state to
// record all of the startup-time tasks, or should we start up DEACTIVATED, so
// that we only record after parsing the command line flag --enable-tracking.
@@ -80,22 +74,6 @@ inline bool IsProfilerTimingEnabled() {
return current_timing_enabled == ENABLED_TIMING;
}
-// Sanitize a thread name by replacing trailing sequence of digits with "*".
-// Examples:
-// 1. "BrowserBlockingWorker1/23857" => "BrowserBlockingWorker1/*"
-// 2. "Chrome_IOThread" => "Chrome_IOThread"
-std::string SanitizeThreadName(const std::string& thread_name) {
- size_t i = thread_name.length();
-
- while (i > 0 && isdigit(thread_name[i - 1]))
- --i;
-
- if (i == thread_name.length())
- return thread_name;
-
- return thread_name.substr(0, i) + '*';
-}
-
} // namespace
//------------------------------------------------------------------------------
@@ -108,15 +86,10 @@ DeathData::DeathData()
queue_duration_sum_(0),
run_duration_max_(0),
queue_duration_max_(0),
- alloc_ops_(0),
- free_ops_(0),
- allocated_bytes_(0),
- freed_bytes_(0),
- alloc_overhead_bytes_(0),
- max_allocated_bytes_(0),
run_duration_sample_(0),
queue_duration_sample_(0),
- last_phase_snapshot_(nullptr) {}
+ last_phase_snapshot_(nullptr) {
+}
DeathData::DeathData(const DeathData& other)
: count_(other.count_),
@@ -125,12 +98,6 @@ DeathData::DeathData(const DeathData& other)
queue_duration_sum_(other.queue_duration_sum_),
run_duration_max_(other.run_duration_max_),
queue_duration_max_(other.queue_duration_max_),
- alloc_ops_(other.alloc_ops_),
- free_ops_(other.free_ops_),
- allocated_bytes_(other.allocated_bytes_),
- freed_bytes_(other.freed_bytes_),
- alloc_overhead_bytes_(other.alloc_overhead_bytes_),
- max_allocated_bytes_(other.max_allocated_bytes_),
run_duration_sample_(other.run_duration_sample_),
queue_duration_sample_(other.queue_duration_sample_),
last_phase_snapshot_(nullptr) {
@@ -158,9 +125,9 @@ DeathData::~DeathData() {
#define CONDITIONAL_ASSIGN(assign_it, target, source) \
((target) ^= ((target) ^ (source)) & -static_cast<int32_t>(assign_it))
-void DeathData::RecordDurations(const int32_t queue_duration,
- const int32_t run_duration,
- const uint32_t random_number) {
+void DeathData::RecordDeath(const int32_t queue_duration,
+ const int32_t run_duration,
+ const uint32_t random_number) {
// We'll just clamp at INT_MAX, but we should note this in the UI as such.
if (count_ < INT_MAX)
base::subtle::NoBarrier_Store(&count_, count_ + 1);
@@ -197,28 +164,12 @@ void DeathData::RecordDurations(const int32_t queue_duration,
}
}
-void DeathData::RecordAllocations(const uint32_t alloc_ops,
- const uint32_t free_ops,
- const uint32_t allocated_bytes,
- const uint32_t freed_bytes,
- const uint32_t alloc_overhead_bytes,
- const uint32_t max_allocated_bytes) {
- // Use saturating arithmetic.
- SaturatingMemberAdd(alloc_ops, &alloc_ops_);
- SaturatingMemberAdd(free_ops, &free_ops_);
- SaturatingMemberAdd(allocated_bytes, &allocated_bytes_);
- SaturatingMemberAdd(freed_bytes, &freed_bytes_);
- SaturatingMemberAdd(alloc_overhead_bytes, &alloc_overhead_bytes_);
-
- int32_t max = base::saturated_cast<int32_t>(max_allocated_bytes);
- if (max > max_allocated_bytes_)
- base::subtle::NoBarrier_Store(&max_allocated_bytes_, max);
-}
-
void DeathData::OnProfilingPhaseCompleted(int profiling_phase) {
// Snapshotting and storing current state.
- last_phase_snapshot_ =
- new DeathDataPhaseSnapshot(profiling_phase, *this, last_phase_snapshot_);
+ last_phase_snapshot_ = new DeathDataPhaseSnapshot(
+ profiling_phase, count(), run_duration_sum(), run_duration_max(),
+ run_duration_sample(), queue_duration_sum(), queue_duration_max(),
+ queue_duration_sample(), last_phase_snapshot_);
// Not touching fields for which a delta can be computed by comparing with a
// snapshot from the previous phase. Resetting other fields. Sample values
@@ -250,17 +201,6 @@ void DeathData::OnProfilingPhaseCompleted(int profiling_phase) {
base::subtle::NoBarrier_Store(&queue_duration_max_, 0);
}
-void DeathData::SaturatingMemberAdd(const uint32_t addend,
- base::subtle::Atomic32* sum) {
- // Bail quick if no work or already saturated.
- if (addend == 0U || *sum == INT_MAX)
- return;
-
- base::CheckedNumeric<int32_t> new_sum = *sum;
- new_sum += addend;
- base::subtle::NoBarrier_Store(sum, new_sum.ValueOrDefault(INT_MAX));
-}
-
//------------------------------------------------------------------------------
DeathDataSnapshot::DeathDataSnapshot()
: count(-1),
@@ -269,13 +209,8 @@ DeathDataSnapshot::DeathDataSnapshot()
run_duration_sample(-1),
queue_duration_sum(-1),
queue_duration_max(-1),
- queue_duration_sample(-1),
- alloc_ops(-1),
- free_ops(-1),
- allocated_bytes(-1),
- freed_bytes(-1),
- alloc_overhead_bytes(-1),
- max_allocated_bytes(-1) {}
+ queue_duration_sample(-1) {
+}
DeathDataSnapshot::DeathDataSnapshot(int count,
int32_t run_duration_sum,
@@ -283,58 +218,25 @@ DeathDataSnapshot::DeathDataSnapshot(int count,
int32_t run_duration_sample,
int32_t queue_duration_sum,
int32_t queue_duration_max,
- int32_t queue_duration_sample,
- int32_t alloc_ops,
- int32_t free_ops,
- int32_t allocated_bytes,
- int32_t freed_bytes,
- int32_t alloc_overhead_bytes,
- int32_t max_allocated_bytes)
+ int32_t queue_duration_sample)
: count(count),
run_duration_sum(run_duration_sum),
run_duration_max(run_duration_max),
run_duration_sample(run_duration_sample),
queue_duration_sum(queue_duration_sum),
queue_duration_max(queue_duration_max),
- queue_duration_sample(queue_duration_sample),
- alloc_ops(alloc_ops),
- free_ops(free_ops),
- allocated_bytes(allocated_bytes),
- freed_bytes(freed_bytes),
- alloc_overhead_bytes(alloc_overhead_bytes),
- max_allocated_bytes(max_allocated_bytes) {}
-
-DeathDataSnapshot::DeathDataSnapshot(const DeathData& death_data)
- : count(death_data.count()),
- run_duration_sum(death_data.run_duration_sum()),
- run_duration_max(death_data.run_duration_max()),
- run_duration_sample(death_data.run_duration_sample()),
- queue_duration_sum(death_data.queue_duration_sum()),
- queue_duration_max(death_data.queue_duration_max()),
- queue_duration_sample(death_data.queue_duration_sample()),
- alloc_ops(death_data.alloc_ops()),
- free_ops(death_data.free_ops()),
- allocated_bytes(death_data.allocated_bytes()),
- freed_bytes(death_data.freed_bytes()),
- alloc_overhead_bytes(death_data.alloc_overhead_bytes()),
- max_allocated_bytes(death_data.max_allocated_bytes()) {}
-
-DeathDataSnapshot::DeathDataSnapshot(const DeathDataSnapshot& death_data) =
- default;
+ queue_duration_sample(queue_duration_sample) {}
DeathDataSnapshot::~DeathDataSnapshot() {
}
DeathDataSnapshot DeathDataSnapshot::Delta(
const DeathDataSnapshot& older) const {
- return DeathDataSnapshot(
- count - older.count, run_duration_sum - older.run_duration_sum,
- run_duration_max, run_duration_sample,
- queue_duration_sum - older.queue_duration_sum, queue_duration_max,
- queue_duration_sample, alloc_ops - older.alloc_ops,
- free_ops - older.free_ops, allocated_bytes - older.allocated_bytes,
- freed_bytes - older.freed_bytes,
- alloc_overhead_bytes - older.alloc_overhead_bytes, max_allocated_bytes);
+ return DeathDataSnapshot(count - older.count,
+ run_duration_sum - older.run_duration_sum,
+ run_duration_max, run_duration_sample,
+ queue_duration_sum - older.queue_duration_sum,
+ queue_duration_max, queue_duration_sample);
}
//------------------------------------------------------------------------------
@@ -350,7 +252,8 @@ BirthOnThreadSnapshot::BirthOnThreadSnapshot() {
BirthOnThreadSnapshot::BirthOnThreadSnapshot(const BirthOnThread& birth)
: location(birth.location()),
- sanitized_thread_name(birth.birth_thread()->sanitized_thread_name()) {}
+ thread_name(birth.birth_thread()->thread_name()) {
+}
BirthOnThreadSnapshot::~BirthOnThreadSnapshot() {
}
@@ -382,6 +285,9 @@ ThreadData::NowFunction* ThreadData::now_function_for_testing_ = NULL;
base::ThreadLocalStorage::StaticSlot ThreadData::tls_index_ = TLS_INITIALIZER;
// static
+int ThreadData::worker_thread_data_creation_count_ = 0;
+
+// static
int ThreadData::cleanup_count_ = 0;
// static
@@ -391,7 +297,7 @@ int ThreadData::incarnation_counter_ = 0;
ThreadData* ThreadData::all_thread_data_list_head_ = NULL;
// static
-ThreadData* ThreadData::first_retired_thread_data_ = NULL;
+ThreadData* ThreadData::first_retired_worker_ = NULL;
// static
base::LazyInstance<base::Lock>::Leaky
@@ -400,14 +306,25 @@ base::LazyInstance<base::Lock>::Leaky
// static
base::subtle::Atomic32 ThreadData::status_ = ThreadData::UNINITIALIZED;
-ThreadData::ThreadData(const std::string& sanitized_thread_name)
+ThreadData::ThreadData(const std::string& suggested_name)
: next_(NULL),
- next_retired_thread_data_(NULL),
- sanitized_thread_name_(sanitized_thread_name),
+ next_retired_worker_(NULL),
+ worker_thread_number_(0),
incarnation_count_for_pool_(-1),
current_stopwatch_(NULL) {
- DCHECK(sanitized_thread_name_.empty() ||
- !isdigit(sanitized_thread_name_.back()));
+ DCHECK_GE(suggested_name.size(), 0u);
+ thread_name_ = suggested_name;
+ PushToHeadOfList(); // Which sets real incarnation_count_for_pool_.
+}
+
+ThreadData::ThreadData(int thread_number)
+ : next_(NULL),
+ next_retired_worker_(NULL),
+ worker_thread_number_(thread_number),
+ incarnation_count_for_pool_(-1),
+ current_stopwatch_(NULL) {
+ CHECK_GT(thread_number, 0);
+ base::StringAppendF(&thread_name_, "WorkerThread-%d", thread_number);
PushToHeadOfList(); // Which sets real incarnation_count_for_pool_.
}
@@ -438,17 +355,15 @@ ThreadData* ThreadData::first() {
ThreadData* ThreadData::next() const { return next_; }
// static
-void ThreadData::InitializeThreadContext(const std::string& thread_name) {
+void ThreadData::InitializeThreadContext(const std::string& suggested_name) {
if (base::WorkerPool::RunsTasksOnCurrentThread())
return;
- DCHECK_NE(thread_name, kWorkerThreadSanitizedName);
EnsureTlsInitialization();
ThreadData* current_thread_data =
reinterpret_cast<ThreadData*>(tls_index_.Get());
if (current_thread_data)
return; // Browser tests instigate this.
- current_thread_data =
- GetRetiredOrCreateThreadData(SanitizeThreadName(thread_name));
+ current_thread_data = new ThreadData(suggested_name);
tls_index_.Set(current_thread_data);
}
@@ -461,8 +376,26 @@ ThreadData* ThreadData::Get() {
return registered;
// We must be a worker thread, since we didn't pre-register.
- ThreadData* worker_thread_data =
- GetRetiredOrCreateThreadData(kWorkerThreadSanitizedName);
+ ThreadData* worker_thread_data = NULL;
+ int worker_thread_number = 0;
+ {
+ base::AutoLock lock(*list_lock_.Pointer());
+ if (first_retired_worker_) {
+ worker_thread_data = first_retired_worker_;
+ first_retired_worker_ = first_retired_worker_->next_retired_worker_;
+ worker_thread_data->next_retired_worker_ = NULL;
+ } else {
+ worker_thread_number = ++worker_thread_data_creation_count_;
+ }
+ }
+
+ // If we can't find a previously used instance, then we have to create one.
+ if (!worker_thread_data) {
+ DCHECK_GT(worker_thread_number, 0);
+ worker_thread_data = new ThreadData(worker_thread_number);
+ }
+ DCHECK_GT(worker_thread_data->worker_thread_number_, 0);
+
tls_index_.Set(worker_thread_data);
return worker_thread_data;
}
@@ -476,23 +409,21 @@ void ThreadData::OnThreadTermination(void* thread_data) {
}
void ThreadData::OnThreadTerminationCleanup() {
- // We must NOT do any allocations during this callback. There is a chance that
- // the allocator is no longer active on this thread.
-
// The list_lock_ was created when we registered the callback, so it won't be
// allocated here despite the lazy reference.
base::AutoLock lock(*list_lock_.Pointer());
if (incarnation_counter_ != incarnation_count_for_pool_)
return; // ThreadData was constructed in an earlier unit test.
++cleanup_count_;
-
- // Add this ThreadData to a retired list so that it can be reused by a thread
- // with the same name sanitized name in the future.
- // |next_retired_thread_data_| is expected to be nullptr for a ThreadData
- // associated with an active thread.
- DCHECK(!next_retired_thread_data_);
- next_retired_thread_data_ = first_retired_thread_data_;
- first_retired_thread_data_ = this;
+ // Only worker threads need to be retired and reused.
+ if (!worker_thread_number_) {
+ return;
+ }
+ // We must NOT do any allocations during this callback.
+ // Using the simple linked lists avoids all allocations.
+ DCHECK_EQ(this->next_retired_worker_, reinterpret_cast<ThreadData*>(NULL));
+ this->next_retired_worker_ = first_retired_worker_;
+ first_retired_worker_ = this;
}
// static
@@ -524,8 +455,7 @@ void ThreadData::Snapshot(int current_profiling_phase,
if (birth_count.second > 0) {
current_phase_tasks->push_back(
TaskSnapshot(BirthOnThreadSnapshot(*birth_count.first),
- DeathDataSnapshot(birth_count.second, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0),
+ DeathDataSnapshot(birth_count.second, 0, 0, 0, 0, 0, 0),
"Still_Alive"));
}
}
@@ -584,21 +514,7 @@ void ThreadData::TallyADeath(const Births& births,
base::AutoLock lock(map_lock_); // Lock as the map may get relocated now.
death_data = &death_map_[&births];
} // Release lock ASAP.
- death_data->RecordDurations(queue_duration, run_duration, random_number_);
-
-#if BUILDFLAG(ENABLE_MEMORY_TASK_PROFILER)
- if (stopwatch.heap_tracking_enabled()) {
- base::debug::ThreadHeapUsage heap_usage = stopwatch.heap_usage().usage();
- // Saturate the 64 bit counts on conversion to 32 bit storage.
- death_data->RecordAllocations(
- base::saturated_cast<int32_t>(heap_usage.alloc_ops),
- base::saturated_cast<int32_t>(heap_usage.free_ops),
- base::saturated_cast<int32_t>(heap_usage.alloc_bytes),
- base::saturated_cast<int32_t>(heap_usage.free_bytes),
- base::saturated_cast<int32_t>(heap_usage.alloc_overhead_bytes),
- base::saturated_cast<int32_t>(heap_usage.max_allocated_bytes));
- }
-#endif
+ death_data->RecordDeath(queue_duration, run_duration, random_number_);
}
// static
@@ -719,7 +635,7 @@ void ThreadData::SnapshotExecutedTasks(
if (death_data.count > 0) {
(*phased_snapshots)[phase->profiling_phase].tasks.push_back(
TaskSnapshot(BirthOnThreadSnapshot(*death.first), death_data,
- sanitized_thread_name()));
+ thread_name()));
}
}
}
@@ -737,7 +653,13 @@ void ThreadData::SnapshotMaps(int profiling_phase,
for (const auto& death : death_map_) {
deaths->push_back(std::make_pair(
death.first,
- DeathDataPhaseSnapshot(profiling_phase, death.second,
+ DeathDataPhaseSnapshot(profiling_phase, death.second.count(),
+ death.second.run_duration_sum(),
+ death.second.run_duration_max(),
+ death.second.run_duration_sample(),
+ death.second.queue_duration_sum(),
+ death.second.queue_duration_max(),
+ death.second.queue_duration_sample(),
death.second.last_phase_snapshot())));
}
}
@@ -794,7 +716,6 @@ void ThreadData::InitializeAndSetTrackingStatus(Status status) {
if (status > DEACTIVATED)
status = PROFILING_ACTIVE;
-
base::subtle::Release_Store(&status_, status);
}
@@ -823,9 +744,10 @@ TrackedTime ThreadData::Now() {
}
// static
-void ThreadData::EnsureCleanupWasCalled(int major_threads_shutdown_count) {
- ALLOW_UNUSED_PARAM(major_threads_shutdown_count);
+void ThreadData::EnsureCleanupWasCalled(int /*major_threads_shutdown_count*/) {
base::AutoLock lock(*list_lock_.Pointer());
+ if (worker_thread_data_creation_count_ == 0)
+ return; // We haven't really run much, and couldn't have leaked.
// TODO(jar): until this is working on XP, don't run the real test.
#if 0
@@ -850,14 +772,16 @@ void ThreadData::ShutdownSingleThreadedCleanup(bool leak) {
all_thread_data_list_head_ = NULL;
++incarnation_counter_;
// To be clean, break apart the retired worker list (though we leak them).
- while (first_retired_thread_data_) {
- ThreadData* thread_data = first_retired_thread_data_;
- first_retired_thread_data_ = thread_data->next_retired_thread_data_;
- thread_data->next_retired_thread_data_ = nullptr;
+ while (first_retired_worker_) {
+ ThreadData* worker = first_retired_worker_;
+ CHECK_GT(worker->worker_thread_number_, 0);
+ first_retired_worker_ = worker->next_retired_worker_;
+ worker->next_retired_worker_ = NULL;
}
}
// Put most global static back in pristine shape.
+ worker_thread_data_creation_count_ = 0;
cleanup_count_ = 0;
tls_index_.Set(NULL);
// Almost UNINITIALIZED.
@@ -889,39 +813,6 @@ void ThreadData::ShutdownSingleThreadedCleanup(bool leak) {
}
}
-// static
-ThreadData* ThreadData::GetRetiredOrCreateThreadData(
- const std::string& sanitized_thread_name) {
- SCOPED_UMA_HISTOGRAM_TIMER("TrackedObjects.GetRetiredOrCreateThreadData");
-
- {
- base::AutoLock lock(*list_lock_.Pointer());
- ThreadData** pcursor = &first_retired_thread_data_;
- ThreadData* cursor = first_retired_thread_data_;
-
- // Assuming that there aren't more than a few tens of retired ThreadData
- // instances, this lookup should be quick compared to the thread creation
- // time. Retired ThreadData instances cannot be stored in a map because
- // insertions are done from OnThreadTerminationCleanup() where allocations
- // are not allowed.
- //
- // Note: Test processes may have more than a few tens of retired ThreadData
- // instances.
- while (cursor) {
- if (cursor->sanitized_thread_name() == sanitized_thread_name) {
- DCHECK_EQ(*pcursor, cursor);
- *pcursor = cursor->next_retired_thread_data_;
- cursor->next_retired_thread_data_ = nullptr;
- return cursor;
- }
- pcursor = &cursor->next_retired_thread_data_;
- cursor = cursor->next_retired_thread_data_;
- }
- }
-
- return new ThreadData(sanitized_thread_name);
-}
-
//------------------------------------------------------------------------------
TaskStopwatch::TaskStopwatch()
: wallclock_duration_ms_(0),
@@ -932,10 +823,6 @@ TaskStopwatch::TaskStopwatch()
state_ = CREATED;
child_ = NULL;
#endif
-#if BUILDFLAG(ENABLE_MEMORY_TASK_PROFILER)
- heap_tracking_enabled_ =
- base::debug::ThreadHeapUsageTracker::IsHeapTrackingEnabled();
-#endif
}
TaskStopwatch::~TaskStopwatch() {
@@ -952,10 +839,6 @@ void TaskStopwatch::Start() {
#endif
start_time_ = ThreadData::Now();
-#if BUILDFLAG(ENABLE_MEMORY_TASK_PROFILER)
- if (heap_tracking_enabled_)
- heap_usage_.Start();
-#endif
current_thread_data_ = ThreadData::Get();
if (!current_thread_data_)
@@ -979,10 +862,6 @@ void TaskStopwatch::Stop() {
state_ = STOPPED;
DCHECK(child_ == NULL);
#endif
-#if BUILDFLAG(ENABLE_MEMORY_TASK_PROFILER)
- if (heap_tracking_enabled_)
- heap_usage_.Stop(true);
-#endif
if (!start_time_.is_null() && !end_time.is_null()) {
wallclock_duration_ms_ = (end_time - start_time_).InMilliseconds();
@@ -1034,9 +913,23 @@ ThreadData* TaskStopwatch::GetThreadData() const {
DeathDataPhaseSnapshot::DeathDataPhaseSnapshot(
int profiling_phase,
- const DeathData& death,
+ int count,
+ int32_t run_duration_sum,
+ int32_t run_duration_max,
+ int32_t run_duration_sample,
+ int32_t queue_duration_sum,
+ int32_t queue_duration_max,
+ int32_t queue_duration_sample,
const DeathDataPhaseSnapshot* prev)
- : profiling_phase(profiling_phase), death_data(death), prev(prev) {}
+ : profiling_phase(profiling_phase),
+ death_data(count,
+ run_duration_sum,
+ run_duration_max,
+ run_duration_sample,
+ queue_duration_sum,
+ queue_duration_max,
+ queue_duration_sample),
+ prev(prev) {}
//------------------------------------------------------------------------------
// TaskSnapshot
@@ -1046,10 +939,11 @@ TaskSnapshot::TaskSnapshot() {
TaskSnapshot::TaskSnapshot(const BirthOnThreadSnapshot& birth,
const DeathDataSnapshot& death_data,
- const std::string& death_sanitized_thread_name)
+ const std::string& death_thread_name)
: birth(birth),
death_data(death_data),
- death_sanitized_thread_name(death_sanitized_thread_name) {}
+ death_thread_name(death_thread_name) {
+}
TaskSnapshot::~TaskSnapshot() {
}
diff --git a/base/tracked_objects.h b/base/tracked_objects.h
index 36caec3c6e..7ef0317c39 100644
--- a/base/tracked_objects.h
+++ b/base/tracked_objects.h
@@ -14,12 +14,9 @@
#include <utility>
#include <vector>
-#include "base/allocator/features.h"
#include "base/atomicops.h"
#include "base/base_export.h"
#include "base/containers/hash_tables.h"
-#include "base/debug/debugging_flags.h"
-#include "base/debug/thread_heap_usage_tracker.h"
#include "base/gtest_prod_util.h"
#include "base/lazy_instance.h"
#include "base/location.h"
@@ -62,76 +59,71 @@ struct TrackingInfo;
// with great efficiency (i.e., copying of strings is never needed, and
// comparisons for equality can be based on pointer comparisons).
//
-// Next, a Births instance is constructed or found. A Births instance records
-// (in a base class BirthOnThread) references to the static data provided in a
-// Location instance, as well as a pointer to the ThreadData bound to the thread
-// on which the birth takes place (see discussion on ThreadData below). There is
-// at most one Births instance for each Location / ThreadData pair. The derived
-// Births class contains slots for recording statistics about all instances born
-// at the same location. Statistics currently include only the count of
-// instances constructed.
+// Next, a Births instance is created for use ONLY on the thread where this
+// instance was created. That Births instance records (in a base class
+// BirthOnThread) references to the static data provided in a Location instance,
+// as well as a pointer specifying the thread on which the birth takes place.
+// Hence there is at most one Births instance for each Location on each thread.
+// The derived Births class contains slots for recording statistics about all
+// instances born at the same location. Statistics currently include only the
+// count of instances constructed.
//
// Since the base class BirthOnThread contains only constant data, it can be
-// freely accessed by any thread at any time. The statistics must be handled
-// more carefully; they are updated exclusively by the single thread to which
-// the ThreadData is bound at a given time.
+// freely accessed by any thread at any time (i.e., only the statistic needs to
+// be handled carefully, and stats are updated exclusively on the birth thread).
//
// For Tasks, having now either constructed or found the Births instance
// described above, a pointer to the Births instance is then recorded into the
-// PendingTask structure. This fact alone is very useful in debugging, when
-// there is a question of where an instance came from. In addition, the birth
-// time is also recorded and used to later evaluate the lifetime duration of the
-// whole Task. As a result of the above embedding, we can find out a Task's
-// location of birth, and name of birth thread, without using any locks, as all
-// that data is constant across the life of the process.
+// PendingTask structure in MessageLoop. This fact alone is very useful in
+// debugging, when there is a question of where an instance came from. In
+// addition, the birth time is also recorded and used to later evaluate the
+// lifetime duration of the whole Task. As a result of the above embedding, we
+// can find out a Task's location of birth, and thread of birth, without using
+// any locks, as all that data is constant across the life of the process.
//
// The above work *could* also be done for any other object as well by calling
// TallyABirthIfActive() and TallyRunOnNamedThreadIfTracking() as appropriate.
//
-// The upper bound for the amount of memory used in the above data structures is
-// the product of the number of ThreadData instances and the number of
-// Locations. Fortunately, Locations are often created on a single thread and
-// the memory utilization is actually fairly restrained.
+// The amount of memory used in the above data structures depends on how many
+// threads there are, and how many Locations of construction there are.
+// Fortunately, we don't use memory that is the product of those two counts, but
+// rather we only need one Births instance for each thread that constructs an
+// instance at a Location. In many cases, instances are only created on one
+// thread, so the memory utilization is actually fairly restrained.
//
// Lastly, when an instance is deleted, the final tallies of statistics are
// carefully accumulated. That tallying writes into slots (members) in a
-// collection of DeathData instances. For each Births / death ThreadData pair,
-// there is a DeathData instance to record the additional death count, as well
-// as to accumulate the run-time and queue-time durations for the instance as it
-// is destroyed (dies). Since a ThreadData is bound to at most one thread at a
-// time, there is no need to lock such DeathData instances. (i.e., these
-// accumulated stats in a DeathData instance are exclusively updated by the
-// singular owning thread).
+// collection of DeathData instances. For each birth place Location that is
+// destroyed on a thread, there is a DeathData instance to record the additional
+// death count, as well as accumulate the run-time and queue-time durations for
+// the instance as it is destroyed (dies). By maintaining a single place to
+// aggregate this running sum *only* for the given thread, we avoid the need to
+// lock such DeathData instances. (i.e., these accumulated stats in a DeathData
+// instance are exclusively updated by the singular owning thread).
//
-// With the above life cycle description complete, the major remaining detail is
-// explaining how existing Births and DeathData instances are found to avoid
-// redundant allocations.
+// With the above life cycle description complete, the major remaining detail
+// is explaining how each thread maintains a list of DeathData instances, and
+// of Births instances, and is able to avoid additional (redundant/unnecessary)
+// allocations.
//
-// A ThreadData instance maintains maps of Births and DeathData instances. The
-// Births map is indexed by Location and the DeathData map is indexed by
-// Births*. As noted earlier, we can compare Locations very efficiently as we
-// consider the underlying data (file, function, line) to be atoms, and hence
-// pointer comparison is used rather than (slow) string comparisons.
+// Each thread maintains a list of data items specific to that thread in a
+// ThreadData instance (for that specific thread only). The two critical items
+// are lists of DeathData and Births instances. These lists are maintained in
+// STL maps, which are indexed by Location. As noted earlier, we can compare
+// locations very efficiently as we consider the underlying data (file,
+// function, line) to be atoms, and hence pointer comparison is used rather than
+// (slow) string comparisons.
//
-// The first time that a thread calls ThreadData::InitializeThreadContext() or
-// ThreadData::Get(), a ThreadData instance is bound to it and stored in TLS. If
-// a ThreadData bound to a terminated thread with the same sanitized name (i.e.
-// name without trailing digits) as the current thread is available, it is
-// reused. Otherwise, a new ThreadData instance is instantiated. Since a
-// ThreadData is bound to at most one thread at a time, there is no need to
-// acquire a lock to access its maps. Over time, a ThreadData may be bound to
-// different threads that share the same sanitized name.
-//
-// We maintain a list of all ThreadData instances for the current process. Each
-// ThreadData instance has a pointer to the next one. A static member of
-// ThreadData provides a pointer to the first item on this global list, and
-// access via that all_thread_data_list_head_ item requires the use of the
-// list_lock_.
-//
-// When new ThreadData instances are added to the global list, they are pre-
-// pended, which ensures that any prior acquisition of the list is valid (i.e.,
-// the holder can iterate over it without fear of it changing, or the necessity
-// of using an additional lock. Iterations are actually pretty rare (used
+// To provide a mechanism for iterating over all "known threads," which means
+// threads that have recorded a birth or a death, we create a singly linked list
+// of ThreadData instances. Each such instance maintains a pointer to the next
+// one. A static member of ThreadData provides a pointer to the first item on
+// this global list, and access via that all_thread_data_list_head_ item
+// requires the use of the list_lock_.
+// When new ThreadData instances is added to the global list, it is pre-pended,
+// which ensures that any prior acquisition of the list is valid (i.e., the
+// holder can iterate over it without fear of it changing, or the necessity of
+// using an additional lock. Iterations are actually pretty rare (used
// primarily for cleanup, or snapshotting data for display), so this lock has
// very little global performance impact.
//
@@ -178,13 +170,12 @@ struct TrackingInfo;
// memory reference).
//
// TODO(jar): We can implement a Snapshot system that *tries* to grab the
-// snapshots on the source threads *when* they have SingleThreadTaskRunners
-// available (worker threads don't have SingleThreadTaskRunners, and hence
-// gathering from them will continue to be asynchronous). We had an
-// implementation of this in the past, but the difficulty is dealing with
-// threads being terminated. We can *try* to post a task to threads that have a
-// SingleThreadTaskRunner and check if that succeeds (will fail if the thread
-// has been terminated). This *might* be valuable when we are collecting data
+// snapshots on the source threads *when* they have MessageLoops available
+// (worker threads don't have message loops generally, and hence gathering from
+// them will continue to be asynchronous). We had an implementation of this in
+// the past, but the difficulty is dealing with message loops being terminated.
+// We can *try* to spam the available threads via some task runner to
+// achieve this feat, and it *might* be valuable when we are collecting data
// for upload via UMA (where correctness of data may be more significant than
// for a single screen of about:profiler).
//
@@ -235,7 +226,7 @@ struct BASE_EXPORT BirthOnThreadSnapshot {
~BirthOnThreadSnapshot();
LocationSnapshot location;
- std::string sanitized_thread_name;
+ std::string thread_name;
};
//------------------------------------------------------------------------------
@@ -257,8 +248,6 @@ class BASE_EXPORT Births: public BirthOnThread {
DISALLOW_COPY_AND_ASSIGN(Births);
};
-class DeathData;
-
//------------------------------------------------------------------------------
// A "snapshotted" representation of the DeathData class.
@@ -276,15 +265,7 @@ struct BASE_EXPORT DeathDataSnapshot {
int32_t run_duration_sample,
int32_t queue_duration_sum,
int32_t queue_duration_max,
- int32_t queue_duration_sample,
- int32_t alloc_ops,
- int32_t free_ops,
- int32_t allocated_bytes,
- int32_t freed_bytes,
- int32_t alloc_overhead_bytes,
- int32_t max_allocated_bytes);
- DeathDataSnapshot(const DeathData& death_data);
- DeathDataSnapshot(const DeathDataSnapshot& other);
+ int32_t queue_duration_sample);
~DeathDataSnapshot();
// Calculates and returns the delta between this snapshot and an earlier
@@ -298,13 +279,6 @@ struct BASE_EXPORT DeathDataSnapshot {
int32_t queue_duration_sum;
int32_t queue_duration_max;
int32_t queue_duration_sample;
-
- int32_t alloc_ops;
- int32_t free_ops;
- int32_t allocated_bytes;
- int32_t freed_bytes;
- int32_t alloc_overhead_bytes;
- int32_t max_allocated_bytes;
};
//------------------------------------------------------------------------------
@@ -313,7 +287,13 @@ struct BASE_EXPORT DeathDataSnapshot {
struct DeathDataPhaseSnapshot {
DeathDataPhaseSnapshot(int profiling_phase,
- const DeathData& death_data,
+ int count,
+ int32_t run_duration_sum,
+ int32_t run_duration_max,
+ int32_t run_duration_sample,
+ int32_t queue_duration_sum,
+ int32_t queue_duration_max,
+ int32_t queue_duration_sample,
const DeathDataPhaseSnapshot* prev);
// Profiling phase at which completion this snapshot was taken.
@@ -346,26 +326,9 @@ class BASE_EXPORT DeathData {
// Update stats for a task destruction (death) that had a Run() time of
// |duration|, and has had a queueing delay of |queue_duration|.
- void RecordDurations(const int32_t queue_duration,
- const int32_t run_duration,
- const uint32_t random_number);
-
- // Update stats for a task destruction that performed |alloc_ops|
- // allocations, |free_ops| frees, allocated |allocated_bytes| bytes, freed
- // |freed_bytes|, where an estimated |alloc_overhead_bytes| went to heap
- // overhead, and where at most |max_allocated_bytes| were outstanding at any
- // one time.
- // Note that |alloc_overhead_bytes|/|alloc_ops| yields the average estimated
- // heap overhead of allocations in the task, and |allocated_bytes|/|alloc_ops|
- // yields the average size of allocation.
- // Note also that |allocated_bytes|-|freed_bytes| yields the net heap memory
- // usage of the task, which can be negative.
- void RecordAllocations(const uint32_t alloc_ops,
- const uint32_t free_ops,
- const uint32_t allocated_bytes,
- const uint32_t freed_bytes,
- const uint32_t alloc_overhead_bytes,
- const uint32_t max_allocated_bytes);
+ void RecordDeath(const int32_t queue_duration,
+ const int32_t run_duration,
+ const uint32_t random_number);
// Metrics and past snapshots accessors, used only for serialization and in
// tests.
@@ -388,22 +351,6 @@ class BASE_EXPORT DeathData {
int32_t queue_duration_sample() const {
return base::subtle::NoBarrier_Load(&queue_duration_sample_);
}
- int32_t alloc_ops() const {
- return base::subtle::NoBarrier_Load(&alloc_ops_);
- }
- int32_t free_ops() const { return base::subtle::NoBarrier_Load(&free_ops_); }
- int32_t allocated_bytes() const {
- return base::subtle::NoBarrier_Load(&allocated_bytes_);
- }
- int32_t freed_bytes() const {
- return base::subtle::NoBarrier_Load(&freed_bytes_);
- }
- int32_t alloc_overhead_bytes() const {
- return base::subtle::NoBarrier_Load(&alloc_overhead_bytes_);
- }
- int32_t max_allocated_bytes() const {
- return base::subtle::NoBarrier_Load(&max_allocated_bytes_);
- }
const DeathDataPhaseSnapshot* last_phase_snapshot() const {
return last_phase_snapshot_;
}
@@ -414,12 +361,6 @@ class BASE_EXPORT DeathData {
void OnProfilingPhaseCompleted(int profiling_phase);
private:
- // A saturating addition operation for member variables. This elides the
- // use of atomic-primitive reads for members that are only written on the
- // owning thread.
- static void SaturatingMemberAdd(const uint32_t addend,
- base::subtle::Atomic32* sum);
-
// Members are ordered from most regularly read and updated, to least
// frequently used. This might help a bit with cache lines.
// Number of runs seen (divisor for calculating averages).
@@ -442,24 +383,6 @@ class BASE_EXPORT DeathData {
// snapshot thread.
base::subtle::Atomic32 run_duration_max_;
base::subtle::Atomic32 queue_duration_max_;
-
- // The cumulative number of allocation and free operations.
- base::subtle::Atomic32 alloc_ops_;
- base::subtle::Atomic32 free_ops_;
-
- // The number of bytes allocated by the task.
- base::subtle::Atomic32 allocated_bytes_;
-
- // The number of bytes freed by the task.
- base::subtle::Atomic32 freed_bytes_;
-
- // The cumulative number of overhead bytes. Where available this yields an
- // estimate of the heap overhead for allocations.
- base::subtle::Atomic32 alloc_overhead_bytes_;
-
- // The high-watermark for the number of outstanding heap allocated bytes.
- base::subtle::Atomic32 max_allocated_bytes_;
-
// Samples, used by crowd sourcing gatherers. These are almost never read,
// and rarely updated. They can be modified only on the death thread.
base::subtle::Atomic32 run_duration_sample_;
@@ -484,14 +407,14 @@ struct BASE_EXPORT TaskSnapshot {
TaskSnapshot();
TaskSnapshot(const BirthOnThreadSnapshot& birth,
const DeathDataSnapshot& death_data,
- const std::string& death_sanitized_thread_name);
+ const std::string& death_thread_name);
~TaskSnapshot();
BirthOnThreadSnapshot birth;
// Delta between death data for a thread for a certain profiling phase and the
// snapshot for the pervious phase, if any. Otherwise, just a snapshot.
DeathDataSnapshot death_data;
- std::string death_sanitized_thread_name;
+ std::string death_thread_name;
};
//------------------------------------------------------------------------------
@@ -527,8 +450,9 @@ class BASE_EXPORT ThreadData {
// Initialize the current thread context with a new instance of ThreadData.
// This is used by all threads that have names, and should be explicitly
- // set *before* any births on the threads have taken place.
- static void InitializeThreadContext(const std::string& thread_name);
+ // set *before* any births on the threads have taken place. It is generally
+ // only used by the message loop, which has a well defined thread name.
+ static void InitializeThreadContext(const std::string& suggested_name);
// Using Thread Local Store, find the current instance for collecting data.
// If an instance does not exist, construct one (and remember it for use on
@@ -586,9 +510,7 @@ class BASE_EXPORT ThreadData {
static void TallyRunInAScopedRegionIfTracking(const Births* births,
const TaskStopwatch& stopwatch);
- const std::string& sanitized_thread_name() const {
- return sanitized_thread_name_;
- }
+ const std::string& thread_name() const { return thread_name_; }
// Initializes all statics if needed (this initialization call should be made
// while we are single threaded).
@@ -637,7 +559,12 @@ class BASE_EXPORT ThreadData {
typedef std::vector<std::pair<const Births*, DeathDataPhaseSnapshot>>
DeathsSnapshot;
- explicit ThreadData(const std::string& sanitized_thread_name);
+ // Worker thread construction creates a name since there is none.
+ explicit ThreadData(int thread_number);
+
+ // Message loop based construction should provide a name.
+ explicit ThreadData(const std::string& suggested_name);
+
~ThreadData();
// Push this instance to the head of all_thread_data_list_head_, linking it to
@@ -701,12 +628,6 @@ class BASE_EXPORT ThreadData {
// ThreadData instances.
static void ShutdownSingleThreadedCleanup(bool leak);
- // Returns a ThreadData instance for a thread whose sanitized name is
- // |sanitized_thread_name|. The returned instance may have been extracted from
- // the list of retired ThreadData instances or newly allocated.
- static ThreadData* GetRetiredOrCreateThreadData(
- const std::string& sanitized_thread_name);
-
// When non-null, this specifies an external function that supplies monotone
// increasing time functcion.
static NowFunction* now_function_for_testing_;
@@ -714,16 +635,22 @@ class BASE_EXPORT ThreadData {
// We use thread local store to identify which ThreadData to interact with.
static base::ThreadLocalStorage::StaticSlot tls_index_;
- // Linked list of ThreadData instances that were associated with threads that
- // have been terminated and that have not been associated with a new thread
- // since then. This is only accessed while |list_lock_| is held.
- static ThreadData* first_retired_thread_data_;
+ // List of ThreadData instances for use with worker threads. When a worker
+ // thread is done (terminated), we push it onto this list. When a new worker
+ // thread is created, we first try to re-use a ThreadData instance from the
+ // list, and if none are available, construct a new one.
+ // This is only accessed while list_lock_ is held.
+ static ThreadData* first_retired_worker_;
// Link to the most recently created instance (starts a null terminated list).
// The list is traversed by about:profiler when it needs to snapshot data.
// This is only accessed while list_lock_ is held.
static ThreadData* all_thread_data_list_head_;
+ // The next available worker thread number. This should only be accessed when
+ // the list_lock_ is held.
+ static int worker_thread_data_creation_count_;
+
// The number of times TLS has called us back to cleanup a ThreadData
// instance. This is only accessed while list_lock_ is held.
static int cleanup_count_;
@@ -744,16 +671,23 @@ class BASE_EXPORT ThreadData {
// Link to next instance (null terminated list). Used to globally track all
// registered instances (corresponds to all registered threads where we keep
- // data). Only modified in the constructor.
+ // data).
ThreadData* next_;
- // Pointer to another retired ThreadData instance. This value is nullptr if
- // this is associated with an active thread.
- ThreadData* next_retired_thread_data_;
+ // Pointer to another ThreadData instance for a Worker-Thread that has been
+ // retired (its thread was terminated). This value is non-NULL only for a
+ // retired ThreadData associated with a Worker-Thread.
+ ThreadData* next_retired_worker_;
+
+ // The name of the thread that is being recorded. If this thread has no
+ // message_loop, then this is a worker thread, with a sequence number postfix.
+ std::string thread_name_;
- // The name of the thread that is being recorded, with all trailing digits
- // replaced with a single "*" character.
- const std::string sanitized_thread_name_;
+ // Indicate if this is a worker thread, and the ThreadData contexts should be
+ // stored in the unregistered_thread_data_pool_ when not in use.
+ // Value is zero when it is not a worker thread. Value is a positive integer
+ // corresponding to the created thread name if it is a worker thread.
+ int worker_thread_number_;
// A map used on each thread to keep track of Births on this thread.
// This map should only be accessed on the thread it was constructed on.
@@ -821,13 +755,6 @@ class BASE_EXPORT TaskStopwatch {
// this thread during that period.
int32_t RunDurationMs() const;
-#if BUILDFLAG(ENABLE_MEMORY_TASK_PROFILER)
- const base::debug::ThreadHeapUsageTracker& heap_usage() const {
- return heap_usage_;
- }
- bool heap_tracking_enabled() const { return heap_tracking_enabled_; }
-#endif
-
// Returns tracking info for the current thread.
ThreadData* GetThreadData() const;
@@ -835,11 +762,6 @@ class BASE_EXPORT TaskStopwatch {
// Time when the stopwatch was started.
TrackedTime start_time_;
-#if BUILDFLAG(ENABLE_MEMORY_TASK_PROFILER)
- base::debug::ThreadHeapUsageTracker heap_usage_;
- bool heap_tracking_enabled_;
-#endif
-
// Wallclock duration of the task.
int32_t wallclock_duration_ms_;
diff --git a/base/tracked_objects_unittest.cc b/base/tracked_objects_unittest.cc
index f208e3c981..70d9601cd0 100644
--- a/base/tracked_objects_unittest.cc
+++ b/base/tracked_objects_unittest.cc
@@ -11,27 +11,17 @@
#include <memory>
-#include "base/macros.h"
#include "base/process/process_handle.h"
-#include "base/strings/stringprintf.h"
-#include "base/threading/thread.h"
#include "base/time/time.h"
#include "base/tracking_info.h"
#include "testing/gtest/include/gtest/gtest.h"
const int kLineNumber = 1776;
const char kFile[] = "FixedUnitTestFileName";
-const char kWorkerThreadName[] = "WorkerThread-*";
+const char kWorkerThreadName[] = "WorkerThread-1";
const char kMainThreadName[] = "SomeMainThreadName";
const char kStillAlive[] = "Still_Alive";
-const int32_t kAllocOps = 23;
-const int32_t kFreeOps = 27;
-const int32_t kAllocatedBytes = 59934;
-const int32_t kFreedBytes = 2 * kAllocatedBytes;
-const int32_t kAllocOverheadBytes = kAllocOps * 8;
-const int32_t kMaxAllocatedBytes = kAllocatedBytes / 2;
-
namespace tracked_objects {
class TrackedObjectsTest : public testing::Test {
@@ -95,8 +85,7 @@ class TrackedObjectsTest : public testing::Test {
EXPECT_EQ(kLineNumber,
process_data_phase.tasks[0].birth.location.line_number);
- EXPECT_EQ(birth_thread,
- process_data_phase.tasks[0].birth.sanitized_thread_name);
+ EXPECT_EQ(birth_thread, process_data_phase.tasks[0].birth.thread_name);
EXPECT_EQ(count, process_data_phase.tasks[0].death_data.count);
EXPECT_EQ(count * run_ms,
@@ -111,8 +100,7 @@ class TrackedObjectsTest : public testing::Test {
EXPECT_EQ(queue_ms,
process_data_phase.tasks[0].death_data.queue_duration_sample);
- EXPECT_EQ(death_thread,
- process_data_phase.tasks[0].death_sanitized_thread_name);
+ EXPECT_EQ(death_thread, process_data_phase.tasks[0].death_thread_name);
EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id);
}
@@ -120,16 +108,6 @@ class TrackedObjectsTest : public testing::Test {
// Sets time that will be returned by ThreadData::Now().
static void SetTestTime(unsigned int test_time) { test_time_ = test_time; }
- int GetNumThreadData() {
- int num_thread_data = 0;
- ThreadData* current = ThreadData::first();
- while (current) {
- ++num_thread_data;
- current = current->next();
- }
- return num_thread_data;
- }
-
private:
// Returns test time in milliseconds.
static unsigned int GetTestTime() { return test_time_; }
@@ -245,8 +223,7 @@ TEST_F(TrackedObjectsTest, TinyStartupShutdown) {
process_data_phase.tasks[0].birth.location.function_name);
EXPECT_EQ(kLineNumber,
process_data_phase.tasks[0].birth.location.line_number);
- EXPECT_EQ(kWorkerThreadName,
- process_data_phase.tasks[0].birth.sanitized_thread_name);
+ EXPECT_EQ(kWorkerThreadName, process_data_phase.tasks[0].birth.thread_name);
EXPECT_EQ(1, process_data_phase.tasks[0].death_data.count);
EXPECT_EQ(time_elapsed,
process_data_phase.tasks[0].death_data.run_duration_sum);
@@ -257,11 +234,10 @@ TEST_F(TrackedObjectsTest, TinyStartupShutdown) {
EXPECT_EQ(0, process_data_phase.tasks[0].death_data.queue_duration_sum);
EXPECT_EQ(0, process_data_phase.tasks[0].death_data.queue_duration_max);
EXPECT_EQ(0, process_data_phase.tasks[0].death_data.queue_duration_sample);
- EXPECT_EQ(kWorkerThreadName,
- process_data_phase.tasks[0].death_sanitized_thread_name);
+ EXPECT_EQ(kWorkerThreadName, process_data_phase.tasks[0].death_thread_name);
}
-TEST_F(TrackedObjectsTest, DeathDataTestRecordDurations) {
+TEST_F(TrackedObjectsTest, DeathDataTestRecordDeath) {
ThreadData::InitializeAndSetTrackingStatus(ThreadData::PROFILING_ACTIVE);
std::unique_ptr<DeathData> data(new DeathData());
@@ -279,7 +255,7 @@ TEST_F(TrackedObjectsTest, DeathDataTestRecordDurations) {
int32_t queue_ms = 8;
const int kUnrandomInt = 0; // Fake random int that ensure we sample data.
- data->RecordDurations(queue_ms, run_ms, kUnrandomInt);
+ data->RecordDeath(queue_ms, run_ms, kUnrandomInt);
EXPECT_EQ(data->run_duration_sum(), run_ms);
EXPECT_EQ(data->run_duration_max(), run_ms);
EXPECT_EQ(data->run_duration_sample(), run_ms);
@@ -289,7 +265,7 @@ TEST_F(TrackedObjectsTest, DeathDataTestRecordDurations) {
EXPECT_EQ(data->count(), 1);
EXPECT_EQ(nullptr, data->last_phase_snapshot());
- data->RecordDurations(queue_ms, run_ms, kUnrandomInt);
+ data->RecordDeath(queue_ms, run_ms, kUnrandomInt);
EXPECT_EQ(data->run_duration_sum(), run_ms + run_ms);
EXPECT_EQ(data->run_duration_max(), run_ms);
EXPECT_EQ(data->run_duration_sample(), run_ms);
@@ -300,77 +276,18 @@ TEST_F(TrackedObjectsTest, DeathDataTestRecordDurations) {
EXPECT_EQ(nullptr, data->last_phase_snapshot());
}
-TEST_F(TrackedObjectsTest, DeathDataTestRecordAllocations) {
- ThreadData::InitializeAndSetTrackingStatus(ThreadData::PROFILING_ACTIVE);
-
- std::unique_ptr<DeathData> data(new DeathData());
- ASSERT_NE(data, nullptr);
-
- EXPECT_EQ(data->alloc_ops(), 0);
- EXPECT_EQ(data->free_ops(), 0);
- EXPECT_EQ(data->allocated_bytes(), 0);
- EXPECT_EQ(data->freed_bytes(), 0);
- EXPECT_EQ(data->alloc_overhead_bytes(), 0);
- EXPECT_EQ(data->max_allocated_bytes(), 0);
-
- EXPECT_EQ(nullptr, data->last_phase_snapshot());
-
- data->RecordAllocations(kAllocOps, kFreeOps, kAllocatedBytes, kFreedBytes,
- kAllocOverheadBytes, kMaxAllocatedBytes);
- EXPECT_EQ(data->alloc_ops(), kAllocOps);
- EXPECT_EQ(data->free_ops(), kFreeOps);
- EXPECT_EQ(data->allocated_bytes(), kAllocatedBytes);
- EXPECT_EQ(data->freed_bytes(), kFreedBytes);
- EXPECT_EQ(data->alloc_overhead_bytes(), kAllocOverheadBytes);
- EXPECT_EQ(data->max_allocated_bytes(), kMaxAllocatedBytes);
-
- // Record another batch, with a smaller max.
- const int32_t kSmallerMaxAllocatedBytes = kMaxAllocatedBytes / 2;
- data->RecordAllocations(kAllocOps, kFreeOps, kAllocatedBytes, kFreedBytes,
- kAllocOverheadBytes, kSmallerMaxAllocatedBytes);
- EXPECT_EQ(data->alloc_ops(), 2 * kAllocOps);
- EXPECT_EQ(data->free_ops(), 2 * kFreeOps);
- EXPECT_EQ(data->allocated_bytes(), 2 * kAllocatedBytes);
- EXPECT_EQ(data->freed_bytes(), 2 * kFreedBytes);
- EXPECT_EQ(data->alloc_overhead_bytes(), 2 * kAllocOverheadBytes);
- EXPECT_EQ(data->max_allocated_bytes(), kMaxAllocatedBytes);
-
- // Now with a larger max.
- const int32_t kLargerMaxAllocatedBytes = kMaxAllocatedBytes * 2;
- data->RecordAllocations(kAllocOps, kFreeOps, kAllocatedBytes, kFreedBytes,
- kAllocOverheadBytes, kLargerMaxAllocatedBytes);
- EXPECT_EQ(data->alloc_ops(), 3 * kAllocOps);
- EXPECT_EQ(data->free_ops(), 3 * kFreeOps);
- EXPECT_EQ(data->allocated_bytes(), 3 * kAllocatedBytes);
- EXPECT_EQ(data->freed_bytes(), 3 * kFreedBytes);
- EXPECT_EQ(data->alloc_overhead_bytes(), 3 * kAllocOverheadBytes);
- EXPECT_EQ(data->max_allocated_bytes(), kLargerMaxAllocatedBytes);
-
- // Saturate everything.
- data->RecordAllocations(INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX);
- EXPECT_EQ(data->alloc_ops(), INT_MAX);
- EXPECT_EQ(data->free_ops(), INT_MAX);
- EXPECT_EQ(data->allocated_bytes(), INT_MAX);
- EXPECT_EQ(data->freed_bytes(), INT_MAX);
- EXPECT_EQ(data->alloc_overhead_bytes(), INT_MAX);
- EXPECT_EQ(data->max_allocated_bytes(), INT_MAX);
-}
-
TEST_F(TrackedObjectsTest, DeathDataTest2Phases) {
ThreadData::InitializeAndSetTrackingStatus(ThreadData::PROFILING_ACTIVE);
std::unique_ptr<DeathData> data(new DeathData());
ASSERT_NE(data, nullptr);
- const int32_t run_ms = 42;
- const int32_t queue_ms = 8;
+ int32_t run_ms = 42;
+ int32_t queue_ms = 8;
const int kUnrandomInt = 0; // Fake random int that ensure we sample data.
- data->RecordDurations(queue_ms, run_ms, kUnrandomInt);
- data->RecordDurations(queue_ms, run_ms, kUnrandomInt);
-
- data->RecordAllocations(kAllocOps, kFreeOps, kAllocatedBytes, kFreedBytes,
- kAllocOverheadBytes, kMaxAllocatedBytes);
+ data->RecordDeath(queue_ms, run_ms, kUnrandomInt);
+ data->RecordDeath(queue_ms, run_ms, kUnrandomInt);
data->OnProfilingPhaseCompleted(123);
EXPECT_EQ(data->run_duration_sum(), run_ms + run_ms);
@@ -380,14 +297,6 @@ TEST_F(TrackedObjectsTest, DeathDataTest2Phases) {
EXPECT_EQ(data->queue_duration_max(), 0);
EXPECT_EQ(data->queue_duration_sample(), queue_ms);
EXPECT_EQ(data->count(), 2);
-
- EXPECT_EQ(data->alloc_ops(), kAllocOps);
- EXPECT_EQ(data->free_ops(), kFreeOps);
- EXPECT_EQ(data->allocated_bytes(), kAllocatedBytes);
- EXPECT_EQ(data->freed_bytes(), kFreedBytes);
- EXPECT_EQ(data->alloc_overhead_bytes(), kAllocOverheadBytes);
- EXPECT_EQ(data->max_allocated_bytes(), kMaxAllocatedBytes);
-
ASSERT_NE(nullptr, data->last_phase_snapshot());
EXPECT_EQ(123, data->last_phase_snapshot()->profiling_phase);
EXPECT_EQ(2, data->last_phase_snapshot()->death_data.count);
@@ -402,26 +311,12 @@ TEST_F(TrackedObjectsTest, DeathDataTest2Phases) {
data->last_phase_snapshot()->death_data.queue_duration_max);
EXPECT_EQ(queue_ms,
data->last_phase_snapshot()->death_data.queue_duration_sample);
-
- EXPECT_EQ(kAllocOps, data->last_phase_snapshot()->death_data.alloc_ops);
- EXPECT_EQ(kFreeOps, data->last_phase_snapshot()->death_data.free_ops);
- EXPECT_EQ(kAllocatedBytes,
- data->last_phase_snapshot()->death_data.allocated_bytes);
- EXPECT_EQ(kFreedBytes, data->last_phase_snapshot()->death_data.freed_bytes);
- EXPECT_EQ(kAllocOverheadBytes,
- data->last_phase_snapshot()->death_data.alloc_overhead_bytes);
- EXPECT_EQ(kMaxAllocatedBytes,
- data->last_phase_snapshot()->death_data.max_allocated_bytes);
-
EXPECT_EQ(nullptr, data->last_phase_snapshot()->prev);
- const int32_t run_ms1 = 21;
- const int32_t queue_ms1 = 4;
-
- data->RecordDurations(queue_ms1, run_ms1, kUnrandomInt);
- data->RecordAllocations(kAllocOps, kFreeOps, kAllocatedBytes, kFreedBytes,
- kAllocOverheadBytes, kMaxAllocatedBytes);
+ int32_t run_ms1 = 21;
+ int32_t queue_ms1 = 4;
+ data->RecordDeath(queue_ms1, run_ms1, kUnrandomInt);
EXPECT_EQ(data->run_duration_sum(), run_ms + run_ms + run_ms1);
EXPECT_EQ(data->run_duration_max(), run_ms1);
EXPECT_EQ(data->run_duration_sample(), run_ms1);
@@ -429,14 +324,6 @@ TEST_F(TrackedObjectsTest, DeathDataTest2Phases) {
EXPECT_EQ(data->queue_duration_max(), queue_ms1);
EXPECT_EQ(data->queue_duration_sample(), queue_ms1);
EXPECT_EQ(data->count(), 3);
-
- EXPECT_EQ(data->alloc_ops(), 2 * kAllocOps);
- EXPECT_EQ(data->free_ops(), 2 * kFreeOps);
- EXPECT_EQ(data->allocated_bytes(), 2 * kAllocatedBytes);
- EXPECT_EQ(data->freed_bytes(), 2 * kFreedBytes);
- EXPECT_EQ(data->alloc_overhead_bytes(), 2 * kAllocOverheadBytes);
- EXPECT_EQ(data->max_allocated_bytes(), kMaxAllocatedBytes);
-
ASSERT_NE(nullptr, data->last_phase_snapshot());
EXPECT_EQ(123, data->last_phase_snapshot()->profiling_phase);
EXPECT_EQ(2, data->last_phase_snapshot()->death_data.count);
@@ -451,17 +338,6 @@ TEST_F(TrackedObjectsTest, DeathDataTest2Phases) {
data->last_phase_snapshot()->death_data.queue_duration_max);
EXPECT_EQ(queue_ms,
data->last_phase_snapshot()->death_data.queue_duration_sample);
-
- EXPECT_EQ(kAllocOps, data->last_phase_snapshot()->death_data.alloc_ops);
- EXPECT_EQ(kFreeOps, data->last_phase_snapshot()->death_data.free_ops);
- EXPECT_EQ(kAllocatedBytes,
- data->last_phase_snapshot()->death_data.allocated_bytes);
- EXPECT_EQ(kFreedBytes, data->last_phase_snapshot()->death_data.freed_bytes);
- EXPECT_EQ(kAllocOverheadBytes,
- data->last_phase_snapshot()->death_data.alloc_overhead_bytes);
- EXPECT_EQ(kMaxAllocatedBytes,
- data->last_phase_snapshot()->death_data.max_allocated_bytes);
-
EXPECT_EQ(nullptr, data->last_phase_snapshot()->prev);
}
@@ -477,13 +353,6 @@ TEST_F(TrackedObjectsTest, Delta) {
snapshot.queue_duration_max = 101;
snapshot.queue_duration_sample = 26;
- snapshot.alloc_ops = 95;
- snapshot.free_ops = 90;
- snapshot.allocated_bytes = 10240;
- snapshot.freed_bytes = 4096;
- snapshot.alloc_overhead_bytes = 950;
- snapshot.max_allocated_bytes = 10240;
-
DeathDataSnapshot older_snapshot;
older_snapshot.count = 2;
older_snapshot.run_duration_sum = 95;
@@ -493,13 +362,6 @@ TEST_F(TrackedObjectsTest, Delta) {
older_snapshot.queue_duration_max = 99;
older_snapshot.queue_duration_sample = 21;
- older_snapshot.alloc_ops = 45;
- older_snapshot.free_ops = 40;
- older_snapshot.allocated_bytes = 4096;
- older_snapshot.freed_bytes = 2048;
- older_snapshot.alloc_overhead_bytes = 450;
- older_snapshot.max_allocated_bytes = 10200;
-
const DeathDataSnapshot& delta = snapshot.Delta(older_snapshot);
EXPECT_EQ(8, delta.count);
EXPECT_EQ(5, delta.run_duration_sum);
@@ -508,13 +370,6 @@ TEST_F(TrackedObjectsTest, Delta) {
EXPECT_EQ(10, delta.queue_duration_sum);
EXPECT_EQ(101, delta.queue_duration_max);
EXPECT_EQ(26, delta.queue_duration_sample);
-
- EXPECT_EQ(50, delta.alloc_ops);
- EXPECT_EQ(50, delta.free_ops);
- EXPECT_EQ(6144, delta.allocated_bytes);
- EXPECT_EQ(2048, delta.freed_bytes);
- EXPECT_EQ(500, delta.alloc_overhead_bytes);
- EXPECT_EQ(10240, delta.max_allocated_bytes);
}
TEST_F(TrackedObjectsTest, DeactivatedBirthOnlyToSnapshotWorkerThread) {
@@ -676,8 +531,7 @@ TEST_F(TrackedObjectsTest, TwoPhases) {
EXPECT_EQ(kLineNumber,
process_data_phase0.tasks[0].birth.location.line_number);
- EXPECT_EQ(kMainThreadName,
- process_data_phase0.tasks[0].birth.sanitized_thread_name);
+ EXPECT_EQ(kMainThreadName, process_data_phase0.tasks[0].birth.thread_name);
EXPECT_EQ(1, process_data_phase0.tasks[0].death_data.count);
EXPECT_EQ(2, process_data_phase0.tasks[0].death_data.run_duration_sum);
@@ -687,8 +541,7 @@ TEST_F(TrackedObjectsTest, TwoPhases) {
EXPECT_EQ(4, process_data_phase0.tasks[0].death_data.queue_duration_max);
EXPECT_EQ(4, process_data_phase0.tasks[0].death_data.queue_duration_sample);
- EXPECT_EQ(kMainThreadName,
- process_data_phase0.tasks[0].death_sanitized_thread_name);
+ EXPECT_EQ(kMainThreadName, process_data_phase0.tasks[0].death_thread_name);
auto it1 = process_data.phased_snapshots.find(1);
ASSERT_TRUE(it1 != process_data.phased_snapshots.end());
@@ -702,8 +555,7 @@ TEST_F(TrackedObjectsTest, TwoPhases) {
EXPECT_EQ(kLineNumber,
process_data_phase1.tasks[0].birth.location.line_number);
- EXPECT_EQ(kMainThreadName,
- process_data_phase1.tasks[0].birth.sanitized_thread_name);
+ EXPECT_EQ(kMainThreadName, process_data_phase1.tasks[0].birth.thread_name);
EXPECT_EQ(1, process_data_phase1.tasks[0].death_data.count);
EXPECT_EQ(10, process_data_phase1.tasks[0].death_data.run_duration_sum);
@@ -713,8 +565,7 @@ TEST_F(TrackedObjectsTest, TwoPhases) {
EXPECT_EQ(2, process_data_phase1.tasks[0].death_data.queue_duration_max);
EXPECT_EQ(2, process_data_phase1.tasks[0].death_data.queue_duration_sample);
- EXPECT_EQ(kMainThreadName,
- process_data_phase1.tasks[0].death_sanitized_thread_name);
+ EXPECT_EQ(kMainThreadName, process_data_phase1.tasks[0].death_thread_name);
EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id);
}
@@ -797,8 +648,7 @@ TEST_F(TrackedObjectsTest, ThreePhases) {
EXPECT_EQ(kLineNumber,
process_data_phase0.tasks[0].birth.location.line_number);
- EXPECT_EQ(kMainThreadName,
- process_data_phase0.tasks[0].birth.sanitized_thread_name);
+ EXPECT_EQ(kMainThreadName, process_data_phase0.tasks[0].birth.thread_name);
EXPECT_EQ(1, process_data_phase0.tasks[0].death_data.count);
EXPECT_EQ(6, process_data_phase0.tasks[0].death_data.run_duration_sum);
@@ -808,8 +658,7 @@ TEST_F(TrackedObjectsTest, ThreePhases) {
EXPECT_EQ(7, process_data_phase0.tasks[0].death_data.queue_duration_max);
EXPECT_EQ(7, process_data_phase0.tasks[0].death_data.queue_duration_sample);
- EXPECT_EQ(kMainThreadName,
- process_data_phase0.tasks[0].death_sanitized_thread_name);
+ EXPECT_EQ(kMainThreadName, process_data_phase0.tasks[0].death_thread_name);
auto it1 = process_data.phased_snapshots.find(1);
ASSERT_TRUE(it1 != process_data.phased_snapshots.end());
@@ -823,8 +672,7 @@ TEST_F(TrackedObjectsTest, ThreePhases) {
EXPECT_EQ(kLineNumber,
process_data_phase1.tasks[0].birth.location.line_number);
- EXPECT_EQ(kMainThreadName,
- process_data_phase1.tasks[0].birth.sanitized_thread_name);
+ EXPECT_EQ(kMainThreadName, process_data_phase1.tasks[0].birth.thread_name);
EXPECT_EQ(1, process_data_phase1.tasks[0].death_data.count);
EXPECT_EQ(4, process_data_phase1.tasks[0].death_data.run_duration_sum);
@@ -834,8 +682,7 @@ TEST_F(TrackedObjectsTest, ThreePhases) {
EXPECT_EQ(5, process_data_phase1.tasks[0].death_data.queue_duration_max);
EXPECT_EQ(5, process_data_phase1.tasks[0].death_data.queue_duration_sample);
- EXPECT_EQ(kMainThreadName,
- process_data_phase1.tasks[0].death_sanitized_thread_name);
+ EXPECT_EQ(kMainThreadName, process_data_phase1.tasks[0].death_thread_name);
auto it2 = process_data.phased_snapshots.find(2);
ASSERT_TRUE(it2 != process_data.phased_snapshots.end());
@@ -849,8 +696,7 @@ TEST_F(TrackedObjectsTest, ThreePhases) {
EXPECT_EQ(kLineNumber,
process_data_phase2.tasks[0].birth.location.line_number);
- EXPECT_EQ(kMainThreadName,
- process_data_phase2.tasks[0].birth.sanitized_thread_name);
+ EXPECT_EQ(kMainThreadName, process_data_phase2.tasks[0].birth.thread_name);
EXPECT_EQ(1, process_data_phase2.tasks[0].death_data.count);
EXPECT_EQ(2, process_data_phase2.tasks[0].death_data.run_duration_sum);
@@ -860,8 +706,7 @@ TEST_F(TrackedObjectsTest, ThreePhases) {
EXPECT_EQ(3, process_data_phase2.tasks[0].death_data.queue_duration_max);
EXPECT_EQ(3, process_data_phase2.tasks[0].death_data.queue_duration_sample);
- EXPECT_EQ(kMainThreadName,
- process_data_phase2.tasks[0].death_sanitized_thread_name);
+ EXPECT_EQ(kMainThreadName, process_data_phase2.tasks[0].death_thread_name);
EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id);
}
@@ -908,8 +753,7 @@ TEST_F(TrackedObjectsTest, TwoPhasesSecondEmpty) {
EXPECT_EQ(kLineNumber,
process_data_phase0.tasks[0].birth.location.line_number);
- EXPECT_EQ(kMainThreadName,
- process_data_phase0.tasks[0].birth.sanitized_thread_name);
+ EXPECT_EQ(kMainThreadName, process_data_phase0.tasks[0].birth.thread_name);
EXPECT_EQ(1, process_data_phase0.tasks[0].death_data.count);
EXPECT_EQ(2, process_data_phase0.tasks[0].death_data.run_duration_sum);
@@ -919,8 +763,7 @@ TEST_F(TrackedObjectsTest, TwoPhasesSecondEmpty) {
EXPECT_EQ(4, process_data_phase0.tasks[0].death_data.queue_duration_max);
EXPECT_EQ(4, process_data_phase0.tasks[0].death_data.queue_duration_sample);
- EXPECT_EQ(kMainThreadName,
- process_data_phase0.tasks[0].death_sanitized_thread_name);
+ EXPECT_EQ(kMainThreadName, process_data_phase0.tasks[0].death_thread_name);
auto it1 = process_data.phased_snapshots.find(1);
ASSERT_TRUE(it1 != process_data.phased_snapshots.end());
@@ -973,8 +816,7 @@ TEST_F(TrackedObjectsTest, TwoPhasesFirstEmpty) {
EXPECT_EQ(kLineNumber,
process_data_phase1.tasks[0].birth.location.line_number);
- EXPECT_EQ(kMainThreadName,
- process_data_phase1.tasks[0].birth.sanitized_thread_name);
+ EXPECT_EQ(kMainThreadName, process_data_phase1.tasks[0].birth.thread_name);
EXPECT_EQ(1, process_data_phase1.tasks[0].death_data.count);
EXPECT_EQ(2, process_data_phase1.tasks[0].death_data.run_duration_sum);
@@ -984,8 +826,7 @@ TEST_F(TrackedObjectsTest, TwoPhasesFirstEmpty) {
EXPECT_EQ(4, process_data_phase1.tasks[0].death_data.queue_duration_max);
EXPECT_EQ(4, process_data_phase1.tasks[0].death_data.queue_duration_sample);
- EXPECT_EQ(kMainThreadName,
- process_data_phase1.tasks[0].death_sanitized_thread_name);
+ EXPECT_EQ(kMainThreadName, process_data_phase1.tasks[0].death_thread_name);
EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id);
}
@@ -1152,8 +993,7 @@ TEST_F(TrackedObjectsTest, DifferentLives) {
process_data_phase.tasks[0].birth.location.function_name);
EXPECT_EQ(kLineNumber,
process_data_phase.tasks[0].birth.location.line_number);
- EXPECT_EQ(kMainThreadName,
- process_data_phase.tasks[0].birth.sanitized_thread_name);
+ EXPECT_EQ(kMainThreadName, process_data_phase.tasks[0].birth.thread_name);
EXPECT_EQ(1, process_data_phase.tasks[0].death_data.count);
EXPECT_EQ(2, process_data_phase.tasks[0].death_data.run_duration_sum);
EXPECT_EQ(2, process_data_phase.tasks[0].death_data.run_duration_max);
@@ -1161,15 +1001,13 @@ TEST_F(TrackedObjectsTest, DifferentLives) {
EXPECT_EQ(4, process_data_phase.tasks[0].death_data.queue_duration_sum);
EXPECT_EQ(4, process_data_phase.tasks[0].death_data.queue_duration_max);
EXPECT_EQ(4, process_data_phase.tasks[0].death_data.queue_duration_sample);
- EXPECT_EQ(kMainThreadName,
- process_data_phase.tasks[0].death_sanitized_thread_name);
+ EXPECT_EQ(kMainThreadName, process_data_phase.tasks[0].death_thread_name);
EXPECT_EQ(kFile, process_data_phase.tasks[1].birth.location.file_name);
EXPECT_EQ(kFunction,
process_data_phase.tasks[1].birth.location.function_name);
EXPECT_EQ(kSecondFakeLineNumber,
process_data_phase.tasks[1].birth.location.line_number);
- EXPECT_EQ(kMainThreadName,
- process_data_phase.tasks[1].birth.sanitized_thread_name);
+ EXPECT_EQ(kMainThreadName, process_data_phase.tasks[1].birth.thread_name);
EXPECT_EQ(1, process_data_phase.tasks[1].death_data.count);
EXPECT_EQ(0, process_data_phase.tasks[1].death_data.run_duration_sum);
EXPECT_EQ(0, process_data_phase.tasks[1].death_data.run_duration_max);
@@ -1177,8 +1015,7 @@ TEST_F(TrackedObjectsTest, DifferentLives) {
EXPECT_EQ(0, process_data_phase.tasks[1].death_data.queue_duration_sum);
EXPECT_EQ(0, process_data_phase.tasks[1].death_data.queue_duration_max);
EXPECT_EQ(0, process_data_phase.tasks[1].death_data.queue_duration_sample);
- EXPECT_EQ(kStillAlive,
- process_data_phase.tasks[1].death_sanitized_thread_name);
+ EXPECT_EQ(kStillAlive, process_data_phase.tasks[1].death_thread_name);
EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id);
}
@@ -1321,8 +1158,7 @@ TEST_F(TrackedObjectsTest, TaskWithNestedExclusionWithNestedTask) {
process_data_phase.tasks[t0].birth.location.function_name);
EXPECT_EQ(kLineNumber,
process_data_phase.tasks[t0].birth.location.line_number);
- EXPECT_EQ(kMainThreadName,
- process_data_phase.tasks[t0].birth.sanitized_thread_name);
+ EXPECT_EQ(kMainThreadName, process_data_phase.tasks[t0].birth.thread_name);
EXPECT_EQ(1, process_data_phase.tasks[t0].death_data.count);
EXPECT_EQ(6, process_data_phase.tasks[t0].death_data.run_duration_sum);
EXPECT_EQ(6, process_data_phase.tasks[t0].death_data.run_duration_max);
@@ -1330,15 +1166,13 @@ TEST_F(TrackedObjectsTest, TaskWithNestedExclusionWithNestedTask) {
EXPECT_EQ(4, process_data_phase.tasks[t0].death_data.queue_duration_sum);
EXPECT_EQ(4, process_data_phase.tasks[t0].death_data.queue_duration_max);
EXPECT_EQ(4, process_data_phase.tasks[t0].death_data.queue_duration_sample);
- EXPECT_EQ(kMainThreadName,
- process_data_phase.tasks[t0].death_sanitized_thread_name);
+ EXPECT_EQ(kMainThreadName, process_data_phase.tasks[t0].death_thread_name);
EXPECT_EQ(kFile, process_data_phase.tasks[t1].birth.location.file_name);
EXPECT_EQ(kFunction,
process_data_phase.tasks[t1].birth.location.function_name);
EXPECT_EQ(kSecondFakeLineNumber,
process_data_phase.tasks[t1].birth.location.line_number);
- EXPECT_EQ(kMainThreadName,
- process_data_phase.tasks[t1].birth.sanitized_thread_name);
+ EXPECT_EQ(kMainThreadName, process_data_phase.tasks[t1].birth.thread_name);
EXPECT_EQ(1, process_data_phase.tasks[t1].death_data.count);
EXPECT_EQ(2, process_data_phase.tasks[t1].death_data.run_duration_sum);
EXPECT_EQ(2, process_data_phase.tasks[t1].death_data.run_duration_max);
@@ -1346,30 +1180,8 @@ TEST_F(TrackedObjectsTest, TaskWithNestedExclusionWithNestedTask) {
EXPECT_EQ(1, process_data_phase.tasks[t1].death_data.queue_duration_sum);
EXPECT_EQ(1, process_data_phase.tasks[t1].death_data.queue_duration_max);
EXPECT_EQ(1, process_data_phase.tasks[t1].death_data.queue_duration_sample);
- EXPECT_EQ(kMainThreadName,
- process_data_phase.tasks[t1].death_sanitized_thread_name);
+ EXPECT_EQ(kMainThreadName, process_data_phase.tasks[t1].death_thread_name);
EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id);
}
-// Repetitively create and stop named threads. Verify that the number of
-// instantiated ThreadData instance is equal to the number of different
-// sanitized thread names used in the test.
-TEST_F(TrackedObjectsTest, ReuseRetiredThreadData) {
- const char* const kThreadNames[] = {"Foo%d", "Bar%d", "123Dummy%d",
- "456Dummy%d", "%d"};
- constexpr int kNumIterations = 10;
- EXPECT_EQ(0, GetNumThreadData());
-
- for (int i = 0; i < kNumIterations; ++i) {
- for (const char* thread_name : kThreadNames) {
- base::Thread thread(base::StringPrintf(thread_name, i));
- EXPECT_TRUE(thread.Start());
- }
- }
-
- // Expect one ThreadData instance for each element in |kThreadNames| and one
- // ThreadData instance for the main thread.
- EXPECT_EQ(static_cast<int>(arraysize(kThreadNames) + 1), GetNumThreadData());
-}
-
} // namespace tracked_objects
diff --git a/base/tuple.h b/base/tuple.h
index 34fd789976..e82f2e5f06 100644
--- a/base/tuple.h
+++ b/base/tuple.h
@@ -28,6 +28,7 @@
#include <stddef.h>
#include <tuple>
+#include "base/bind_helpers.h"
#include "build/build_config.h"
namespace base {
@@ -42,6 +43,56 @@ struct IndexSequence {};
template <size_t... Ns>
struct MakeIndexSequenceImpl;
+#if defined(_PREFAST_) && defined(OS_WIN)
+
+// Work around VC++ 2013 /analyze internal compiler error:
+// https://connect.microsoft.com/VisualStudio/feedback/details/1053626
+
+template <> struct MakeIndexSequenceImpl<0> {
+ using Type = IndexSequence<>;
+};
+template <> struct MakeIndexSequenceImpl<1> {
+ using Type = IndexSequence<0>;
+};
+template <> struct MakeIndexSequenceImpl<2> {
+ using Type = IndexSequence<0,1>;
+};
+template <> struct MakeIndexSequenceImpl<3> {
+ using Type = IndexSequence<0,1,2>;
+};
+template <> struct MakeIndexSequenceImpl<4> {
+ using Type = IndexSequence<0,1,2,3>;
+};
+template <> struct MakeIndexSequenceImpl<5> {
+ using Type = IndexSequence<0,1,2,3,4>;
+};
+template <> struct MakeIndexSequenceImpl<6> {
+ using Type = IndexSequence<0,1,2,3,4,5>;
+};
+template <> struct MakeIndexSequenceImpl<7> {
+ using Type = IndexSequence<0,1,2,3,4,5,6>;
+};
+template <> struct MakeIndexSequenceImpl<8> {
+ using Type = IndexSequence<0,1,2,3,4,5,6,7>;
+};
+template <> struct MakeIndexSequenceImpl<9> {
+ using Type = IndexSequence<0,1,2,3,4,5,6,7,8>;
+};
+template <> struct MakeIndexSequenceImpl<10> {
+ using Type = IndexSequence<0,1,2,3,4,5,6,7,8,9>;
+};
+template <> struct MakeIndexSequenceImpl<11> {
+ using Type = IndexSequence<0,1,2,3,4,5,6,7,8,9,10>;
+};
+template <> struct MakeIndexSequenceImpl<12> {
+ using Type = IndexSequence<0,1,2,3,4,5,6,7,8,9,10,11>;
+};
+template <> struct MakeIndexSequenceImpl<13> {
+ using Type = IndexSequence<0,1,2,3,4,5,6,7,8,9,10,11,12>;
+};
+
+#else // defined(OS_WIN) && defined(_PREFAST_)
+
template <size_t... Ns>
struct MakeIndexSequenceImpl<0, Ns...> {
using Type = IndexSequence<Ns...>;
@@ -51,6 +102,8 @@ template <size_t N, size_t... Ns>
struct MakeIndexSequenceImpl<N, Ns...>
: MakeIndexSequenceImpl<N - 1, N - 1, Ns...> {};
+#endif // defined(OS_WIN) && defined(_PREFAST_)
+
// std::get() in <=libstdc++-4.6 returns an lvalue-reference for
// rvalue-reference of a tuple, where an rvalue-reference is expected.
template <size_t I, typename... Ts>
@@ -68,10 +121,6 @@ auto get(T& t) -> decltype(std::get<I>(t)) {
template <size_t N>
using MakeIndexSequence = typename MakeIndexSequenceImpl<N>::Type;
-template <typename T>
-using MakeIndexSequenceForTuple =
- MakeIndexSequence<std::tuple_size<typename std::decay<T>::type>::value>;
-
// Dispatchers ----------------------------------------------------------------
//
// Helper functions that call the given method on an object, with the unpacked
@@ -83,63 +132,62 @@ using MakeIndexSequenceForTuple =
// Non-Static Dispatchers with no out params.
-template <typename ObjT, typename Method, typename Tuple, size_t... Ns>
+template <typename ObjT, typename Method, typename... Ts, size_t... Ns>
inline void DispatchToMethodImpl(const ObjT& obj,
Method method,
- Tuple&& args,
+ const std::tuple<Ts...>& arg,
IndexSequence<Ns...>) {
- (obj->*method)(base::get<Ns>(std::forward<Tuple>(args))...);
+ (obj->*method)(internal::Unwrap(std::get<Ns>(arg))...);
}
-template <typename ObjT, typename Method, typename Tuple>
+template <typename ObjT, typename Method, typename... Ts>
inline void DispatchToMethod(const ObjT& obj,
Method method,
- Tuple&& args) {
- DispatchToMethodImpl(obj, method, std::forward<Tuple>(args),
- MakeIndexSequenceForTuple<Tuple>());
+ const std::tuple<Ts...>& arg) {
+ DispatchToMethodImpl(obj, method, arg, MakeIndexSequence<sizeof...(Ts)>());
}
// Static Dispatchers with no out params.
-template <typename Function, typename Tuple, size_t... Ns>
+template <typename Function, typename... Ts, size_t... Ns>
inline void DispatchToFunctionImpl(Function function,
- Tuple&& args,
+ const std::tuple<Ts...>& arg,
IndexSequence<Ns...>) {
- (*function)(base::get<Ns>(std::forward<Tuple>(args))...);
+ (*function)(internal::Unwrap(std::get<Ns>(arg))...);
}
-template <typename Function, typename Tuple>
-inline void DispatchToFunction(Function function, Tuple&& args) {
- DispatchToFunctionImpl(function, std::forward<Tuple>(args),
- MakeIndexSequenceForTuple<Tuple>());
+template <typename Function, typename... Ts>
+inline void DispatchToFunction(Function function,
+ const std::tuple<Ts...>& arg) {
+ DispatchToFunctionImpl(function, arg, MakeIndexSequence<sizeof...(Ts)>());
}
// Dispatchers with out parameters.
template <typename ObjT,
typename Method,
- typename InTuple,
- typename OutTuple,
+ typename... InTs,
+ typename... OutTs,
size_t... InNs,
size_t... OutNs>
inline void DispatchToMethodImpl(const ObjT& obj,
Method method,
- InTuple&& in,
- OutTuple* out,
+ const std::tuple<InTs...>& in,
+ std::tuple<OutTs...>* out,
IndexSequence<InNs...>,
IndexSequence<OutNs...>) {
- (obj->*method)(base::get<InNs>(std::forward<InTuple>(in))...,
+ (obj->*method)(internal::Unwrap(std::get<InNs>(in))...,
&std::get<OutNs>(*out)...);
}
-template <typename ObjT, typename Method, typename InTuple, typename OutTuple>
+template <typename ObjT, typename Method, typename... InTs, typename... OutTs>
inline void DispatchToMethod(const ObjT& obj,
Method method,
- InTuple&& in,
- OutTuple* out) {
- DispatchToMethodImpl(obj, method, std::forward<InTuple>(in), out,
- MakeIndexSequenceForTuple<InTuple>(),
- MakeIndexSequenceForTuple<OutTuple>());
+ const std::tuple<InTs...>& in,
+ std::tuple<OutTs...>* out) {
+ DispatchToMethodImpl(obj, method, in, out,
+ MakeIndexSequence<sizeof...(InTs)>(),
+ MakeIndexSequence<sizeof...(OutTs)>());
}
} // namespace base
diff --git a/base/values.cc b/base/values.cc
index 5cc0d693bd..d579699079 100644
--- a/base/values.cc
+++ b/base/values.cc
@@ -21,12 +21,6 @@ namespace base {
namespace {
-const char* const kTypeNames[] = {"null", "boolean", "integer", "double",
- "string", "binary", "dictionary", "list"};
-static_assert(arraysize(kTypeNames) ==
- static_cast<size_t>(Value::Type::LIST) + 1,
- "kTypeNames Has Wrong Size");
-
std::unique_ptr<Value> CopyWithoutEmptyChildren(const Value& node);
// Make a deep copy of |node|, but don't include empty lists or dictionaries
@@ -61,10 +55,10 @@ std::unique_ptr<DictionaryValue> CopyDictionaryWithoutEmptyChildren(
std::unique_ptr<Value> CopyWithoutEmptyChildren(const Value& node) {
switch (node.GetType()) {
- case Value::Type::LIST:
+ case Value::TYPE_LIST:
return CopyListWithoutEmptyChildren(static_cast<const ListValue&>(node));
- case Value::Type::DICTIONARY:
+ case Value::TYPE_DICTIONARY:
return CopyDictionaryWithoutEmptyChildren(
static_cast<const DictionaryValue&>(node));
@@ -75,541 +69,266 @@ std::unique_ptr<Value> CopyWithoutEmptyChildren(const Value& node) {
} // namespace
-// static
-std::unique_ptr<Value> Value::CreateNullValue() {
- return WrapUnique(new Value(Type::NONE));
+Value::~Value() {
}
// static
-std::unique_ptr<BinaryValue> BinaryValue::CreateWithCopiedBuffer(
- const char* buffer,
- size_t size) {
- return MakeUnique<BinaryValue>(std::vector<char>(buffer, buffer + size));
-}
-
-Value::Value(const Value& that) {
- InternalCopyConstructFrom(that);
-}
-
-Value::Value(Value&& that) {
- InternalMoveConstructFrom(std::move(that));
-}
-
-Value::Value() : type_(Type::NONE) {}
-
-Value::Value(Type type) : type_(type) {
- // Initialize with the default value.
- switch (type_) {
- case Type::NONE:
- return;
-
- case Type::BOOLEAN:
- bool_value_ = false;
- return;
- case Type::INTEGER:
- int_value_ = 0;
- return;
- case Type::DOUBLE:
- double_value_ = 0.0;
- return;
- case Type::STRING:
- string_value_.Init();
- return;
- case Type::BINARY:
- binary_value_.Init();
- return;
- case Type::DICTIONARY:
- dict_ptr_.Init(MakeUnique<DictStorage>());
- return;
- case Type::LIST:
- list_.Init();
- return;
- }
-}
-
-Value::Value(bool in_bool) : type_(Type::BOOLEAN), bool_value_(in_bool) {}
-
-Value::Value(int in_int) : type_(Type::INTEGER), int_value_(in_int) {}
-
-Value::Value(double in_double) : type_(Type::DOUBLE), double_value_(in_double) {
- if (!std::isfinite(double_value_)) {
- NOTREACHED() << "Non-finite (i.e. NaN or positive/negative infinity) "
- << "values cannot be represented in JSON";
- double_value_ = 0.0;
- }
+std::unique_ptr<Value> Value::CreateNullValue() {
+ return WrapUnique(new Value(TYPE_NULL));
}
-Value::Value(const char* in_string) : type_(Type::STRING) {
- string_value_.Init(in_string);
- DCHECK(IsStringUTF8(*string_value_));
+bool Value::GetAsBinary(const BinaryValue**) const {
+ return false;
}
-Value::Value(const std::string& in_string) : type_(Type::STRING) {
- string_value_.Init(in_string);
- DCHECK(IsStringUTF8(*string_value_));
+bool Value::GetAsBoolean(bool*) const {
+ return false;
}
-Value::Value(std::string&& in_string) : type_(Type::STRING) {
- string_value_.Init(std::move(in_string));
- DCHECK(IsStringUTF8(*string_value_));
+bool Value::GetAsInteger(int*) const {
+ return false;
}
-Value::Value(const char16* in_string) : type_(Type::STRING) {
- string_value_.Init(UTF16ToUTF8(in_string));
+bool Value::GetAsDouble(double*) const {
+ return false;
}
-Value::Value(const string16& in_string) : type_(Type::STRING) {
- string_value_.Init(UTF16ToUTF8(in_string));
+bool Value::GetAsString(std::string*) const {
+ return false;
}
-Value::Value(StringPiece in_string) : Value(in_string.as_string()) {}
-
-Value::Value(const std::vector<char>& in_blob) : type_(Type::BINARY) {
- binary_value_.Init(in_blob);
+bool Value::GetAsString(string16*) const {
+ return false;
}
-Value::Value(std::vector<char>&& in_blob) : type_(Type::BINARY) {
- binary_value_.Init(std::move(in_blob));
+bool Value::GetAsString(const StringValue**) const {
+ return false;
}
-Value& Value::operator=(const Value& that) {
- if (this != &that) {
- if (type_ == that.type_) {
- InternalCopyAssignFromSameType(that);
- } else {
- InternalCleanup();
- InternalCopyConstructFrom(that);
- }
- }
-
- return *this;
+bool Value::GetAsList(ListValue**) {
+ return false;
}
-Value& Value::operator=(Value&& that) {
- if (this != &that) {
- if (type_ == that.type_) {
- InternalMoveAssignFromSameType(std::move(that));
- } else {
- InternalCleanup();
- InternalMoveConstructFrom(std::move(that));
- }
- }
-
- return *this;
+bool Value::GetAsList(const ListValue**) const {
+ return false;
}
-Value::~Value() {
- InternalCleanup();
+bool Value::GetAsDictionary(DictionaryValue**) {
+ return false;
}
-// static
-const char* Value::GetTypeName(Value::Type type) {
- DCHECK_GE(static_cast<int>(type), 0);
- DCHECK_LT(static_cast<size_t>(type), arraysize(kTypeNames));
- return kTypeNames[static_cast<size_t>(type)];
+bool Value::GetAsDictionary(const DictionaryValue**) const {
+ return false;
}
-bool Value::GetBool() const {
- CHECK(is_bool());
- return bool_value_;
+Value* Value::DeepCopy() const {
+ // This method should only be getting called for null Values--all subclasses
+ // need to provide their own implementation;.
+ DCHECK(IsType(TYPE_NULL));
+ return CreateNullValue().release();
}
-int Value::GetInt() const {
- CHECK(is_int());
- return int_value_;
+std::unique_ptr<Value> Value::CreateDeepCopy() const {
+ return WrapUnique(DeepCopy());
}
-double Value::GetDouble() const {
- if (is_double())
- return double_value_;
- if (is_int())
- return int_value_;
- CHECK(false);
- return 0.0;
+bool Value::Equals(const Value* other) const {
+ // This method should only be getting called for null Values--all subclasses
+ // need to provide their own implementation;.
+ DCHECK(IsType(TYPE_NULL));
+ return other->IsType(TYPE_NULL);
}
-const std::string& Value::GetString() const {
- CHECK(is_string());
- return *string_value_;
+// static
+bool Value::Equals(const Value* a, const Value* b) {
+ if ((a == NULL) && (b == NULL)) return true;
+ if ((a == NULL) ^ (b == NULL)) return false;
+ return a->Equals(b);
}
-const std::vector<char>& Value::GetBlob() const {
- CHECK(is_blob());
- return *binary_value_;
-}
+Value::Value(Type type) : type_(type) {}
-size_t Value::GetSize() const {
- return GetBlob().size();
-}
+Value::Value(const Value& that) : type_(that.type_) {}
-const char* Value::GetBuffer() const {
- return GetBlob().data();
+Value& Value::operator=(const Value& that) {
+ type_ = that.type_;
+ return *this;
}
-bool Value::GetAsBoolean(bool* out_value) const {
- if (out_value && is_bool()) {
- *out_value = bool_value_;
- return true;
- }
- return is_bool();
-}
+///////////////////// FundamentalValue ////////////////////
-bool Value::GetAsInteger(int* out_value) const {
- if (out_value && is_int()) {
- *out_value = int_value_;
- return true;
- }
- return is_int();
+FundamentalValue::FundamentalValue(bool in_value)
+ : Value(TYPE_BOOLEAN), boolean_value_(in_value) {
}
-bool Value::GetAsDouble(double* out_value) const {
- if (out_value && is_double()) {
- *out_value = double_value_;
- return true;
- } else if (out_value && is_int()) {
- // Allow promotion from int to double.
- *out_value = int_value_;
- return true;
- }
- return is_double() || is_int();
+FundamentalValue::FundamentalValue(int in_value)
+ : Value(TYPE_INTEGER), integer_value_(in_value) {
}
-bool Value::GetAsString(std::string* out_value) const {
- if (out_value && is_string()) {
- *out_value = *string_value_;
- return true;
+FundamentalValue::FundamentalValue(double in_value)
+ : Value(TYPE_DOUBLE), double_value_(in_value) {
+ if (!std::isfinite(double_value_)) {
+ NOTREACHED() << "Non-finite (i.e. NaN or positive/negative infinity) "
+ << "values cannot be represented in JSON";
+ double_value_ = 0.0;
}
- return is_string();
}
-bool Value::GetAsString(string16* out_value) const {
- if (out_value && is_string()) {
- *out_value = UTF8ToUTF16(*string_value_);
- return true;
- }
- return is_string();
+FundamentalValue::~FundamentalValue() {
}
-bool Value::GetAsString(const Value** out_value) const {
- if (out_value && is_string()) {
- *out_value = static_cast<const Value*>(this);
- return true;
- }
- return is_string();
+bool FundamentalValue::GetAsBoolean(bool* out_value) const {
+ if (out_value && IsType(TYPE_BOOLEAN))
+ *out_value = boolean_value_;
+ return (IsType(TYPE_BOOLEAN));
}
-bool Value::GetAsString(StringPiece* out_value) const {
- if (out_value && is_string()) {
- *out_value = *string_value_;
- return true;
- }
- return is_string();
+bool FundamentalValue::GetAsInteger(int* out_value) const {
+ if (out_value && IsType(TYPE_INTEGER))
+ *out_value = integer_value_;
+ return (IsType(TYPE_INTEGER));
}
-bool Value::GetAsBinary(const BinaryValue** out_value) const {
- if (out_value && is_blob()) {
- *out_value = this;
- return true;
- }
- return is_blob();
+bool FundamentalValue::GetAsDouble(double* out_value) const {
+ if (out_value && IsType(TYPE_DOUBLE))
+ *out_value = double_value_;
+ else if (out_value && IsType(TYPE_INTEGER))
+ *out_value = integer_value_;
+ return (IsType(TYPE_DOUBLE) || IsType(TYPE_INTEGER));
}
-bool Value::GetAsList(ListValue** out_value) {
- if (out_value && is_list()) {
- *out_value = static_cast<ListValue*>(this);
- return true;
- }
- return is_list();
-}
+FundamentalValue* FundamentalValue::DeepCopy() const {
+ switch (GetType()) {
+ case TYPE_BOOLEAN:
+ return new FundamentalValue(boolean_value_);
-bool Value::GetAsList(const ListValue** out_value) const {
- if (out_value && is_list()) {
- *out_value = static_cast<const ListValue*>(this);
- return true;
- }
- return is_list();
-}
+ case TYPE_INTEGER:
+ return new FundamentalValue(integer_value_);
-bool Value::GetAsDictionary(DictionaryValue** out_value) {
- if (out_value && is_dict()) {
- *out_value = static_cast<DictionaryValue*>(this);
- return true;
- }
- return is_dict();
-}
+ case TYPE_DOUBLE:
+ return new FundamentalValue(double_value_);
-bool Value::GetAsDictionary(const DictionaryValue** out_value) const {
- if (out_value && is_dict()) {
- *out_value = static_cast<const DictionaryValue*>(this);
- return true;
+ default:
+ NOTREACHED();
+ return NULL;
}
- return is_dict();
}
-Value* Value::DeepCopy() const {
- // This method should only be getting called for null Values--all subclasses
- // need to provide their own implementation;.
- switch (type()) {
- case Type::NONE:
- return CreateNullValue().release();
-
- case Type::BOOLEAN:
- return new Value(bool_value_);
- case Type::INTEGER:
- return new Value(int_value_);
- case Type::DOUBLE:
- return new Value(double_value_);
- case Type::STRING:
- return new Value(*string_value_);
- // For now, make BinaryValues for backward-compatibility. Convert to
- // Value when that code is deleted.
- case Type::BINARY:
- return new Value(*binary_value_);
-
- // TODO(crbug.com/646113): Clean this up when DictionaryValue and ListValue
- // are completely inlined.
- case Type::DICTIONARY: {
- DictionaryValue* result = new DictionaryValue;
-
- for (const auto& current_entry : **dict_ptr_) {
- result->SetWithoutPathExpansion(current_entry.first,
- current_entry.second->CreateDeepCopy());
- }
+bool FundamentalValue::Equals(const Value* other) const {
+ if (other->GetType() != GetType())
+ return false;
- return result;
+ switch (GetType()) {
+ case TYPE_BOOLEAN: {
+ bool lhs, rhs;
+ return GetAsBoolean(&lhs) && other->GetAsBoolean(&rhs) && lhs == rhs;
}
-
- case Type::LIST: {
- ListValue* result = new ListValue;
-
- for (const auto& entry : *list_)
- result->Append(entry->CreateDeepCopy());
-
- return result;
+ case TYPE_INTEGER: {
+ int lhs, rhs;
+ return GetAsInteger(&lhs) && other->GetAsInteger(&rhs) && lhs == rhs;
+ }
+ case TYPE_DOUBLE: {
+ double lhs, rhs;
+ return GetAsDouble(&lhs) && other->GetAsDouble(&rhs) && lhs == rhs;
}
-
default:
NOTREACHED();
- return nullptr;
+ return false;
}
}
-std::unique_ptr<Value> Value::CreateDeepCopy() const {
- return WrapUnique(DeepCopy());
+///////////////////// StringValue ////////////////////
+
+StringValue::StringValue(const std::string& in_value)
+ : Value(TYPE_STRING),
+ value_(in_value) {
+ DCHECK(IsStringUTF8(in_value));
}
-bool Value::Equals(const Value* other) const {
- if (other->type() != type())
- return false;
+StringValue::StringValue(const string16& in_value)
+ : Value(TYPE_STRING),
+ value_(UTF16ToUTF8(in_value)) {
+}
- switch (type()) {
- case Type::NONE:
- return true;
- case Type::BOOLEAN:
- return bool_value_ == other->bool_value_;
- case Type::INTEGER:
- return int_value_ == other->int_value_;
- case Type::DOUBLE:
- return double_value_ == other->double_value_;
- case Type::STRING:
- return *string_value_ == *(other->string_value_);
- case Type::BINARY:
- return *binary_value_ == *(other->binary_value_);
- // TODO(crbug.com/646113): Clean this up when DictionaryValue and ListValue
- // are completely inlined.
- case Type::DICTIONARY: {
- if ((*dict_ptr_)->size() != (*other->dict_ptr_)->size())
- return false;
-
- return std::equal(std::begin(**dict_ptr_), std::end(**dict_ptr_),
- std::begin(**(other->dict_ptr_)),
- [](const DictStorage::value_type& lhs,
- const DictStorage::value_type& rhs) {
- if (lhs.first != rhs.first)
- return false;
-
- return lhs.second->Equals(rhs.second.get());
- });
- }
- case Type::LIST: {
- if (list_->size() != other->list_->size())
- return false;
-
- return std::equal(std::begin(*list_), std::end(*list_),
- std::begin(*(other->list_)),
- [](const ListStorage::value_type& lhs,
- const ListStorage::value_type& rhs) {
- return lhs->Equals(rhs.get());
- });
- }
- }
+StringValue::~StringValue() {
+}
- NOTREACHED();
- return false;
+std::string* StringValue::GetString() {
+ return &value_;
}
-// static
-bool Value::Equals(const Value* a, const Value* b) {
- if ((a == NULL) && (b == NULL)) return true;
- if ((a == NULL) ^ (b == NULL)) return false;
- return a->Equals(b);
+const std::string& StringValue::GetString() const {
+ return value_;
}
-void Value::InternalCopyFundamentalValue(const Value& that) {
- switch (type_) {
- case Type::NONE:
- // Nothing to do.
- return;
+bool StringValue::GetAsString(std::string* out_value) const {
+ if (out_value)
+ *out_value = value_;
+ return true;
+}
- case Type::BOOLEAN:
- bool_value_ = that.bool_value_;
- return;
- case Type::INTEGER:
- int_value_ = that.int_value_;
- return;
- case Type::DOUBLE:
- double_value_ = that.double_value_;
- return;
+bool StringValue::GetAsString(string16* out_value) const {
+ if (out_value)
+ *out_value = UTF8ToUTF16(value_);
+ return true;
+}
- default:
- NOTREACHED();
- }
+bool StringValue::GetAsString(const StringValue** out_value) const {
+ if (out_value)
+ *out_value = this;
+ return true;
}
-void Value::InternalCopyConstructFrom(const Value& that) {
- type_ = that.type_;
+StringValue* StringValue::DeepCopy() const {
+ return new StringValue(value_);
+}
- switch (type_) {
- case Type::NONE:
- case Type::BOOLEAN:
- case Type::INTEGER:
- case Type::DOUBLE:
- InternalCopyFundamentalValue(that);
- return;
-
- case Type::STRING:
- string_value_.Init(*that.string_value_);
- return;
- case Type::BINARY:
- binary_value_.Init(*that.binary_value_);
- return;
- // DictStorage and ListStorage are move-only types due to the presence of
- // unique_ptrs. This is why the call to |CreateDeepCopy| is necessary here.
- // TODO(crbug.com/646113): Clean this up when DictStorage and ListStorage
- // can be copied directly.
- case Type::DICTIONARY:
- dict_ptr_.Init(std::move(*that.CreateDeepCopy()->dict_ptr_));
- return;
- case Type::LIST:
- list_.Init(std::move(*that.CreateDeepCopy()->list_));
- return;
- }
+bool StringValue::Equals(const Value* other) const {
+ if (other->GetType() != GetType())
+ return false;
+ std::string lhs, rhs;
+ return GetAsString(&lhs) && other->GetAsString(&rhs) && lhs == rhs;
}
-void Value::InternalMoveConstructFrom(Value&& that) {
- type_ = that.type_;
+///////////////////// BinaryValue ////////////////////
- switch (type_) {
- case Type::NONE:
- case Type::BOOLEAN:
- case Type::INTEGER:
- case Type::DOUBLE:
- InternalCopyFundamentalValue(that);
- return;
-
- case Type::STRING:
- string_value_.InitFromMove(std::move(that.string_value_));
- return;
- case Type::BINARY:
- binary_value_.InitFromMove(std::move(that.binary_value_));
- return;
- case Type::DICTIONARY:
- dict_ptr_.InitFromMove(std::move(that.dict_ptr_));
- return;
- case Type::LIST:
- list_.InitFromMove(std::move(that.list_));
- return;
- }
+BinaryValue::BinaryValue()
+ : Value(TYPE_BINARY),
+ size_(0) {
}
-void Value::InternalCopyAssignFromSameType(const Value& that) {
- CHECK_EQ(type_, that.type_);
-
- switch (type_) {
- case Type::NONE:
- case Type::BOOLEAN:
- case Type::INTEGER:
- case Type::DOUBLE:
- InternalCopyFundamentalValue(that);
- return;
-
- case Type::STRING:
- *string_value_ = *that.string_value_;
- return;
- case Type::BINARY:
- *binary_value_ = *that.binary_value_;
- return;
- // DictStorage and ListStorage are move-only types due to the presence of
- // unique_ptrs. This is why the call to |CreateDeepCopy| is necessary here.
- // TODO(crbug.com/646113): Clean this up when DictStorage and ListStorage
- // can be copied directly.
- case Type::DICTIONARY:
- *dict_ptr_ = std::move(*that.CreateDeepCopy()->dict_ptr_);
- return;
- case Type::LIST:
- *list_ = std::move(*that.CreateDeepCopy()->list_);
- return;
- }
+BinaryValue::BinaryValue(std::unique_ptr<char[]> buffer, size_t size)
+ : Value(TYPE_BINARY), buffer_(std::move(buffer)), size_(size) {}
+
+BinaryValue::~BinaryValue() {
}
-void Value::InternalMoveAssignFromSameType(Value&& that) {
- CHECK_EQ(type_, that.type_);
-
- switch (type_) {
- case Type::NONE:
- case Type::BOOLEAN:
- case Type::INTEGER:
- case Type::DOUBLE:
- InternalCopyFundamentalValue(that);
- return;
-
- case Type::STRING:
- *string_value_ = std::move(*that.string_value_);
- return;
- case Type::BINARY:
- *binary_value_ = std::move(*that.binary_value_);
- return;
- case Type::DICTIONARY:
- *dict_ptr_ = std::move(*that.dict_ptr_);
- return;
- case Type::LIST:
- *list_ = std::move(*that.list_);
- return;
- }
+// static
+std::unique_ptr<BinaryValue> BinaryValue::CreateWithCopiedBuffer(
+ const char* buffer,
+ size_t size) {
+ std::unique_ptr<char[]> buffer_copy(new char[size]);
+ memcpy(buffer_copy.get(), buffer, size);
+ return base::MakeUnique<BinaryValue>(std::move(buffer_copy), size);
}
-void Value::InternalCleanup() {
- switch (type_) {
- case Type::NONE:
- case Type::BOOLEAN:
- case Type::INTEGER:
- case Type::DOUBLE:
- // Nothing to do
- return;
-
- case Type::STRING:
- string_value_.Destroy();
- return;
- case Type::BINARY:
- binary_value_.Destroy();
- return;
- case Type::DICTIONARY:
- dict_ptr_.Destroy();
- return;
- case Type::LIST:
- list_.Destroy();
- return;
- }
+bool BinaryValue::GetAsBinary(const BinaryValue** out_value) const {
+ if (out_value)
+ *out_value = this;
+ return true;
+}
+
+BinaryValue* BinaryValue::DeepCopy() const {
+ return CreateWithCopiedBuffer(buffer_.get(), size_).release();
+}
+
+bool BinaryValue::Equals(const Value* other) const {
+ if (other->GetType() != GetType())
+ return false;
+ const BinaryValue* other_binary = static_cast<const BinaryValue*>(other);
+ if (other_binary->size_ != size_)
+ return false;
+ return !memcmp(GetBuffer(), other_binary->GetBuffer(), size_);
}
///////////////////// DictionaryValue ////////////////////
@@ -625,102 +344,122 @@ std::unique_ptr<DictionaryValue> DictionaryValue::From(
return nullptr;
}
-DictionaryValue::DictionaryValue() : Value(Type::DICTIONARY) {}
+DictionaryValue::DictionaryValue()
+ : Value(TYPE_DICTIONARY) {
+}
+
+DictionaryValue::~DictionaryValue() {
+ Clear();
+}
+
+bool DictionaryValue::GetAsDictionary(DictionaryValue** out_value) {
+ if (out_value)
+ *out_value = this;
+ return true;
+}
+
+bool DictionaryValue::GetAsDictionary(const DictionaryValue** out_value) const {
+ if (out_value)
+ *out_value = this;
+ return true;
+}
-bool DictionaryValue::HasKey(StringPiece key) const {
+bool DictionaryValue::HasKey(const std::string& key) const {
DCHECK(IsStringUTF8(key));
- auto current_entry = (*dict_ptr_)->find(key.as_string());
- DCHECK((current_entry == (*dict_ptr_)->end()) || current_entry->second);
- return current_entry != (*dict_ptr_)->end();
+ auto current_entry = dictionary_.find(key);
+ DCHECK((current_entry == dictionary_.end()) || current_entry->second);
+ return current_entry != dictionary_.end();
}
void DictionaryValue::Clear() {
- (*dict_ptr_)->clear();
+ dictionary_.clear();
}
-void DictionaryValue::Set(StringPiece path, std::unique_ptr<Value> in_value) {
+void DictionaryValue::Set(const std::string& path,
+ std::unique_ptr<Value> in_value) {
DCHECK(IsStringUTF8(path));
DCHECK(in_value);
- StringPiece current_path(path);
+ std::string current_path(path);
DictionaryValue* current_dictionary = this;
for (size_t delimiter_position = current_path.find('.');
- delimiter_position != StringPiece::npos;
+ delimiter_position != std::string::npos;
delimiter_position = current_path.find('.')) {
// Assume that we're indexing into a dictionary.
- StringPiece key = current_path.substr(0, delimiter_position);
- DictionaryValue* child_dictionary = nullptr;
+ std::string key(current_path, 0, delimiter_position);
+ DictionaryValue* child_dictionary = NULL;
if (!current_dictionary->GetDictionary(key, &child_dictionary)) {
child_dictionary = new DictionaryValue;
- current_dictionary->SetWithoutPathExpansion(
- key, base::WrapUnique(child_dictionary));
+ current_dictionary->SetWithoutPathExpansion(key, child_dictionary);
}
current_dictionary = child_dictionary;
- current_path = current_path.substr(delimiter_position + 1);
+ current_path.erase(0, delimiter_position + 1);
}
current_dictionary->SetWithoutPathExpansion(current_path,
std::move(in_value));
}
-void DictionaryValue::Set(StringPiece path, Value* in_value) {
+void DictionaryValue::Set(const std::string& path, Value* in_value) {
Set(path, WrapUnique(in_value));
}
-void DictionaryValue::SetBoolean(StringPiece path, bool in_value) {
- Set(path, new Value(in_value));
+void DictionaryValue::SetBoolean(const std::string& path, bool in_value) {
+ Set(path, new FundamentalValue(in_value));
}
-void DictionaryValue::SetInteger(StringPiece path, int in_value) {
- Set(path, new Value(in_value));
+void DictionaryValue::SetInteger(const std::string& path, int in_value) {
+ Set(path, new FundamentalValue(in_value));
}
-void DictionaryValue::SetDouble(StringPiece path, double in_value) {
- Set(path, new Value(in_value));
+void DictionaryValue::SetDouble(const std::string& path, double in_value) {
+ Set(path, new FundamentalValue(in_value));
}
-void DictionaryValue::SetString(StringPiece path, StringPiece in_value) {
- Set(path, new Value(in_value));
+void DictionaryValue::SetString(const std::string& path,
+ const std::string& in_value) {
+ Set(path, new StringValue(in_value));
}
-void DictionaryValue::SetString(StringPiece path, const string16& in_value) {
- Set(path, new Value(in_value));
+void DictionaryValue::SetString(const std::string& path,
+ const string16& in_value) {
+ Set(path, new StringValue(in_value));
}
-void DictionaryValue::SetWithoutPathExpansion(StringPiece key,
+void DictionaryValue::SetWithoutPathExpansion(const std::string& key,
std::unique_ptr<Value> in_value) {
- (**dict_ptr_)[key.as_string()] = std::move(in_value);
+ dictionary_[key] = std::move(in_value);
}
-void DictionaryValue::SetWithoutPathExpansion(StringPiece key,
+void DictionaryValue::SetWithoutPathExpansion(const std::string& key,
Value* in_value) {
SetWithoutPathExpansion(key, WrapUnique(in_value));
}
-void DictionaryValue::SetBooleanWithoutPathExpansion(StringPiece path,
- bool in_value) {
- SetWithoutPathExpansion(path, base::MakeUnique<base::Value>(in_value));
+void DictionaryValue::SetBooleanWithoutPathExpansion(
+ const std::string& path, bool in_value) {
+ SetWithoutPathExpansion(path, new FundamentalValue(in_value));
}
-void DictionaryValue::SetIntegerWithoutPathExpansion(StringPiece path,
- int in_value) {
- SetWithoutPathExpansion(path, base::MakeUnique<base::Value>(in_value));
+void DictionaryValue::SetIntegerWithoutPathExpansion(
+ const std::string& path, int in_value) {
+ SetWithoutPathExpansion(path, new FundamentalValue(in_value));
}
-void DictionaryValue::SetDoubleWithoutPathExpansion(StringPiece path,
- double in_value) {
- SetWithoutPathExpansion(path, base::MakeUnique<base::Value>(in_value));
+void DictionaryValue::SetDoubleWithoutPathExpansion(
+ const std::string& path, double in_value) {
+ SetWithoutPathExpansion(path, new FundamentalValue(in_value));
}
-void DictionaryValue::SetStringWithoutPathExpansion(StringPiece path,
- StringPiece in_value) {
- SetWithoutPathExpansion(path, base::MakeUnique<base::Value>(in_value));
+void DictionaryValue::SetStringWithoutPathExpansion(
+ const std::string& path, const std::string& in_value) {
+ SetWithoutPathExpansion(path, new StringValue(in_value));
}
-void DictionaryValue::SetStringWithoutPathExpansion(StringPiece path,
- const string16& in_value) {
- SetWithoutPathExpansion(path, base::MakeUnique<base::Value>(in_value));
+void DictionaryValue::SetStringWithoutPathExpansion(
+ const std::string& path, const string16& in_value) {
+ SetWithoutPathExpansion(path, new StringValue(in_value));
}
bool DictionaryValue::Get(StringPiece path,
@@ -733,7 +472,8 @@ bool DictionaryValue::Get(StringPiece path,
delimiter_position = current_path.find('.')) {
const DictionaryValue* child_dictionary = NULL;
if (!current_dictionary->GetDictionaryWithoutPathExpansion(
- current_path.substr(0, delimiter_position), &child_dictionary)) {
+ current_path.substr(0, delimiter_position).as_string(),
+ &child_dictionary)) {
return false;
}
@@ -741,7 +481,8 @@ bool DictionaryValue::Get(StringPiece path,
current_path = current_path.substr(delimiter_position + 1);
}
- return current_dictionary->GetWithoutPathExpansion(current_path, out_value);
+ return current_dictionary->GetWithoutPathExpansion(current_path.as_string(),
+ out_value);
}
bool DictionaryValue::Get(StringPiece path, Value** out_value) {
@@ -750,7 +491,8 @@ bool DictionaryValue::Get(StringPiece path, Value** out_value) {
const_cast<const Value**>(out_value));
}
-bool DictionaryValue::GetBoolean(StringPiece path, bool* bool_value) const {
+bool DictionaryValue::GetBoolean(const std::string& path,
+ bool* bool_value) const {
const Value* value;
if (!Get(path, &value))
return false;
@@ -758,7 +500,8 @@ bool DictionaryValue::GetBoolean(StringPiece path, bool* bool_value) const {
return value->GetAsBoolean(bool_value);
}
-bool DictionaryValue::GetInteger(StringPiece path, int* out_value) const {
+bool DictionaryValue::GetInteger(const std::string& path,
+ int* out_value) const {
const Value* value;
if (!Get(path, &value))
return false;
@@ -766,7 +509,8 @@ bool DictionaryValue::GetInteger(StringPiece path, int* out_value) const {
return value->GetAsInteger(out_value);
}
-bool DictionaryValue::GetDouble(StringPiece path, double* out_value) const {
+bool DictionaryValue::GetDouble(const std::string& path,
+ double* out_value) const {
const Value* value;
if (!Get(path, &value))
return false;
@@ -774,7 +518,7 @@ bool DictionaryValue::GetDouble(StringPiece path, double* out_value) const {
return value->GetAsDouble(out_value);
}
-bool DictionaryValue::GetString(StringPiece path,
+bool DictionaryValue::GetString(const std::string& path,
std::string* out_value) const {
const Value* value;
if (!Get(path, &value))
@@ -783,7 +527,8 @@ bool DictionaryValue::GetString(StringPiece path,
return value->GetAsString(out_value);
}
-bool DictionaryValue::GetString(StringPiece path, string16* out_value) const {
+bool DictionaryValue::GetString(const std::string& path,
+ string16* out_value) const {
const Value* value;
if (!Get(path, &value))
return false;
@@ -791,7 +536,7 @@ bool DictionaryValue::GetString(StringPiece path, string16* out_value) const {
return value->GetAsString(out_value);
}
-bool DictionaryValue::GetStringASCII(StringPiece path,
+bool DictionaryValue::GetStringASCII(const std::string& path,
std::string* out_value) const {
std::string out;
if (!GetString(path, &out))
@@ -806,20 +551,21 @@ bool DictionaryValue::GetStringASCII(StringPiece path,
return true;
}
-bool DictionaryValue::GetBinary(StringPiece path,
+bool DictionaryValue::GetBinary(const std::string& path,
const BinaryValue** out_value) const {
const Value* value;
bool result = Get(path, &value);
- if (!result || !value->IsType(Type::BINARY))
+ if (!result || !value->IsType(TYPE_BINARY))
return false;
if (out_value)
- *out_value = value;
+ *out_value = static_cast<const BinaryValue*>(value);
return true;
}
-bool DictionaryValue::GetBinary(StringPiece path, BinaryValue** out_value) {
+bool DictionaryValue::GetBinary(const std::string& path,
+ BinaryValue** out_value) {
return static_cast<const DictionaryValue&>(*this).GetBinary(
path,
const_cast<const BinaryValue**>(out_value));
@@ -829,7 +575,7 @@ bool DictionaryValue::GetDictionary(StringPiece path,
const DictionaryValue** out_value) const {
const Value* value;
bool result = Get(path, &value);
- if (!result || !value->IsType(Type::DICTIONARY))
+ if (!result || !value->IsType(TYPE_DICTIONARY))
return false;
if (out_value)
@@ -845,11 +591,11 @@ bool DictionaryValue::GetDictionary(StringPiece path,
const_cast<const DictionaryValue**>(out_value));
}
-bool DictionaryValue::GetList(StringPiece path,
+bool DictionaryValue::GetList(const std::string& path,
const ListValue** out_value) const {
const Value* value;
bool result = Get(path, &value);
- if (!result || !value->IsType(Type::LIST))
+ if (!result || !value->IsType(TYPE_LIST))
return false;
if (out_value)
@@ -858,17 +604,17 @@ bool DictionaryValue::GetList(StringPiece path,
return true;
}
-bool DictionaryValue::GetList(StringPiece path, ListValue** out_value) {
+bool DictionaryValue::GetList(const std::string& path, ListValue** out_value) {
return static_cast<const DictionaryValue&>(*this).GetList(
path,
const_cast<const ListValue**>(out_value));
}
-bool DictionaryValue::GetWithoutPathExpansion(StringPiece key,
+bool DictionaryValue::GetWithoutPathExpansion(const std::string& key,
const Value** out_value) const {
DCHECK(IsStringUTF8(key));
- auto entry_iterator = (*dict_ptr_)->find(key.as_string());
- if (entry_iterator == (*dict_ptr_)->end())
+ auto entry_iterator = dictionary_.find(key);
+ if (entry_iterator == dictionary_.end())
return false;
if (out_value)
@@ -876,14 +622,14 @@ bool DictionaryValue::GetWithoutPathExpansion(StringPiece key,
return true;
}
-bool DictionaryValue::GetWithoutPathExpansion(StringPiece key,
+bool DictionaryValue::GetWithoutPathExpansion(const std::string& key,
Value** out_value) {
return static_cast<const DictionaryValue&>(*this).GetWithoutPathExpansion(
key,
const_cast<const Value**>(out_value));
}
-bool DictionaryValue::GetBooleanWithoutPathExpansion(StringPiece key,
+bool DictionaryValue::GetBooleanWithoutPathExpansion(const std::string& key,
bool* out_value) const {
const Value* value;
if (!GetWithoutPathExpansion(key, &value))
@@ -892,7 +638,7 @@ bool DictionaryValue::GetBooleanWithoutPathExpansion(StringPiece key,
return value->GetAsBoolean(out_value);
}
-bool DictionaryValue::GetIntegerWithoutPathExpansion(StringPiece key,
+bool DictionaryValue::GetIntegerWithoutPathExpansion(const std::string& key,
int* out_value) const {
const Value* value;
if (!GetWithoutPathExpansion(key, &value))
@@ -901,7 +647,7 @@ bool DictionaryValue::GetIntegerWithoutPathExpansion(StringPiece key,
return value->GetAsInteger(out_value);
}
-bool DictionaryValue::GetDoubleWithoutPathExpansion(StringPiece key,
+bool DictionaryValue::GetDoubleWithoutPathExpansion(const std::string& key,
double* out_value) const {
const Value* value;
if (!GetWithoutPathExpansion(key, &value))
@@ -911,7 +657,7 @@ bool DictionaryValue::GetDoubleWithoutPathExpansion(StringPiece key,
}
bool DictionaryValue::GetStringWithoutPathExpansion(
- StringPiece key,
+ const std::string& key,
std::string* out_value) const {
const Value* value;
if (!GetWithoutPathExpansion(key, &value))
@@ -920,7 +666,7 @@ bool DictionaryValue::GetStringWithoutPathExpansion(
return value->GetAsString(out_value);
}
-bool DictionaryValue::GetStringWithoutPathExpansion(StringPiece key,
+bool DictionaryValue::GetStringWithoutPathExpansion(const std::string& key,
string16* out_value) const {
const Value* value;
if (!GetWithoutPathExpansion(key, &value))
@@ -930,11 +676,11 @@ bool DictionaryValue::GetStringWithoutPathExpansion(StringPiece key,
}
bool DictionaryValue::GetDictionaryWithoutPathExpansion(
- StringPiece key,
+ const std::string& key,
const DictionaryValue** out_value) const {
const Value* value;
bool result = GetWithoutPathExpansion(key, &value);
- if (!result || !value->IsType(Type::DICTIONARY))
+ if (!result || !value->IsType(TYPE_DICTIONARY))
return false;
if (out_value)
@@ -944,7 +690,7 @@ bool DictionaryValue::GetDictionaryWithoutPathExpansion(
}
bool DictionaryValue::GetDictionaryWithoutPathExpansion(
- StringPiece key,
+ const std::string& key,
DictionaryValue** out_value) {
const DictionaryValue& const_this =
static_cast<const DictionaryValue&>(*this);
@@ -954,11 +700,11 @@ bool DictionaryValue::GetDictionaryWithoutPathExpansion(
}
bool DictionaryValue::GetListWithoutPathExpansion(
- StringPiece key,
+ const std::string& key,
const ListValue** out_value) const {
const Value* value;
bool result = GetWithoutPathExpansion(key, &value);
- if (!result || !value->IsType(Type::LIST))
+ if (!result || !value->IsType(TYPE_LIST))
return false;
if (out_value)
@@ -967,7 +713,7 @@ bool DictionaryValue::GetListWithoutPathExpansion(
return true;
}
-bool DictionaryValue::GetListWithoutPathExpansion(StringPiece key,
+bool DictionaryValue::GetListWithoutPathExpansion(const std::string& key,
ListValue** out_value) {
return
static_cast<const DictionaryValue&>(*this).GetListWithoutPathExpansion(
@@ -975,17 +721,17 @@ bool DictionaryValue::GetListWithoutPathExpansion(StringPiece key,
const_cast<const ListValue**>(out_value));
}
-bool DictionaryValue::Remove(StringPiece path,
+bool DictionaryValue::Remove(const std::string& path,
std::unique_ptr<Value>* out_value) {
DCHECK(IsStringUTF8(path));
- StringPiece current_path(path);
+ std::string current_path(path);
DictionaryValue* current_dictionary = this;
size_t delimiter_position = current_path.rfind('.');
- if (delimiter_position != StringPiece::npos) {
+ if (delimiter_position != std::string::npos) {
if (!GetDictionary(current_path.substr(0, delimiter_position),
&current_dictionary))
return false;
- current_path = current_path.substr(delimiter_position + 1);
+ current_path.erase(0, delimiter_position + 1);
}
return current_dictionary->RemoveWithoutPathExpansion(current_path,
@@ -993,20 +739,20 @@ bool DictionaryValue::Remove(StringPiece path,
}
bool DictionaryValue::RemoveWithoutPathExpansion(
- StringPiece key,
+ const std::string& key,
std::unique_ptr<Value>* out_value) {
DCHECK(IsStringUTF8(key));
- auto entry_iterator = (*dict_ptr_)->find(key.as_string());
- if (entry_iterator == (*dict_ptr_)->end())
+ auto entry_iterator = dictionary_.find(key);
+ if (entry_iterator == dictionary_.end())
return false;
if (out_value)
*out_value = std::move(entry_iterator->second);
- (*dict_ptr_)->erase(entry_iterator);
+ dictionary_.erase(entry_iterator);
return true;
}
-bool DictionaryValue::RemovePath(StringPiece path,
+bool DictionaryValue::RemovePath(const std::string& path,
std::unique_ptr<Value>* out_value) {
bool result = false;
size_t delimiter_position = path.find('.');
@@ -1014,7 +760,7 @@ bool DictionaryValue::RemovePath(StringPiece path,
if (delimiter_position == std::string::npos)
return RemoveWithoutPathExpansion(path, out_value);
- StringPiece subdict_path = path.substr(0, delimiter_position);
+ const std::string subdict_path = path.substr(0, delimiter_position);
DictionaryValue* subdict = NULL;
if (!GetDictionary(subdict_path, &subdict))
return false;
@@ -1036,11 +782,10 @@ std::unique_ptr<DictionaryValue> DictionaryValue::DeepCopyWithoutEmptyChildren()
}
void DictionaryValue::MergeDictionary(const DictionaryValue* dictionary) {
- CHECK(dictionary->is_dict());
for (DictionaryValue::Iterator it(*dictionary); !it.IsAtEnd(); it.Advance()) {
const Value* merge_value = &it.value();
// Check whether we have to merge dictionaries.
- if (merge_value->IsType(Value::Type::DICTIONARY)) {
+ if (merge_value->IsType(Value::TYPE_DICTIONARY)) {
DictionaryValue* sub_dict;
if (GetDictionaryWithoutPathExpansion(it.key(), &sub_dict)) {
sub_dict->MergeDictionary(
@@ -1049,31 +794,59 @@ void DictionaryValue::MergeDictionary(const DictionaryValue* dictionary) {
}
}
// All other cases: Make a copy and hook it up.
- SetWithoutPathExpansion(it.key(),
- base::WrapUnique(merge_value->DeepCopy()));
+ SetWithoutPathExpansion(it.key(), merge_value->DeepCopy());
}
}
void DictionaryValue::Swap(DictionaryValue* other) {
- CHECK(other->is_dict());
- dict_ptr_->swap(*(other->dict_ptr_));
+ dictionary_.swap(other->dictionary_);
}
DictionaryValue::Iterator::Iterator(const DictionaryValue& target)
- : target_(target), it_((*target.dict_ptr_)->begin()) {}
+ : target_(target),
+ it_(target.dictionary_.begin()) {}
DictionaryValue::Iterator::Iterator(const Iterator& other) = default;
DictionaryValue::Iterator::~Iterator() {}
DictionaryValue* DictionaryValue::DeepCopy() const {
- return static_cast<DictionaryValue*>(Value::DeepCopy());
+ DictionaryValue* result = new DictionaryValue;
+
+ for (const auto& current_entry : dictionary_) {
+ result->SetWithoutPathExpansion(current_entry.first,
+ current_entry.second->CreateDeepCopy());
+ }
+
+ return result;
}
std::unique_ptr<DictionaryValue> DictionaryValue::CreateDeepCopy() const {
return WrapUnique(DeepCopy());
}
+bool DictionaryValue::Equals(const Value* other) const {
+ if (other->GetType() != GetType())
+ return false;
+
+ const DictionaryValue* other_dict =
+ static_cast<const DictionaryValue*>(other);
+ Iterator lhs_it(*this);
+ Iterator rhs_it(*other_dict);
+ while (!lhs_it.IsAtEnd() && !rhs_it.IsAtEnd()) {
+ if (lhs_it.key() != rhs_it.key() ||
+ !lhs_it.value().Equals(&rhs_it.value())) {
+ return false;
+ }
+ lhs_it.Advance();
+ rhs_it.Advance();
+ }
+ if (!lhs_it.IsAtEnd() || !rhs_it.IsAtEnd())
+ return false;
+
+ return true;
+}
+
///////////////////// ListValue ////////////////////
// static
@@ -1086,10 +859,15 @@ std::unique_ptr<ListValue> ListValue::From(std::unique_ptr<Value> value) {
return nullptr;
}
-ListValue::ListValue() : Value(Type::LIST) {}
+ListValue::ListValue() : Value(TYPE_LIST) {
+}
+
+ListValue::~ListValue() {
+ Clear();
+}
void ListValue::Clear() {
- list_->clear();
+ list_.clear();
}
bool ListValue::Set(size_t index, Value* in_value) {
@@ -1100,25 +878,25 @@ bool ListValue::Set(size_t index, std::unique_ptr<Value> in_value) {
if (!in_value)
return false;
- if (index >= list_->size()) {
+ if (index >= list_.size()) {
// Pad out any intermediate indexes with null settings
- while (index > list_->size())
+ while (index > list_.size())
Append(CreateNullValue());
Append(std::move(in_value));
} else {
// TODO(dcheng): remove this DCHECK once the raw pointer version is removed?
- DCHECK((*list_)[index] != in_value);
- (*list_)[index] = std::move(in_value);
+ DCHECK(list_[index] != in_value);
+ list_[index] = std::move(in_value);
}
return true;
}
bool ListValue::Get(size_t index, const Value** out_value) const {
- if (index >= list_->size())
+ if (index >= list_.size())
return false;
if (out_value)
- *out_value = (*list_)[index].get();
+ *out_value = list_[index].get();
return true;
}
@@ -1172,11 +950,11 @@ bool ListValue::GetString(size_t index, string16* out_value) const {
bool ListValue::GetBinary(size_t index, const BinaryValue** out_value) const {
const Value* value;
bool result = Get(index, &value);
- if (!result || !value->IsType(Type::BINARY))
+ if (!result || !value->IsType(TYPE_BINARY))
return false;
if (out_value)
- *out_value = value;
+ *out_value = static_cast<const BinaryValue*>(value);
return true;
}
@@ -1191,7 +969,7 @@ bool ListValue::GetDictionary(size_t index,
const DictionaryValue** out_value) const {
const Value* value;
bool result = Get(index, &value);
- if (!result || !value->IsType(Type::DICTIONARY))
+ if (!result || !value->IsType(TYPE_DICTIONARY))
return false;
if (out_value)
@@ -1209,7 +987,7 @@ bool ListValue::GetDictionary(size_t index, DictionaryValue** out_value) {
bool ListValue::GetList(size_t index, const ListValue** out_value) const {
const Value* value;
bool result = Get(index, &value);
- if (!result || !value->IsType(Type::LIST))
+ if (!result || !value->IsType(TYPE_LIST))
return false;
if (out_value)
@@ -1225,21 +1003,21 @@ bool ListValue::GetList(size_t index, ListValue** out_value) {
}
bool ListValue::Remove(size_t index, std::unique_ptr<Value>* out_value) {
- if (index >= list_->size())
+ if (index >= list_.size())
return false;
if (out_value)
- *out_value = std::move((*list_)[index]);
+ *out_value = std::move(list_[index]);
- list_->erase(list_->begin() + index);
+ list_.erase(list_.begin() + index);
return true;
}
bool ListValue::Remove(const Value& value, size_t* index) {
- for (auto it = list_->begin(); it != list_->end(); ++it) {
+ for (auto it = list_.begin(); it != list_.end(); ++it) {
if ((*it)->Equals(&value)) {
- size_t previous_index = it - list_->begin();
- list_->erase(it);
+ size_t previous_index = it - list_.begin();
+ list_.erase(it);
if (index)
*index = previous_index;
@@ -1252,40 +1030,38 @@ bool ListValue::Remove(const Value& value, size_t* index) {
ListValue::iterator ListValue::Erase(iterator iter,
std::unique_ptr<Value>* out_value) {
if (out_value)
- *out_value = std::move(*ListStorage::iterator(iter));
+ *out_value = std::move(*Storage::iterator(iter));
- return list_->erase(iter);
+ return list_.erase(iter);
}
void ListValue::Append(std::unique_ptr<Value> in_value) {
- list_->push_back(std::move(in_value));
+ list_.push_back(std::move(in_value));
}
-#if !defined(OS_LINUX)
void ListValue::Append(Value* in_value) {
DCHECK(in_value);
Append(WrapUnique(in_value));
}
-#endif
void ListValue::AppendBoolean(bool in_value) {
- Append(MakeUnique<Value>(in_value));
+ Append(new FundamentalValue(in_value));
}
void ListValue::AppendInteger(int in_value) {
- Append(MakeUnique<Value>(in_value));
+ Append(new FundamentalValue(in_value));
}
void ListValue::AppendDouble(double in_value) {
- Append(MakeUnique<Value>(in_value));
+ Append(new FundamentalValue(in_value));
}
-void ListValue::AppendString(StringPiece in_value) {
- Append(MakeUnique<Value>(in_value));
+void ListValue::AppendString(const std::string& in_value) {
+ Append(new StringValue(in_value));
}
void ListValue::AppendString(const string16& in_value) {
- Append(MakeUnique<Value>(in_value));
+ Append(new StringValue(in_value));
}
void ListValue::AppendStrings(const std::vector<std::string>& in_values) {
@@ -1302,46 +1078,82 @@ void ListValue::AppendStrings(const std::vector<string16>& in_values) {
}
}
-bool ListValue::AppendIfNotPresent(std::unique_ptr<Value> in_value) {
+bool ListValue::AppendIfNotPresent(Value* in_value) {
DCHECK(in_value);
- for (const auto& entry : *list_) {
- if (entry->Equals(in_value.get())) {
+ for (const auto& entry : list_) {
+ if (entry->Equals(in_value)) {
+ delete in_value;
return false;
}
}
- list_->push_back(std::move(in_value));
+ list_.emplace_back(in_value);
return true;
}
-bool ListValue::Insert(size_t index, std::unique_ptr<Value> in_value) {
+bool ListValue::Insert(size_t index, Value* in_value) {
DCHECK(in_value);
- if (index > list_->size())
+ if (index > list_.size())
return false;
- list_->insert(list_->begin() + index, std::move(in_value));
+ list_.insert(list_.begin() + index, WrapUnique(in_value));
return true;
}
ListValue::const_iterator ListValue::Find(const Value& value) const {
- return std::find_if(list_->begin(), list_->end(),
+ return std::find_if(list_.begin(), list_.end(),
[&value](const std::unique_ptr<Value>& entry) {
return entry->Equals(&value);
});
}
void ListValue::Swap(ListValue* other) {
- CHECK(other->is_list());
- list_->swap(*(other->list_));
+ list_.swap(other->list_);
+}
+
+bool ListValue::GetAsList(ListValue** out_value) {
+ if (out_value)
+ *out_value = this;
+ return true;
+}
+
+bool ListValue::GetAsList(const ListValue** out_value) const {
+ if (out_value)
+ *out_value = this;
+ return true;
}
ListValue* ListValue::DeepCopy() const {
- return static_cast<ListValue*>(Value::DeepCopy());
+ ListValue* result = new ListValue;
+
+ for (const auto& entry : list_)
+ result->Append(entry->CreateDeepCopy());
+
+ return result;
}
std::unique_ptr<ListValue> ListValue::CreateDeepCopy() const {
return WrapUnique(DeepCopy());
}
+bool ListValue::Equals(const Value* other) const {
+ if (other->GetType() != GetType())
+ return false;
+
+ const ListValue* other_list =
+ static_cast<const ListValue*>(other);
+ Storage::const_iterator lhs_it, rhs_it;
+ for (lhs_it = begin(), rhs_it = other_list->begin();
+ lhs_it != end() && rhs_it != other_list->end();
+ ++lhs_it, ++rhs_it) {
+ if (!(*lhs_it)->Equals(rhs_it->get()))
+ return false;
+ }
+ if (lhs_it != end() || rhs_it != other_list->end())
+ return false;
+
+ return true;
+}
+
ValueSerializer::~ValueSerializer() {
}
@@ -1354,11 +1166,4 @@ std::ostream& operator<<(std::ostream& out, const Value& value) {
return out << json;
}
-std::ostream& operator<<(std::ostream& out, const Value::Type& type) {
- if (static_cast<int>(type) < 0 ||
- static_cast<size_t>(type) >= arraysize(kTypeNames))
- return out << "Invalid Type (index = " << static_cast<int>(type) << ")";
- return out << Value::GetTypeName(type);
-}
-
} // namespace base
diff --git a/base/values.h b/base/values.h
index 35f66df904..e3d60891b3 100644
--- a/base/values.h
+++ b/base/values.h
@@ -30,16 +30,17 @@
#include "base/base_export.h"
#include "base/compiler_specific.h"
#include "base/macros.h"
-#include "base/memory/manual_constructor.h"
#include "base/strings/string16.h"
#include "base/strings/string_piece.h"
namespace base {
+class BinaryValue;
class DictionaryValue;
+class FundamentalValue;
class ListValue;
+class StringValue;
class Value;
-using BinaryValue = Value;
// The Value class is the base class for Values. A Value can be instantiated
// via the Create*Value() factory methods, or by directly creating instances of
@@ -48,151 +49,158 @@ using BinaryValue = Value;
// See the file-level comment above for more information.
class BASE_EXPORT Value {
public:
- using DictStorage = std::map<std::string, std::unique_ptr<Value>>;
- using ListStorage = std::vector<std::unique_ptr<Value>>;
-
- enum class Type {
- NONE = 0,
- BOOLEAN,
- INTEGER,
- DOUBLE,
- STRING,
- BINARY,
- DICTIONARY,
- LIST
+ enum Type {
+ TYPE_NULL = 0,
+ TYPE_BOOLEAN,
+ TYPE_INTEGER,
+ TYPE_DOUBLE,
+ TYPE_STRING,
+ TYPE_BINARY,
+ TYPE_DICTIONARY,
+ TYPE_LIST
// Note: Do not add more types. See the file-level comment above for why.
};
- static std::unique_ptr<Value> CreateNullValue();
+ virtual ~Value();
- // For situations where you want to keep ownership of your buffer, this
- // factory method creates a new BinaryValue by copying the contents of the
- // buffer that's passed in.
- // DEPRECATED, use MakeUnique<Value>(const std::vector<char>&) instead.
- // TODO(crbug.com/646113): Delete this and migrate callsites.
- static std::unique_ptr<BinaryValue> CreateWithCopiedBuffer(const char* buffer,
- size_t size);
-
- Value(const Value& that);
- Value(Value&& that);
- Value(); // A null value.
- explicit Value(Type type);
- explicit Value(bool in_bool);
- explicit Value(int in_int);
- explicit Value(double in_double);
-
- // Value(const char*) and Value(const char16*) are required despite
- // Value(const std::string&) and Value(const string16&) because otherwise the
- // compiler will choose the Value(bool) constructor for these arguments.
- // Value(std::string&&) allow for efficient move construction.
- // Value(StringPiece) exists due to many callsites passing StringPieces as
- // arguments.
- explicit Value(const char* in_string);
- explicit Value(const std::string& in_string);
- explicit Value(std::string&& in_string);
- explicit Value(const char16* in_string);
- explicit Value(const string16& in_string);
- explicit Value(StringPiece in_string);
-
- explicit Value(const std::vector<char>& in_blob);
- explicit Value(std::vector<char>&& in_blob);
-
- Value& operator=(const Value& that);
- Value& operator=(Value&& that);
-
- ~Value();
-
- // Returns the name for a given |type|.
- static const char* GetTypeName(Type type);
+ static std::unique_ptr<Value> CreateNullValue();
// Returns the type of the value stored by the current Value object.
// Each type will be implemented by only one subclass of Value, so it's
// safe to use the Type to determine whether you can cast from
// Value* to (Implementing Class)*. Also, a Value object never changes
// its type after construction.
- Type GetType() const { return type_; } // DEPRECATED, use type().
- Type type() const { return type_; }
+ Type GetType() const { return type_; }
// Returns true if the current object represents a given type.
bool IsType(Type type) const { return type == type_; }
- bool is_bool() const { return type() == Type::BOOLEAN; }
- bool is_int() const { return type() == Type::INTEGER; }
- bool is_double() const { return type() == Type::DOUBLE; }
- bool is_string() const { return type() == Type::STRING; }
- bool is_blob() const { return type() == Type::BINARY; }
- bool is_dict() const { return type() == Type::DICTIONARY; }
- bool is_list() const { return type() == Type::LIST; }
-
- // These will all fatally assert if the type doesn't match.
- bool GetBool() const;
- int GetInt() const;
- double GetDouble() const; // Implicitly converts from int if necessary.
- const std::string& GetString() const;
- const std::vector<char>& GetBlob() const;
-
- size_t GetSize() const; // DEPRECATED, use GetBlob().size() instead.
- const char* GetBuffer() const; // DEPRECATED, use GetBlob().data() instead.
// These methods allow the convenient retrieval of the contents of the Value.
// If the current object can be converted into the given type, the value is
// returned through the |out_value| parameter and true is returned;
// otherwise, false is returned and |out_value| is unchanged.
- bool GetAsBoolean(bool* out_value) const;
- bool GetAsInteger(int* out_value) const;
- bool GetAsDouble(double* out_value) const;
- bool GetAsString(std::string* out_value) const;
- bool GetAsString(string16* out_value) const;
- bool GetAsString(const Value** out_value) const;
- bool GetAsString(StringPiece* out_value) const;
- bool GetAsBinary(const BinaryValue** out_value) const;
- // ListValue::From is the equivalent for std::unique_ptr conversions.
- bool GetAsList(ListValue** out_value);
- bool GetAsList(const ListValue** out_value) const;
- // DictionaryValue::From is the equivalent for std::unique_ptr conversions.
- bool GetAsDictionary(DictionaryValue** out_value);
- bool GetAsDictionary(const DictionaryValue** out_value) const;
+ virtual bool GetAsBoolean(bool* out_value) const;
+ virtual bool GetAsInteger(int* out_value) const;
+ virtual bool GetAsDouble(double* out_value) const;
+ virtual bool GetAsString(std::string* out_value) const;
+ virtual bool GetAsString(string16* out_value) const;
+ virtual bool GetAsString(const StringValue** out_value) const;
+ virtual bool GetAsBinary(const BinaryValue** out_value) const;
+ virtual bool GetAsList(ListValue** out_value);
+ virtual bool GetAsList(const ListValue** out_value) const;
+ virtual bool GetAsDictionary(DictionaryValue** out_value);
+ virtual bool GetAsDictionary(const DictionaryValue** out_value) const;
// Note: Do not add more types. See the file-level comment above for why.
// This creates a deep copy of the entire Value tree, and returns a pointer
- // to the copy. The caller gets ownership of the copy, of course.
+ // to the copy. The caller gets ownership of the copy, of course.
+ //
// Subclasses return their own type directly in their overrides;
// this works because C++ supports covariant return types.
- Value* DeepCopy() const;
+ virtual Value* DeepCopy() const;
// Preferred version of DeepCopy. TODO(estade): remove the above.
std::unique_ptr<Value> CreateDeepCopy() const;
// Compares if two Value objects have equal contents.
- bool Equals(const Value* other) const;
+ virtual bool Equals(const Value* other) const;
// Compares if two Value objects have equal contents. Can handle NULLs.
// NULLs are considered equal but different from Value::CreateNullValue().
static bool Equals(const Value* a, const Value* b);
protected:
- // TODO(crbug.com/646113): Make these private once DictionaryValue and
- // ListValue are properly inlined.
+ // These aren't safe for end-users, but they are useful for subclasses.
+ explicit Value(Type type);
+ Value(const Value& that);
+ Value& operator=(const Value& that);
+
+ private:
Type type_;
+};
+// FundamentalValue represents the simple fundamental types of values.
+class BASE_EXPORT FundamentalValue : public Value {
+ public:
+ explicit FundamentalValue(bool in_value);
+ explicit FundamentalValue(int in_value);
+ explicit FundamentalValue(double in_value);
+ ~FundamentalValue() override;
+
+ // Overridden from Value:
+ bool GetAsBoolean(bool* out_value) const override;
+ bool GetAsInteger(int* out_value) const override;
+ // Values of both type TYPE_INTEGER and TYPE_DOUBLE can be obtained as
+ // doubles.
+ bool GetAsDouble(double* out_value) const override;
+ FundamentalValue* DeepCopy() const override;
+ bool Equals(const Value* other) const override;
+
+ private:
union {
- bool bool_value_;
- int int_value_;
+ bool boolean_value_;
+ int integer_value_;
double double_value_;
- ManualConstructor<std::string> string_value_;
- ManualConstructor<std::vector<char>> binary_value_;
- // For current gcc and clang sizeof(DictStorage) = 48, which would result
- // in sizeof(Value) = 56 if DictStorage was stack allocated. Allocating it
- // on the heap results in sizeof(Value) = 40 for all of gcc, clang and MSVC.
- ManualConstructor<std::unique_ptr<DictStorage>> dict_ptr_;
- ManualConstructor<ListStorage> list_;
};
+};
+
+class BASE_EXPORT StringValue : public Value {
+ public:
+ // Initializes a StringValue with a UTF-8 narrow character string.
+ explicit StringValue(const std::string& in_value);
+
+ // Initializes a StringValue with a string16.
+ explicit StringValue(const string16& in_value);
+
+ ~StringValue() override;
+
+ // Returns |value_| as a pointer or reference.
+ std::string* GetString();
+ const std::string& GetString() const;
+
+ // Overridden from Value:
+ bool GetAsString(std::string* out_value) const override;
+ bool GetAsString(string16* out_value) const override;
+ bool GetAsString(const StringValue** out_value) const override;
+ StringValue* DeepCopy() const override;
+ bool Equals(const Value* other) const override;
private:
- void InternalCopyFundamentalValue(const Value& that);
- void InternalCopyConstructFrom(const Value& that);
- void InternalMoveConstructFrom(Value&& that);
- void InternalCopyAssignFromSameType(const Value& that);
- void InternalMoveAssignFromSameType(Value&& that);
- void InternalCleanup();
+ std::string value_;
+};
+
+class BASE_EXPORT BinaryValue: public Value {
+ public:
+ // Creates a BinaryValue with a null buffer and size of 0.
+ BinaryValue();
+
+ // Creates a BinaryValue, taking ownership of the bytes pointed to by
+ // |buffer|.
+ BinaryValue(std::unique_ptr<char[]> buffer, size_t size);
+
+ ~BinaryValue() override;
+
+ // For situations where you want to keep ownership of your buffer, this
+ // factory method creates a new BinaryValue by copying the contents of the
+ // buffer that's passed in.
+ static std::unique_ptr<BinaryValue> CreateWithCopiedBuffer(const char* buffer,
+ size_t size);
+
+ size_t GetSize() const { return size_; }
+
+ // May return NULL.
+ char* GetBuffer() { return buffer_.get(); }
+ const char* GetBuffer() const { return buffer_.get(); }
+
+ // Overridden from Value:
+ bool GetAsBinary(const BinaryValue** out_value) const override;
+ BinaryValue* DeepCopy() const override;
+ bool Equals(const Value* other) const override;
+
+ private:
+ std::unique_ptr<char[]> buffer_;
+ size_t size_;
+
+ DISALLOW_COPY_AND_ASSIGN(BinaryValue);
};
// DictionaryValue provides a key-value dictionary with (optional) "path"
@@ -200,19 +208,25 @@ class BASE_EXPORT Value {
// are |std::string|s and should be UTF-8 encoded.
class BASE_EXPORT DictionaryValue : public Value {
public:
+ using Storage = std::map<std::string, std::unique_ptr<Value>>;
// Returns |value| if it is a dictionary, nullptr otherwise.
static std::unique_ptr<DictionaryValue> From(std::unique_ptr<Value> value);
DictionaryValue();
+ ~DictionaryValue() override;
+
+ // Overridden from Value:
+ bool GetAsDictionary(DictionaryValue** out_value) override;
+ bool GetAsDictionary(const DictionaryValue** out_value) const override;
// Returns true if the current dictionary has a value for the given key.
- bool HasKey(StringPiece key) const;
+ bool HasKey(const std::string& key) const;
// Returns the number of Values in this dictionary.
- size_t size() const { return (*dict_ptr_)->size(); }
+ size_t size() const { return dictionary_.size(); }
// Returns whether the dictionary is empty.
- bool empty() const { return (*dict_ptr_)->empty(); }
+ bool empty() const { return dictionary_.empty(); }
// Clears any current contents of this dictionary.
void Clear();
@@ -224,31 +238,32 @@ class BASE_EXPORT DictionaryValue : public Value {
// If the key at any step of the way doesn't exist, or exists but isn't
// a DictionaryValue, a new DictionaryValue will be created and attached
// to the path in that location. |in_value| must be non-null.
- void Set(StringPiece path, std::unique_ptr<Value> in_value);
+ void Set(const std::string& path, std::unique_ptr<Value> in_value);
// Deprecated version of the above. TODO(estade): remove.
- void Set(StringPiece path, Value* in_value);
+ void Set(const std::string& path, Value* in_value);
// Convenience forms of Set(). These methods will replace any existing
// value at that path, even if it has a different type.
- void SetBoolean(StringPiece path, bool in_value);
- void SetInteger(StringPiece path, int in_value);
- void SetDouble(StringPiece path, double in_value);
- void SetString(StringPiece path, StringPiece in_value);
- void SetString(StringPiece path, const string16& in_value);
+ void SetBoolean(const std::string& path, bool in_value);
+ void SetInteger(const std::string& path, int in_value);
+ void SetDouble(const std::string& path, double in_value);
+ void SetString(const std::string& path, const std::string& in_value);
+ void SetString(const std::string& path, const string16& in_value);
// Like Set(), but without special treatment of '.'. This allows e.g. URLs to
// be used as paths.
- void SetWithoutPathExpansion(StringPiece key,
+ void SetWithoutPathExpansion(const std::string& key,
std::unique_ptr<Value> in_value);
// Deprecated version of the above. TODO(estade): remove.
- void SetWithoutPathExpansion(StringPiece key, Value* in_value);
+ void SetWithoutPathExpansion(const std::string& key, Value* in_value);
// Convenience forms of SetWithoutPathExpansion().
- void SetBooleanWithoutPathExpansion(StringPiece path, bool in_value);
- void SetIntegerWithoutPathExpansion(StringPiece path, int in_value);
- void SetDoubleWithoutPathExpansion(StringPiece path, double in_value);
- void SetStringWithoutPathExpansion(StringPiece path, StringPiece in_value);
- void SetStringWithoutPathExpansion(StringPiece path,
+ void SetBooleanWithoutPathExpansion(const std::string& path, bool in_value);
+ void SetIntegerWithoutPathExpansion(const std::string& path, int in_value);
+ void SetDoubleWithoutPathExpansion(const std::string& path, double in_value);
+ void SetStringWithoutPathExpansion(const std::string& path,
+ const std::string& in_value);
+ void SetStringWithoutPathExpansion(const std::string& path,
const string16& in_value);
// Gets the Value associated with the given path starting from this object.
@@ -266,41 +281,46 @@ class BASE_EXPORT DictionaryValue : public Value {
// and the return value will be true if the path is valid and the value at
// the end of the path can be returned in the form specified.
// |out_value| is optional and will only be set if non-NULL.
- bool GetBoolean(StringPiece path, bool* out_value) const;
- bool GetInteger(StringPiece path, int* out_value) const;
- // Values of both type Type::INTEGER and Type::DOUBLE can be obtained as
+ bool GetBoolean(const std::string& path, bool* out_value) const;
+ bool GetInteger(const std::string& path, int* out_value) const;
+ // Values of both type TYPE_INTEGER and TYPE_DOUBLE can be obtained as
// doubles.
- bool GetDouble(StringPiece path, double* out_value) const;
- bool GetString(StringPiece path, std::string* out_value) const;
- bool GetString(StringPiece path, string16* out_value) const;
- bool GetStringASCII(StringPiece path, std::string* out_value) const;
- bool GetBinary(StringPiece path, const BinaryValue** out_value) const;
- bool GetBinary(StringPiece path, BinaryValue** out_value);
+ bool GetDouble(const std::string& path, double* out_value) const;
+ bool GetString(const std::string& path, std::string* out_value) const;
+ bool GetString(const std::string& path, string16* out_value) const;
+ bool GetStringASCII(const std::string& path, std::string* out_value) const;
+ bool GetBinary(const std::string& path, const BinaryValue** out_value) const;
+ bool GetBinary(const std::string& path, BinaryValue** out_value);
bool GetDictionary(StringPiece path,
const DictionaryValue** out_value) const;
bool GetDictionary(StringPiece path, DictionaryValue** out_value);
- bool GetList(StringPiece path, const ListValue** out_value) const;
- bool GetList(StringPiece path, ListValue** out_value);
+ bool GetList(const std::string& path, const ListValue** out_value) const;
+ bool GetList(const std::string& path, ListValue** out_value);
// Like Get(), but without special treatment of '.'. This allows e.g. URLs to
// be used as paths.
- bool GetWithoutPathExpansion(StringPiece key, const Value** out_value) const;
- bool GetWithoutPathExpansion(StringPiece key, Value** out_value);
- bool GetBooleanWithoutPathExpansion(StringPiece key, bool* out_value) const;
- bool GetIntegerWithoutPathExpansion(StringPiece key, int* out_value) const;
- bool GetDoubleWithoutPathExpansion(StringPiece key, double* out_value) const;
- bool GetStringWithoutPathExpansion(StringPiece key,
+ bool GetWithoutPathExpansion(const std::string& key,
+ const Value** out_value) const;
+ bool GetWithoutPathExpansion(const std::string& key, Value** out_value);
+ bool GetBooleanWithoutPathExpansion(const std::string& key,
+ bool* out_value) const;
+ bool GetIntegerWithoutPathExpansion(const std::string& key,
+ int* out_value) const;
+ bool GetDoubleWithoutPathExpansion(const std::string& key,
+ double* out_value) const;
+ bool GetStringWithoutPathExpansion(const std::string& key,
std::string* out_value) const;
- bool GetStringWithoutPathExpansion(StringPiece key,
+ bool GetStringWithoutPathExpansion(const std::string& key,
string16* out_value) const;
bool GetDictionaryWithoutPathExpansion(
- StringPiece key,
+ const std::string& key,
const DictionaryValue** out_value) const;
- bool GetDictionaryWithoutPathExpansion(StringPiece key,
+ bool GetDictionaryWithoutPathExpansion(const std::string& key,
DictionaryValue** out_value);
- bool GetListWithoutPathExpansion(StringPiece key,
+ bool GetListWithoutPathExpansion(const std::string& key,
const ListValue** out_value) const;
- bool GetListWithoutPathExpansion(StringPiece key, ListValue** out_value);
+ bool GetListWithoutPathExpansion(const std::string& key,
+ ListValue** out_value);
// Removes the Value with the specified path from this dictionary (or one
// of its child dictionaries, if the path is more than just a local key).
@@ -308,16 +328,18 @@ class BASE_EXPORT DictionaryValue : public Value {
// |out_value|. If |out_value| is NULL, the removed value will be deleted.
// This method returns true if |path| is a valid path; otherwise it will
// return false and the DictionaryValue object will be unchanged.
- bool Remove(StringPiece path, std::unique_ptr<Value>* out_value);
+ virtual bool Remove(const std::string& path,
+ std::unique_ptr<Value>* out_value);
// Like Remove(), but without special treatment of '.'. This allows e.g. URLs
// to be used as paths.
- bool RemoveWithoutPathExpansion(StringPiece key,
- std::unique_ptr<Value>* out_value);
+ virtual bool RemoveWithoutPathExpansion(const std::string& key,
+ std::unique_ptr<Value>* out_value);
// Removes a path, clearing out all dictionaries on |path| that remain empty
// after removing the value at |path|.
- bool RemovePath(StringPiece path, std::unique_ptr<Value>* out_value);
+ virtual bool RemovePath(const std::string& path,
+ std::unique_ptr<Value>* out_value);
// Makes a copy of |this| but doesn't include empty dictionaries and lists in
// the copy. This never returns NULL, even if |this| itself is empty.
@@ -331,7 +353,7 @@ class BASE_EXPORT DictionaryValue : public Value {
void MergeDictionary(const DictionaryValue* dictionary);
// Swaps contents with the |other| dictionary.
- void Swap(DictionaryValue* other);
+ virtual void Swap(DictionaryValue* other);
// This class provides an iterator over both keys and values in the
// dictionary. It can't be used to modify the dictionary.
@@ -341,7 +363,7 @@ class BASE_EXPORT DictionaryValue : public Value {
Iterator(const Iterator& other);
~Iterator();
- bool IsAtEnd() const { return it_ == (*target_.dict_ptr_)->end(); }
+ bool IsAtEnd() const { return it_ == target_.dictionary_.end(); }
void Advance() { ++it_; }
const std::string& key() const { return it_->first; }
@@ -349,33 +371,42 @@ class BASE_EXPORT DictionaryValue : public Value {
private:
const DictionaryValue& target_;
- DictStorage::const_iterator it_;
+ Storage::const_iterator it_;
};
- DictionaryValue* DeepCopy() const;
+ // Overridden from Value:
+ DictionaryValue* DeepCopy() const override;
// Preferred version of DeepCopy. TODO(estade): remove the above.
std::unique_ptr<DictionaryValue> CreateDeepCopy() const;
+ bool Equals(const Value* other) const override;
+
+ private:
+ Storage dictionary_;
+
+ DISALLOW_COPY_AND_ASSIGN(DictionaryValue);
};
// This type of Value represents a list of other Value values.
class BASE_EXPORT ListValue : public Value {
public:
- using const_iterator = ListStorage::const_iterator;
- using iterator = ListStorage::iterator;
+ using Storage = std::vector<std::unique_ptr<Value>>;
+ using const_iterator = Storage::const_iterator;
+ using iterator = Storage::iterator;
// Returns |value| if it is a list, nullptr otherwise.
static std::unique_ptr<ListValue> From(std::unique_ptr<Value> value);
ListValue();
+ ~ListValue() override;
// Clears the contents of this ListValue
void Clear();
// Returns the number of Values in this list.
- size_t GetSize() const { return list_->size(); }
+ size_t GetSize() const { return list_.size(); }
// Returns whether the list is empty.
- bool empty() const { return list_->empty(); }
+ bool empty() const { return list_.empty(); }
// Sets the list item at the given index to be the Value specified by
// the value given. If the index beyond the current end of the list, null
@@ -399,7 +430,7 @@ class BASE_EXPORT ListValue : public Value {
// |out_value| is optional and will only be set if non-NULL.
bool GetBoolean(size_t index, bool* out_value) const;
bool GetInteger(size_t index, int* out_value) const;
- // Values of both type Type::INTEGER and Type::DOUBLE can be obtained as
+ // Values of both type TYPE_INTEGER and TYPE_DOUBLE can be obtained as
// doubles.
bool GetDouble(size_t index, double* out_value) const;
bool GetString(size_t index, std::string* out_value) const;
@@ -416,7 +447,7 @@ class BASE_EXPORT ListValue : public Value {
// passed out via |out_value|. If |out_value| is NULL, the removed value will
// be deleted. This method returns true if |index| is valid; otherwise
// it will return false and the ListValue object will be unchanged.
- bool Remove(size_t index, std::unique_ptr<Value>* out_value);
+ virtual bool Remove(size_t index, std::unique_ptr<Value>* out_value);
// Removes the first instance of |value| found in the list, if any, and
// deletes it. |index| is the location where |value| was found. Returns false
@@ -431,27 +462,26 @@ class BASE_EXPORT ListValue : public Value {
// Appends a Value to the end of the list.
void Append(std::unique_ptr<Value> in_value);
-#if !defined(OS_LINUX)
// Deprecated version of the above. TODO(estade): remove.
void Append(Value* in_value);
-#endif
// Convenience forms of Append.
void AppendBoolean(bool in_value);
void AppendInteger(int in_value);
void AppendDouble(double in_value);
- void AppendString(StringPiece in_value);
+ void AppendString(const std::string& in_value);
void AppendString(const string16& in_value);
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
- bool AppendIfNotPresent(std::unique_ptr<Value> in_value);
+ // Appends a Value if it's not already present. Takes ownership of the
+ // |in_value|. Returns true if successful, or false if the value was already
+ // present. If the value was already present the |in_value| is deleted.
+ bool AppendIfNotPresent(Value* in_value);
// Insert a Value at index.
// Returns true if successful, or false if the index was out of range.
- bool Insert(size_t index, std::unique_ptr<Value> in_value);
+ bool Insert(size_t index, Value* in_value);
// Searches for the first instance of |value| in the list using the Equals
// method of the Value type.
@@ -459,18 +489,28 @@ class BASE_EXPORT ListValue : public Value {
const_iterator Find(const Value& value) const;
// Swaps contents with the |other| list.
- void Swap(ListValue* other);
+ virtual void Swap(ListValue* other);
// Iteration.
- iterator begin() { return list_->begin(); }
- iterator end() { return list_->end(); }
+ iterator begin() { return list_.begin(); }
+ iterator end() { return list_.end(); }
+
+ const_iterator begin() const { return list_.begin(); }
+ const_iterator end() const { return list_.end(); }
- const_iterator begin() const { return list_->begin(); }
- const_iterator end() const { return list_->end(); }
+ // Overridden from Value:
+ bool GetAsList(ListValue** out_value) override;
+ bool GetAsList(const ListValue** out_value) const override;
+ ListValue* DeepCopy() const override;
+ bool Equals(const Value* other) const override;
- ListValue* DeepCopy() const;
// Preferred version of DeepCopy. TODO(estade): remove DeepCopy.
std::unique_ptr<ListValue> CreateDeepCopy() const;
+
+ private:
+ Storage list_;
+
+ DISALLOW_COPY_AND_ASSIGN(ListValue);
};
// This interface is implemented by classes that know how to serialize
@@ -505,6 +545,16 @@ class BASE_EXPORT ValueDeserializer {
BASE_EXPORT std::ostream& operator<<(std::ostream& out, const Value& value);
BASE_EXPORT inline std::ostream& operator<<(std::ostream& out,
+ const FundamentalValue& value) {
+ return out << static_cast<const Value&>(value);
+}
+
+BASE_EXPORT inline std::ostream& operator<<(std::ostream& out,
+ const StringValue& value) {
+ return out << static_cast<const Value&>(value);
+}
+
+BASE_EXPORT inline std::ostream& operator<<(std::ostream& out,
const DictionaryValue& value) {
return out << static_cast<const Value&>(value);
}
@@ -514,10 +564,6 @@ BASE_EXPORT inline std::ostream& operator<<(std::ostream& out,
return out << static_cast<const Value&>(value);
}
-// Stream operator so that enum class Types can be used in log statements.
-BASE_EXPORT std::ostream& operator<<(std::ostream& out,
- const Value::Type& type);
-
} // namespace base
#endif // BASE_VALUES_H_
diff --git a/base/values_unittest.cc b/base/values_unittest.cc
index 3bcdc16e37..d68522234d 100644
--- a/base/values_unittest.cc
+++ b/base/values_unittest.cc
@@ -9,7 +9,6 @@
#include <limits>
#include <memory>
#include <utility>
-#include <vector>
#include "base/memory/ptr_util.h"
#include "base/strings/string16.h"
@@ -18,322 +17,6 @@
namespace base {
-// Group of tests for the value constructors.
-TEST(ValuesTest, ConstructBool) {
- Value true_value(true);
- EXPECT_EQ(Value::Type::BOOLEAN, true_value.type());
- EXPECT_TRUE(true_value.GetBool());
-
- Value false_value(false);
- EXPECT_EQ(Value::Type::BOOLEAN, false_value.type());
- EXPECT_FALSE(false_value.GetBool());
-}
-
-TEST(ValuesTest, ConstructInt) {
- Value value(-37);
- EXPECT_EQ(Value::Type::INTEGER, value.type());
- EXPECT_EQ(-37, value.GetInt());
-}
-
-TEST(ValuesTest, ConstructDouble) {
- Value value(-4.655);
- EXPECT_EQ(Value::Type::DOUBLE, value.type());
- EXPECT_EQ(-4.655, value.GetDouble());
-}
-
-TEST(ValuesTest, ConstructStringFromConstCharPtr) {
- const char* str = "foobar";
- Value value(str);
- EXPECT_EQ(Value::Type::STRING, value.type());
- EXPECT_EQ("foobar", value.GetString());
-}
-
-TEST(ValuesTest, ConstructStringFromStdStringConstRef) {
- std::string str = "foobar";
- Value value(str);
- EXPECT_EQ(Value::Type::STRING, value.type());
- EXPECT_EQ("foobar", value.GetString());
-}
-
-TEST(ValuesTest, ConstructStringFromStdStringRefRef) {
- std::string str = "foobar";
- Value value(std::move(str));
- EXPECT_EQ(Value::Type::STRING, value.type());
- EXPECT_EQ("foobar", value.GetString());
-}
-
-TEST(ValuesTest, ConstructStringFromConstChar16Ptr) {
- string16 str = ASCIIToUTF16("foobar");
- Value value(str.c_str());
- EXPECT_EQ(Value::Type::STRING, value.type());
- EXPECT_EQ("foobar", value.GetString());
-}
-
-TEST(ValuesTest, ConstructStringFromString16) {
- string16 str = ASCIIToUTF16("foobar");
- Value value(str);
- EXPECT_EQ(Value::Type::STRING, value.type());
- EXPECT_EQ("foobar", value.GetString());
-}
-
-TEST(ValuesTest, ConstructStringFromStringPiece) {
- StringPiece str = "foobar";
- Value value(str);
- EXPECT_EQ(Value::Type::STRING, value.type());
- EXPECT_EQ("foobar", value.GetString());
-}
-
-TEST(ValuesTest, ConstructBinary) {
- BinaryValue value(std::vector<char>({0xF, 0x0, 0x0, 0xB, 0xA, 0x2}));
- EXPECT_EQ(Value::Type::BINARY, value.type());
- EXPECT_EQ(std::vector<char>({0xF, 0x0, 0x0, 0xB, 0xA, 0x2}), value.GetBlob());
-}
-
-TEST(ValuesTest, ConstructDict) {
- DictionaryValue value;
- EXPECT_EQ(Value::Type::DICTIONARY, value.type());
-}
-
-TEST(ValuesTest, ConstructList) {
- ListValue value;
- EXPECT_EQ(Value::Type::LIST, value.type());
-}
-
-// Group of tests for the copy constructors and copy-assigmnent. For equality
-// checks comparisons of the interesting fields are done instead of relying on
-// Equals being correct.
-TEST(ValuesTest, CopyBool) {
- Value true_value(true);
- Value copied_true_value(true_value);
- EXPECT_EQ(true_value.type(), copied_true_value.type());
- EXPECT_EQ(true_value.GetBool(), copied_true_value.GetBool());
-
- Value false_value(false);
- Value copied_false_value(false_value);
- EXPECT_EQ(false_value.type(), copied_false_value.type());
- EXPECT_EQ(false_value.GetBool(), copied_false_value.GetBool());
-
- Value blank;
-
- blank = true_value;
- EXPECT_EQ(true_value.type(), blank.type());
- EXPECT_EQ(true_value.GetBool(), blank.GetBool());
-
- blank = false_value;
- EXPECT_EQ(false_value.type(), blank.type());
- EXPECT_EQ(false_value.GetBool(), blank.GetBool());
-}
-
-TEST(ValuesTest, CopyInt) {
- Value value(74);
- Value copied_value(value);
- EXPECT_EQ(value.type(), copied_value.type());
- EXPECT_EQ(value.GetInt(), copied_value.GetInt());
-
- Value blank;
-
- blank = value;
- EXPECT_EQ(value.type(), blank.type());
- EXPECT_EQ(value.GetInt(), blank.GetInt());
-}
-
-TEST(ValuesTest, CopyDouble) {
- Value value(74.896);
- Value copied_value(value);
- EXPECT_EQ(value.type(), copied_value.type());
- EXPECT_EQ(value.GetDouble(), copied_value.GetDouble());
-
- Value blank;
-
- blank = value;
- EXPECT_EQ(value.type(), blank.type());
- EXPECT_EQ(value.GetDouble(), blank.GetDouble());
-}
-
-TEST(ValuesTest, CopyString) {
- Value value("foobar");
- Value copied_value(value);
- EXPECT_EQ(value.type(), copied_value.type());
- EXPECT_EQ(value.GetString(), copied_value.GetString());
-
- Value blank;
-
- blank = value;
- EXPECT_EQ(value.type(), blank.type());
- EXPECT_EQ(value.GetString(), blank.GetString());
-}
-
-TEST(ValuesTest, CopyBinary) {
- BinaryValue value(std::vector<char>({0xF, 0x0, 0x0, 0xB, 0xA, 0x2}));
- BinaryValue copied_value(value);
- EXPECT_EQ(value.type(), copied_value.type());
- EXPECT_EQ(value.GetBlob(), copied_value.GetBlob());
-
- Value blank;
-
- blank = value;
- EXPECT_EQ(value.type(), blank.type());
- EXPECT_EQ(value.GetBlob(), blank.GetBlob());
-}
-
-TEST(ValuesTest, CopyDictionary) {
- // TODO(crbug.com/646113): Clean this up once DictionaryValue switched to
- // value semantics.
- int copy;
- DictionaryValue value;
- value.SetInteger("Int", 123);
-
- DictionaryValue copied_value(value);
- copied_value.GetInteger("Int", &copy);
-
- EXPECT_EQ(value.type(), copied_value.type());
- EXPECT_EQ(123, copy);
-
- auto blank = MakeUnique<Value>();
-
- *blank = value;
- EXPECT_EQ(Value::Type::DICTIONARY, blank->type());
-
- static_cast<DictionaryValue*>(blank.get())->GetInteger("Int", &copy);
- EXPECT_EQ(123, copy);
-}
-
-TEST(ValuesTest, CopyList) {
- // TODO(crbug.com/646113): Clean this up once ListValue switched to
- // value semantics.
- int copy;
- ListValue value;
- value.AppendInteger(123);
-
- ListValue copied_value(value);
- copied_value.GetInteger(0, &copy);
-
- EXPECT_EQ(value.type(), copied_value.type());
- EXPECT_EQ(123, copy);
-
- auto blank = MakeUnique<Value>();
-
- *blank = value;
- EXPECT_EQ(Value::Type::LIST, blank->type());
-
- static_cast<ListValue*>(blank.get())->GetInteger(0, &copy);
- EXPECT_EQ(123, copy);
-}
-
-// Group of tests for the move constructors and move-assigmnent.
-TEST(ValuesTest, MoveBool) {
- Value true_value(true);
- Value moved_true_value(std::move(true_value));
- EXPECT_EQ(Value::Type::BOOLEAN, moved_true_value.type());
- EXPECT_TRUE(moved_true_value.GetBool());
-
- Value false_value(false);
- Value moved_false_value(std::move(false_value));
- EXPECT_EQ(Value::Type::BOOLEAN, moved_false_value.type());
- EXPECT_FALSE(moved_false_value.GetBool());
-
- Value blank;
-
- blank = Value(true);
- EXPECT_EQ(Value::Type::BOOLEAN, blank.type());
- EXPECT_TRUE(blank.GetBool());
-
- blank = Value(false);
- EXPECT_EQ(Value::Type::BOOLEAN, blank.type());
- EXPECT_FALSE(blank.GetBool());
-}
-
-TEST(ValuesTest, MoveInt) {
- Value value(74);
- Value moved_value(std::move(value));
- EXPECT_EQ(Value::Type::INTEGER, moved_value.type());
- EXPECT_EQ(74, moved_value.GetInt());
-
- Value blank;
-
- blank = Value(47);
- EXPECT_EQ(Value::Type::INTEGER, blank.type());
- EXPECT_EQ(47, blank.GetInt());
-}
-
-TEST(ValuesTest, MoveDouble) {
- Value value(74.896);
- Value moved_value(std::move(value));
- EXPECT_EQ(Value::Type::DOUBLE, moved_value.type());
- EXPECT_EQ(74.896, moved_value.GetDouble());
-
- Value blank;
-
- blank = Value(654.38);
- EXPECT_EQ(Value::Type::DOUBLE, blank.type());
- EXPECT_EQ(654.38, blank.GetDouble());
-}
-
-TEST(ValuesTest, MoveString) {
- Value value("foobar");
- Value moved_value(std::move(value));
- EXPECT_EQ(Value::Type::STRING, moved_value.type());
- EXPECT_EQ("foobar", moved_value.GetString());
-
- Value blank;
-
- blank = Value("foobar");
- EXPECT_EQ(Value::Type::STRING, blank.type());
- EXPECT_EQ("foobar", blank.GetString());
-}
-
-TEST(ValuesTest, MoveBinary) {
- const std::vector<char> buffer = {0xF, 0x0, 0x0, 0xB, 0xA, 0x2};
- BinaryValue value(buffer);
- BinaryValue moved_value(std::move(value));
- EXPECT_EQ(Value::Type::BINARY, moved_value.type());
- EXPECT_EQ(buffer, moved_value.GetBlob());
-
- Value blank;
-
- blank = BinaryValue(buffer);
- EXPECT_EQ(Value::Type::BINARY, blank.type());
- EXPECT_EQ(buffer, blank.GetBlob());
-}
-
-TEST(ValuesTest, MoveDictionary) {
- // TODO(crbug.com/646113): Clean this up once DictionaryValue switched to
- // value semantics.
- int move;
- DictionaryValue value;
- value.SetInteger("Int", 123);
-
- DictionaryValue moved_value(std::move(value));
- moved_value.GetInteger("Int", &move);
-
- EXPECT_EQ(Value::Type::DICTIONARY, moved_value.type());
- EXPECT_EQ(123, move);
-
- Value blank;
-
- blank = DictionaryValue();
- EXPECT_EQ(Value::Type::DICTIONARY, blank.type());
-}
-
-TEST(ValuesTest, MoveList) {
- // TODO(crbug.com/646113): Clean this up once ListValue switched to
- // value semantics.
- int move;
- ListValue value;
- value.AppendInteger(123);
-
- ListValue moved_value(std::move(value));
- moved_value.GetInteger(0, &move);
-
- EXPECT_EQ(Value::Type::LIST, moved_value.type());
- EXPECT_EQ(123, move);
-
- Value blank;
-
- blank = ListValue();
- EXPECT_EQ(Value::Type::LIST, blank.type());
-}
-
TEST(ValuesTest, Basic) {
// Test basic dictionary getting/setting
DictionaryValue settings;
@@ -379,10 +62,10 @@ TEST(ValuesTest, Basic) {
TEST(ValuesTest, List) {
std::unique_ptr<ListValue> mixed_list(new ListValue());
- mixed_list->Set(0, MakeUnique<Value>(true));
- mixed_list->Set(1, MakeUnique<Value>(42));
- mixed_list->Set(2, MakeUnique<Value>(88.8));
- mixed_list->Set(3, MakeUnique<Value>("foo"));
+ mixed_list->Set(0, WrapUnique(new FundamentalValue(true)));
+ mixed_list->Set(1, WrapUnique(new FundamentalValue(42)));
+ mixed_list->Set(2, WrapUnique(new FundamentalValue(88.8)));
+ mixed_list->Set(3, WrapUnique(new StringValue("foo")));
ASSERT_EQ(4u, mixed_list->GetSize());
Value *value = NULL;
@@ -417,8 +100,8 @@ TEST(ValuesTest, List) {
ASSERT_EQ("foo", string_value);
// Try searching in the mixed list.
- base::Value sought_value(42);
- base::Value not_found_value(false);
+ base::FundamentalValue sought_value(42);
+ base::FundamentalValue not_found_value(false);
ASSERT_NE(mixed_list->end(), mixed_list->Find(sought_value));
ASSERT_TRUE((*mixed_list->Find(sought_value))->GetAsInteger(&int_value));
@@ -427,15 +110,16 @@ TEST(ValuesTest, List) {
}
TEST(ValuesTest, BinaryValue) {
- // Default constructor creates a BinaryValue with a buffer of size 0.
- auto binary = MakeUnique<Value>(Value::Type::BINARY);
+ // Default constructor creates a BinaryValue with a null buffer and size 0.
+ std::unique_ptr<BinaryValue> binary(new BinaryValue());
ASSERT_TRUE(binary.get());
+ ASSERT_EQ(NULL, binary->GetBuffer());
ASSERT_EQ(0U, binary->GetSize());
// Test the common case of a non-empty buffer
- std::vector<char> buffer(15);
- char* original_buffer = buffer.data();
- binary.reset(new BinaryValue(std::move(buffer)));
+ std::unique_ptr<char[]> buffer(new char[15]);
+ char* original_buffer = buffer.get();
+ binary.reset(new BinaryValue(std::move(buffer), 15));
ASSERT_TRUE(binary.get());
ASSERT_TRUE(binary->GetBuffer());
ASSERT_EQ(original_buffer, binary->GetBuffer());
@@ -459,17 +143,17 @@ TEST(ValuesTest, BinaryValue) {
TEST(ValuesTest, StringValue) {
// Test overloaded StringValue constructor.
- std::unique_ptr<Value> narrow_value(new Value("narrow"));
+ std::unique_ptr<Value> narrow_value(new StringValue("narrow"));
ASSERT_TRUE(narrow_value.get());
- ASSERT_TRUE(narrow_value->IsType(Value::Type::STRING));
- std::unique_ptr<Value> utf16_value(new Value(ASCIIToUTF16("utf16")));
+ ASSERT_TRUE(narrow_value->IsType(Value::TYPE_STRING));
+ std::unique_ptr<Value> utf16_value(new StringValue(ASCIIToUTF16("utf16")));
ASSERT_TRUE(utf16_value.get());
- ASSERT_TRUE(utf16_value->IsType(Value::Type::STRING));
+ ASSERT_TRUE(utf16_value->IsType(Value::TYPE_STRING));
// Test overloaded GetAsString.
std::string narrow = "http://google.com";
string16 utf16 = ASCIIToUTF16("http://google.com");
- const Value* string_value = NULL;
+ const StringValue* string_value = NULL;
ASSERT_TRUE(narrow_value->GetAsString(&narrow));
ASSERT_TRUE(narrow_value->GetAsString(&utf16));
ASSERT_TRUE(narrow_value->GetAsString(&string_value));
@@ -487,23 +171,65 @@ TEST(ValuesTest, StringValue) {
// Don't choke on NULL values.
ASSERT_TRUE(narrow_value->GetAsString(static_cast<string16*>(NULL)));
ASSERT_TRUE(narrow_value->GetAsString(static_cast<std::string*>(NULL)));
- ASSERT_TRUE(narrow_value->GetAsString(static_cast<const Value**>(NULL)));
+ ASSERT_TRUE(narrow_value->GetAsString(
+ static_cast<const StringValue**>(NULL)));
}
+// This is a Value object that allows us to tell if it's been
+// properly deleted by modifying the value of external flag on destruction.
+class DeletionTestValue : public Value {
+ public:
+ explicit DeletionTestValue(bool* deletion_flag) : Value(TYPE_NULL) {
+ Init(deletion_flag); // Separate function so that we can use ASSERT_*
+ }
+
+ void Init(bool* deletion_flag) {
+ ASSERT_TRUE(deletion_flag);
+ deletion_flag_ = deletion_flag;
+ *deletion_flag_ = false;
+ }
+
+ ~DeletionTestValue() override { *deletion_flag_ = true; }
+
+ private:
+ bool* deletion_flag_;
+};
+
TEST(ValuesTest, ListDeletion) {
- ListValue list;
- list.Append(MakeUnique<Value>());
- EXPECT_FALSE(list.empty());
- list.Clear();
- EXPECT_TRUE(list.empty());
+ bool deletion_flag = true;
+
+ {
+ ListValue list;
+ list.Append(WrapUnique(new DeletionTestValue(&deletion_flag)));
+ EXPECT_FALSE(deletion_flag);
+ }
+ EXPECT_TRUE(deletion_flag);
+
+ {
+ ListValue list;
+ list.Append(WrapUnique(new DeletionTestValue(&deletion_flag)));
+ EXPECT_FALSE(deletion_flag);
+ list.Clear();
+ EXPECT_TRUE(deletion_flag);
+ }
+
+ {
+ ListValue list;
+ list.Append(WrapUnique(new DeletionTestValue(&deletion_flag)));
+ EXPECT_FALSE(deletion_flag);
+ EXPECT_TRUE(list.Set(0, Value::CreateNullValue()));
+ EXPECT_TRUE(deletion_flag);
+ }
}
TEST(ValuesTest, ListRemoval) {
+ bool deletion_flag = true;
std::unique_ptr<Value> removed_item;
{
ListValue list;
- list.Append(MakeUnique<Value>());
+ list.Append(WrapUnique(new DeletionTestValue(&deletion_flag)));
+ EXPECT_FALSE(deletion_flag);
EXPECT_EQ(1U, list.GetSize());
EXPECT_FALSE(list.Remove(std::numeric_limits<size_t>::max(),
&removed_item));
@@ -512,55 +238,88 @@ TEST(ValuesTest, ListRemoval) {
ASSERT_TRUE(removed_item);
EXPECT_EQ(0U, list.GetSize());
}
+ EXPECT_FALSE(deletion_flag);
removed_item.reset();
+ EXPECT_TRUE(deletion_flag);
{
ListValue list;
- list.Append(MakeUnique<Value>());
+ list.Append(WrapUnique(new DeletionTestValue(&deletion_flag)));
+ EXPECT_FALSE(deletion_flag);
EXPECT_TRUE(list.Remove(0, NULL));
+ EXPECT_TRUE(deletion_flag);
EXPECT_EQ(0U, list.GetSize());
}
{
ListValue list;
- auto value = MakeUnique<Value>();
- Value* original_value = value.get();
+ std::unique_ptr<DeletionTestValue> value(
+ new DeletionTestValue(&deletion_flag));
+ DeletionTestValue* original_value = value.get();
list.Append(std::move(value));
+ EXPECT_FALSE(deletion_flag);
size_t index = 0;
list.Remove(*original_value, &index);
EXPECT_EQ(0U, index);
+ EXPECT_TRUE(deletion_flag);
EXPECT_EQ(0U, list.GetSize());
}
}
TEST(ValuesTest, DictionaryDeletion) {
std::string key = "test";
- DictionaryValue dict;
- dict.Set(key, MakeUnique<Value>());
- EXPECT_FALSE(dict.empty());
- dict.Clear();
- EXPECT_TRUE(dict.empty());
+ bool deletion_flag = true;
+
+ {
+ DictionaryValue dict;
+ dict.Set(key, WrapUnique(new DeletionTestValue(&deletion_flag)));
+ EXPECT_FALSE(deletion_flag);
+ }
+ EXPECT_TRUE(deletion_flag);
+
+ {
+ DictionaryValue dict;
+ dict.Set(key, WrapUnique(new DeletionTestValue(&deletion_flag)));
+ EXPECT_FALSE(deletion_flag);
+ dict.Clear();
+ EXPECT_TRUE(deletion_flag);
+ }
+
+ {
+ DictionaryValue dict;
+ dict.Set(key, WrapUnique(new DeletionTestValue(&deletion_flag)));
+ EXPECT_FALSE(deletion_flag);
+ dict.Set(key, Value::CreateNullValue());
+ EXPECT_TRUE(deletion_flag);
+ }
}
TEST(ValuesTest, DictionaryRemoval) {
std::string key = "test";
+ bool deletion_flag = true;
std::unique_ptr<Value> removed_item;
{
DictionaryValue dict;
- dict.Set(key, MakeUnique<Value>());
+ dict.Set(key, WrapUnique(new DeletionTestValue(&deletion_flag)));
+ EXPECT_FALSE(deletion_flag);
EXPECT_TRUE(dict.HasKey(key));
EXPECT_FALSE(dict.Remove("absent key", &removed_item));
EXPECT_TRUE(dict.Remove(key, &removed_item));
EXPECT_FALSE(dict.HasKey(key));
ASSERT_TRUE(removed_item);
}
+ EXPECT_FALSE(deletion_flag);
+ removed_item.reset();
+ EXPECT_TRUE(deletion_flag);
{
DictionaryValue dict;
- dict.Set(key, MakeUnique<Value>());
+ dict.Set(key, WrapUnique(new DeletionTestValue(&deletion_flag)));
+ EXPECT_FALSE(deletion_flag);
EXPECT_TRUE(dict.HasKey(key));
EXPECT_TRUE(dict.Remove(key, NULL));
+ EXPECT_TRUE(deletion_flag);
EXPECT_FALSE(dict.HasKey(key));
}
}
@@ -584,7 +343,7 @@ TEST(ValuesTest, DictionaryWithoutPathExpansion) {
EXPECT_FALSE(dict.Get("this.isnt.expanded", &value3));
Value* value4;
ASSERT_TRUE(dict.GetWithoutPathExpansion("this.isnt.expanded", &value4));
- EXPECT_EQ(Value::Type::NONE, value4->GetType());
+ EXPECT_EQ(Value::TYPE_NULL, value4->GetType());
}
// Tests the deprecated version of SetWithoutPathExpansion.
@@ -608,7 +367,7 @@ TEST(ValuesTest, DictionaryWithoutPathExpansionDeprecated) {
EXPECT_FALSE(dict.Get("this.isnt.expanded", &value3));
Value* value4;
ASSERT_TRUE(dict.GetWithoutPathExpansion("this.isnt.expanded", &value4));
- EXPECT_EQ(Value::Type::NONE, value4->GetType());
+ EXPECT_EQ(Value::TYPE_NULL, value4->GetType());
}
TEST(ValuesTest, DictionaryRemovePath) {
@@ -619,7 +378,7 @@ TEST(ValuesTest, DictionaryRemovePath) {
std::unique_ptr<Value> removed_item;
EXPECT_TRUE(dict.RemovePath("a.long.way.down", &removed_item));
ASSERT_TRUE(removed_item);
- EXPECT_TRUE(removed_item->IsType(base::Value::Type::INTEGER));
+ EXPECT_TRUE(removed_item->IsType(base::Value::TYPE_INTEGER));
EXPECT_FALSE(dict.HasKey("a.long.way.down"));
EXPECT_FALSE(dict.HasKey("a.long.way"));
EXPECT_TRUE(dict.Get("a.long.key.path", NULL));
@@ -632,7 +391,7 @@ TEST(ValuesTest, DictionaryRemovePath) {
removed_item.reset();
EXPECT_TRUE(dict.RemovePath("a.long.key.path", &removed_item));
ASSERT_TRUE(removed_item);
- EXPECT_TRUE(removed_item->IsType(base::Value::Type::BOOLEAN));
+ EXPECT_TRUE(removed_item->IsType(base::Value::TYPE_BOOLEAN));
EXPECT_TRUE(dict.empty());
}
@@ -641,34 +400,38 @@ TEST(ValuesTest, DeepCopy) {
std::unique_ptr<Value> scoped_null = Value::CreateNullValue();
Value* original_null = scoped_null.get();
original_dict.Set("null", std::move(scoped_null));
- std::unique_ptr<Value> scoped_bool(new Value(true));
- Value* original_bool = scoped_bool.get();
+ std::unique_ptr<FundamentalValue> scoped_bool(new FundamentalValue(true));
+ FundamentalValue* original_bool = scoped_bool.get();
original_dict.Set("bool", std::move(scoped_bool));
- std::unique_ptr<Value> scoped_int(new Value(42));
- Value* original_int = scoped_int.get();
+ std::unique_ptr<FundamentalValue> scoped_int(new FundamentalValue(42));
+ FundamentalValue* original_int = scoped_int.get();
original_dict.Set("int", std::move(scoped_int));
- std::unique_ptr<Value> scoped_double(new Value(3.14));
- Value* original_double = scoped_double.get();
+ std::unique_ptr<FundamentalValue> scoped_double(new FundamentalValue(3.14));
+ FundamentalValue* original_double = scoped_double.get();
original_dict.Set("double", std::move(scoped_double));
- std::unique_ptr<Value> scoped_string(new Value("hello"));
- Value* original_string = scoped_string.get();
+ std::unique_ptr<StringValue> scoped_string(new StringValue("hello"));
+ StringValue* original_string = scoped_string.get();
original_dict.Set("string", std::move(scoped_string));
- std::unique_ptr<Value> scoped_string16(new Value(ASCIIToUTF16("hello16")));
- Value* original_string16 = scoped_string16.get();
+ std::unique_ptr<StringValue> scoped_string16(
+ new StringValue(ASCIIToUTF16("hello16")));
+ StringValue* original_string16 = scoped_string16.get();
original_dict.Set("string16", std::move(scoped_string16));
- std::vector<char> original_buffer(42, '!');
+ std::unique_ptr<char[]> original_buffer(new char[42]);
+ memset(original_buffer.get(), '!', 42);
std::unique_ptr<BinaryValue> scoped_binary(
- new BinaryValue(std::move(original_buffer)));
+ new BinaryValue(std::move(original_buffer), 42));
BinaryValue* original_binary = scoped_binary.get();
original_dict.Set("binary", std::move(scoped_binary));
std::unique_ptr<ListValue> scoped_list(new ListValue());
Value* original_list = scoped_list.get();
- std::unique_ptr<Value> scoped_list_element_0(new Value(0));
+ std::unique_ptr<FundamentalValue> scoped_list_element_0(
+ new FundamentalValue(0));
Value* original_list_element_0 = scoped_list_element_0.get();
scoped_list->Append(std::move(scoped_list_element_0));
- std::unique_ptr<Value> scoped_list_element_1(new Value(1));
+ std::unique_ptr<FundamentalValue> scoped_list_element_1(
+ new FundamentalValue(1));
Value* original_list_element_1 = scoped_list_element_1.get();
scoped_list->Append(std::move(scoped_list_element_1));
original_dict.Set("list", std::move(scoped_list));
@@ -687,13 +450,13 @@ TEST(ValuesTest, DeepCopy) {
ASSERT_TRUE(copy_dict->Get("null", &copy_null));
ASSERT_TRUE(copy_null);
ASSERT_NE(copy_null, original_null);
- ASSERT_TRUE(copy_null->IsType(Value::Type::NONE));
+ ASSERT_TRUE(copy_null->IsType(Value::TYPE_NULL));
Value* copy_bool = NULL;
ASSERT_TRUE(copy_dict->Get("bool", &copy_bool));
ASSERT_TRUE(copy_bool);
ASSERT_NE(copy_bool, original_bool);
- ASSERT_TRUE(copy_bool->IsType(Value::Type::BOOLEAN));
+ ASSERT_TRUE(copy_bool->IsType(Value::TYPE_BOOLEAN));
bool copy_bool_value = false;
ASSERT_TRUE(copy_bool->GetAsBoolean(&copy_bool_value));
ASSERT_TRUE(copy_bool_value);
@@ -702,7 +465,7 @@ TEST(ValuesTest, DeepCopy) {
ASSERT_TRUE(copy_dict->Get("int", &copy_int));
ASSERT_TRUE(copy_int);
ASSERT_NE(copy_int, original_int);
- ASSERT_TRUE(copy_int->IsType(Value::Type::INTEGER));
+ ASSERT_TRUE(copy_int->IsType(Value::TYPE_INTEGER));
int copy_int_value = 0;
ASSERT_TRUE(copy_int->GetAsInteger(&copy_int_value));
ASSERT_EQ(42, copy_int_value);
@@ -711,7 +474,7 @@ TEST(ValuesTest, DeepCopy) {
ASSERT_TRUE(copy_dict->Get("double", &copy_double));
ASSERT_TRUE(copy_double);
ASSERT_NE(copy_double, original_double);
- ASSERT_TRUE(copy_double->IsType(Value::Type::DOUBLE));
+ ASSERT_TRUE(copy_double->IsType(Value::TYPE_DOUBLE));
double copy_double_value = 0;
ASSERT_TRUE(copy_double->GetAsDouble(&copy_double_value));
ASSERT_EQ(3.14, copy_double_value);
@@ -720,7 +483,7 @@ TEST(ValuesTest, DeepCopy) {
ASSERT_TRUE(copy_dict->Get("string", &copy_string));
ASSERT_TRUE(copy_string);
ASSERT_NE(copy_string, original_string);
- ASSERT_TRUE(copy_string->IsType(Value::Type::STRING));
+ ASSERT_TRUE(copy_string->IsType(Value::TYPE_STRING));
std::string copy_string_value;
string16 copy_string16_value;
ASSERT_TRUE(copy_string->GetAsString(&copy_string_value));
@@ -732,7 +495,7 @@ TEST(ValuesTest, DeepCopy) {
ASSERT_TRUE(copy_dict->Get("string16", &copy_string16));
ASSERT_TRUE(copy_string16);
ASSERT_NE(copy_string16, original_string16);
- ASSERT_TRUE(copy_string16->IsType(Value::Type::STRING));
+ ASSERT_TRUE(copy_string16->IsType(Value::TYPE_STRING));
ASSERT_TRUE(copy_string16->GetAsString(&copy_string_value));
ASSERT_TRUE(copy_string16->GetAsString(&copy_string16_value));
ASSERT_EQ(std::string("hello16"), copy_string_value);
@@ -742,17 +505,20 @@ TEST(ValuesTest, DeepCopy) {
ASSERT_TRUE(copy_dict->Get("binary", &copy_binary));
ASSERT_TRUE(copy_binary);
ASSERT_NE(copy_binary, original_binary);
- ASSERT_TRUE(copy_binary->IsType(Value::Type::BINARY));
- ASSERT_NE(original_binary->GetBuffer(), copy_binary->GetBuffer());
- ASSERT_EQ(original_binary->GetSize(), copy_binary->GetSize());
- ASSERT_EQ(0, memcmp(original_binary->GetBuffer(), copy_binary->GetBuffer(),
- original_binary->GetSize()));
+ ASSERT_TRUE(copy_binary->IsType(Value::TYPE_BINARY));
+ ASSERT_NE(original_binary->GetBuffer(),
+ static_cast<BinaryValue*>(copy_binary)->GetBuffer());
+ ASSERT_EQ(original_binary->GetSize(),
+ static_cast<BinaryValue*>(copy_binary)->GetSize());
+ ASSERT_EQ(0, memcmp(original_binary->GetBuffer(),
+ static_cast<BinaryValue*>(copy_binary)->GetBuffer(),
+ original_binary->GetSize()));
Value* copy_value = NULL;
ASSERT_TRUE(copy_dict->Get("list", &copy_value));
ASSERT_TRUE(copy_value);
ASSERT_NE(copy_value, original_list);
- ASSERT_TRUE(copy_value->IsType(Value::Type::LIST));
+ ASSERT_TRUE(copy_value->IsType(Value::TYPE_LIST));
ListValue* copy_list = NULL;
ASSERT_TRUE(copy_value->GetAsList(&copy_list));
ASSERT_TRUE(copy_list);
@@ -778,7 +544,7 @@ TEST(ValuesTest, DeepCopy) {
ASSERT_TRUE(copy_dict->Get("dictionary", &copy_value));
ASSERT_TRUE(copy_value);
ASSERT_NE(copy_value, original_nested_dictionary);
- ASSERT_TRUE(copy_value->IsType(Value::Type::DICTIONARY));
+ ASSERT_TRUE(copy_value->IsType(Value::TYPE_DICTIONARY));
DictionaryValue* copy_nested_dictionary = NULL;
ASSERT_TRUE(copy_value->GetAsDictionary(&copy_nested_dictionary));
ASSERT_TRUE(copy_nested_dictionary);
@@ -791,7 +557,7 @@ TEST(ValuesTest, Equals) {
EXPECT_NE(null1.get(), null2.get());
EXPECT_TRUE(null1->Equals(null2.get()));
- Value boolean(false);
+ FundamentalValue boolean(false);
EXPECT_FALSE(null1->Equals(&boolean));
DictionaryValue dv;
@@ -816,7 +582,7 @@ TEST(ValuesTest, Equals) {
copy->Set("f", std::move(list_copy));
EXPECT_TRUE(dv.Equals(copy.get()));
- original_list->Append(MakeUnique<Value>(true));
+ original_list->Append(WrapUnique(new FundamentalValue(true)));
EXPECT_FALSE(dv.Equals(copy.get()));
// Check if Equals detects differences in only the keys.
@@ -833,9 +599,9 @@ TEST(ValuesTest, StaticEquals) {
EXPECT_TRUE(Value::Equals(null1.get(), null2.get()));
EXPECT_TRUE(Value::Equals(NULL, NULL));
- std::unique_ptr<Value> i42(new Value(42));
- std::unique_ptr<Value> j42(new Value(42));
- std::unique_ptr<Value> i17(new Value(17));
+ std::unique_ptr<Value> i42(new FundamentalValue(42));
+ std::unique_ptr<Value> j42(new FundamentalValue(42));
+ std::unique_ptr<Value> i17(new FundamentalValue(17));
EXPECT_TRUE(Value::Equals(i42.get(), i42.get()));
EXPECT_TRUE(Value::Equals(j42.get(), i42.get()));
EXPECT_TRUE(Value::Equals(i42.get(), j42.get()));
@@ -855,33 +621,37 @@ TEST(ValuesTest, DeepCopyCovariantReturnTypes) {
std::unique_ptr<Value> scoped_null(Value::CreateNullValue());
Value* original_null = scoped_null.get();
original_dict.Set("null", std::move(scoped_null));
- std::unique_ptr<Value> scoped_bool(new Value(true));
+ std::unique_ptr<FundamentalValue> scoped_bool(new FundamentalValue(true));
Value* original_bool = scoped_bool.get();
original_dict.Set("bool", std::move(scoped_bool));
- std::unique_ptr<Value> scoped_int(new Value(42));
+ std::unique_ptr<FundamentalValue> scoped_int(new FundamentalValue(42));
Value* original_int = scoped_int.get();
original_dict.Set("int", std::move(scoped_int));
- std::unique_ptr<Value> scoped_double(new Value(3.14));
+ std::unique_ptr<FundamentalValue> scoped_double(new FundamentalValue(3.14));
Value* original_double = scoped_double.get();
original_dict.Set("double", std::move(scoped_double));
- std::unique_ptr<Value> scoped_string(new Value("hello"));
+ std::unique_ptr<StringValue> scoped_string(new StringValue("hello"));
Value* original_string = scoped_string.get();
original_dict.Set("string", std::move(scoped_string));
- std::unique_ptr<Value> scoped_string16(new Value(ASCIIToUTF16("hello16")));
+ std::unique_ptr<StringValue> scoped_string16(
+ new StringValue(ASCIIToUTF16("hello16")));
Value* original_string16 = scoped_string16.get();
original_dict.Set("string16", std::move(scoped_string16));
- std::vector<char> original_buffer(42, '!');
+ std::unique_ptr<char[]> original_buffer(new char[42]);
+ memset(original_buffer.get(), '!', 42);
std::unique_ptr<BinaryValue> scoped_binary(
- new BinaryValue(std::move(original_buffer)));
+ new BinaryValue(std::move(original_buffer), 42));
Value* original_binary = scoped_binary.get();
original_dict.Set("binary", std::move(scoped_binary));
std::unique_ptr<ListValue> scoped_list(new ListValue());
Value* original_list = scoped_list.get();
- std::unique_ptr<Value> scoped_list_element_0(new Value(0));
+ std::unique_ptr<FundamentalValue> scoped_list_element_0(
+ new FundamentalValue(0));
scoped_list->Append(std::move(scoped_list_element_0));
- std::unique_ptr<Value> scoped_list_element_1(new Value(1));
+ std::unique_ptr<FundamentalValue> scoped_list_element_1(
+ new FundamentalValue(1));
scoped_list->Append(std::move(scoped_list_element_1));
original_dict.Set("list", std::move(scoped_list));
@@ -969,7 +739,7 @@ TEST(ValuesTest, RemoveEmptyChildren) {
{
std::unique_ptr<ListValue> inner(new ListValue);
std::unique_ptr<ListValue> inner2(new ListValue);
- inner2->Append(MakeUnique<Value>("hello"));
+ inner2->Append(WrapUnique(new StringValue("hello")));
inner->Append(WrapUnique(new DictionaryValue));
inner->Append(std::move(inner2));
root->Set("list_with_empty_children", std::move(inner));
@@ -1067,7 +837,7 @@ TEST(ValuesTest, DictionaryIterator) {
ADD_FAILURE();
}
- Value value1("value1");
+ StringValue value1("value1");
dict.Set("key1", value1.CreateDeepCopy());
bool seen1 = false;
for (DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
@@ -1078,7 +848,7 @@ TEST(ValuesTest, DictionaryIterator) {
}
EXPECT_TRUE(seen1);
- Value value2("value2");
+ StringValue value2("value2");
dict.Set("key2", value2.CreateDeepCopy());
bool seen2 = seen1 = false;
for (DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
@@ -1104,11 +874,11 @@ TEST(ValuesTest, GetWithNullOutValue) {
DictionaryValue main_dict;
ListValue main_list;
- Value bool_value(false);
- Value int_value(1234);
- Value double_value(12.34567);
- Value string_value("foo");
- BinaryValue binary_value(Value::Type::BINARY);
+ FundamentalValue bool_value(false);
+ FundamentalValue int_value(1234);
+ FundamentalValue double_value(12.34567);
+ StringValue string_value("foo");
+ BinaryValue binary_value;
DictionaryValue dict_value;
ListValue list_value;
diff --git a/base/version.cc b/base/version.cc
index ca97a84222..02213fbf15 100644
--- a/base/version.cc
+++ b/base/version.cc
@@ -93,8 +93,6 @@ Version::Version(const std::string& version_str) {
components_.swap(parsed);
}
-Version::Version(std::vector<uint32_t> components) : components_(components) {}
-
bool Version::IsValid() const {
return (!components_.empty());
}
diff --git a/base/version.h b/base/version.h
index b3a0956bbe..25b570a4e3 100644
--- a/base/version.h
+++ b/base/version.h
@@ -25,17 +25,13 @@ class BASE_EXPORT Version {
Version(const Version& other);
+ ~Version();
+
// Initializes from a decimal dotted version number, like "0.1.1".
// Each component is limited to a uint16_t. Call IsValid() to learn
// the outcome.
explicit Version(const std::string& version_str);
- // Initializes from a vector of components, like {1, 2, 3, 4}. Call IsValid()
- // to learn the outcome.
- explicit Version(std::vector<uint32_t> components);
-
- ~Version();
-
// Returns true if the object contains a valid version number.
bool IsValid() const;
@@ -73,4 +69,8 @@ BASE_EXPORT std::ostream& operator<<(std::ostream& stream, const Version& v);
} // namespace base
+// TODO(xhwang) remove this when all users are updated to explicitly use the
+// namespace
+using base::Version;
+
#endif // BASE_VERSION_H_
diff --git a/base/version_unittest.cc b/base/version_unittest.cc
index 4ca784fc11..5d9ea9973c 100644
--- a/base/version_unittest.cc
+++ b/base/version_unittest.cc
@@ -6,7 +6,6 @@
#include <stddef.h>
#include <stdint.h>
-#include <utility>
#include "base/macros.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -14,17 +13,17 @@
namespace {
TEST(VersionTest, DefaultConstructor) {
- base::Version v;
+ Version v;
EXPECT_FALSE(v.IsValid());
}
TEST(VersionTest, ValueSemantics) {
- base::Version v1("1.2.3.4");
+ Version v1("1.2.3.4");
EXPECT_TRUE(v1.IsValid());
- base::Version v3;
+ Version v3;
EXPECT_FALSE(v3.IsValid());
{
- base::Version v2(v1);
+ Version v2(v1);
v3 = v2;
EXPECT_TRUE(v2.IsValid());
EXPECT_EQ(v1, v2);
@@ -32,14 +31,6 @@ TEST(VersionTest, ValueSemantics) {
EXPECT_EQ(v3, v1);
}
-TEST(VersionTest, MoveSemantics) {
- const std::vector<uint32_t> components = {1, 2, 3, 4};
- base::Version v1(std::move(components));
- EXPECT_TRUE(v1.IsValid());
- base::Version v2("1.2.3.4");
- EXPECT_EQ(v1, v2);
-}
-
TEST(VersionTest, GetVersionFromString) {
static const struct version_string {
const char* input;
@@ -76,7 +67,7 @@ TEST(VersionTest, GetVersionFromString) {
};
for (size_t i = 0; i < arraysize(cases); ++i) {
- base::Version version(cases[i].input);
+ Version version(cases[i].input);
EXPECT_EQ(cases[i].success, version.IsValid());
if (cases[i].success) {
EXPECT_EQ(cases[i].parts, version.components().size());
@@ -105,8 +96,8 @@ TEST(VersionTest, Compare) {
{"11.0.10", "15.5.28.130162", -1},
};
for (size_t i = 0; i < arraysize(cases); ++i) {
- base::Version lhs(cases[i].lhs);
- base::Version rhs(cases[i].rhs);
+ Version lhs(cases[i].lhs);
+ Version rhs(cases[i].rhs);
EXPECT_EQ(lhs.CompareTo(rhs), cases[i].expected) <<
cases[i].lhs << " ? " << cases[i].rhs;
@@ -161,7 +152,7 @@ TEST(VersionTest, CompareToWildcardString) {
{"1.2.0.0.0.0", "1.2.*", 0},
};
for (size_t i = 0; i < arraysize(cases); ++i) {
- const base::Version version(cases[i].lhs);
+ const Version version(cases[i].lhs);
const int result = version.CompareToWildcardString(cases[i].rhs);
EXPECT_EQ(result, cases[i].expected) << cases[i].lhs << "?" << cases[i].rhs;
}
@@ -185,7 +176,7 @@ TEST(VersionTest, IsValidWildcardString) {
{"*.2", false},
};
for (size_t i = 0; i < arraysize(cases); ++i) {
- EXPECT_EQ(base::Version::IsValidWildcardString(cases[i].version),
+ EXPECT_EQ(Version::IsValidWildcardString(cases[i].version),
cases[i].expected) << cases[i].version << "?" << cases[i].expected;
}
}
diff --git a/base/win/scoped_comptr.h b/base/win/scoped_comptr.h
index 9442672054..5ce60e2b68 100644
--- a/base/win/scoped_comptr.h
+++ b/base/win/scoped_comptr.h
@@ -51,7 +51,7 @@ class ScopedComPtr : public scoped_refptr<Interface> {
// Explicit Release() of the held object. Useful for reuse of the
// ScopedComPtr instance.
// Note that this function equates to IUnknown::Release and should not
- // be confused with e.g. unique_ptr::release().
+ // be confused with e.g. scoped_ptr::release().
void Release() {
if (this->ptr_ != NULL) {
this->ptr_->Release();
diff --git a/base/win/scoped_handle_test_dll.cc b/base/win/scoped_handle_test_dll.cc
index 0d70c0b627..c72e4592b9 100644
--- a/base/win/scoped_handle_test_dll.cc
+++ b/base/win/scoped_handle_test_dll.cc
@@ -66,7 +66,7 @@ bool InternalRunThreadTest() {
::CloseHandle(ready_event);
if (threads_.size() != kNumThreads) {
- for (auto* thread : threads_)
+ for (const auto& thread : threads_)
::CloseHandle(thread);
::CloseHandle(start_event);
return false;
@@ -74,7 +74,7 @@ bool InternalRunThreadTest() {
::SetEvent(start_event);
::CloseHandle(start_event);
- for (auto* thread : threads_) {
+ for (const auto& thread : threads_) {
::WaitForSingleObject(thread, INFINITE);
::CloseHandle(thread);
}
diff --git a/base/win/scoped_hdc.h b/base/win/scoped_hdc.h
index 890e34a82c..fa686dd050 100644
--- a/base/win/scoped_hdc.h
+++ b/base/win/scoped_hdc.h
@@ -7,7 +7,6 @@
#include <windows.h>
-#include "base/debug/gdi_debug_util_win.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/win/scoped_handle.h"
@@ -29,8 +28,7 @@ class ScopedGetDC {
// If GetDC(NULL) returns NULL, something really bad has happened, like
// GDI handle exhaustion. In this case Chrome is going to behave badly no
// matter what, so we may as well just force a crash now.
- if (!hdc_)
- base::debug::CollectGDIUsageAndDie();
+ CHECK(hdc_);
}
}
diff --git a/components/timers/alarm_timer_chromeos.cc b/components/timers/alarm_timer_chromeos.cc
index 601b411bd7..3f1abbfbc1 100644
--- a/components/timers/alarm_timer_chromeos.cc
+++ b/components/timers/alarm_timer_chromeos.cc
@@ -6,146 +6,450 @@
#include <stdint.h>
#include <sys/timerfd.h>
-
-#include <algorithm>
#include <utility>
#include "base/bind.h"
-#include "base/debug/task_annotator.h"
+#include "base/bind_helpers.h"
#include "base/files/file_util.h"
+#include "base/lazy_instance.h"
#include "base/logging.h"
-#include "base/memory/ptr_util.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
#include "base/pending_task.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
namespace timers {
+namespace {
+// This class represents the IO thread that the AlarmTimer::Delegate may use for
+// watching file descriptors if it gets called from a thread that does not have
+// a MessageLoopForIO. It is a lazy global instance because it may not always
+// be necessary.
+class RtcAlarmIOThread : public base::Thread {
+ public:
+ RtcAlarmIOThread() : Thread("RTC Alarm IO Thread") {
+ CHECK(
+ StartWithOptions(base::Thread::Options(base::MessageLoop::TYPE_IO, 0)));
+ }
+ ~RtcAlarmIOThread() override { Stop(); }
+};
+
+base::LazyInstance<RtcAlarmIOThread> g_io_thread = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+// Watches a MessageLoop and runs a callback if that MessageLoop will be
+// destroyed.
+class AlarmTimer::MessageLoopObserver
+ : public base::MessageLoop::DestructionObserver {
+ public:
+ // Constructs a MessageLoopObserver that will observe |message_loop| and will
+ // call |on_will_be_destroyed_callback| when |message_loop| is about to be
+ // destroyed.
+ MessageLoopObserver(base::MessageLoop* message_loop,
+ base::Closure on_will_be_destroyed_callback)
+ : message_loop_(message_loop),
+ on_will_be_destroyed_callback_(on_will_be_destroyed_callback) {
+ DCHECK(message_loop_);
+ message_loop_->AddDestructionObserver(this);
+ }
+
+ ~MessageLoopObserver() override {
+ // If |message_loop_| was destroyed, then this class will have already
+ // unregistered itself. Doing it again will trigger a warning.
+ if (message_loop_)
+ message_loop_->RemoveDestructionObserver(this);
+ }
+
+ // base::MessageLoop::DestructionObserver override.
+ void WillDestroyCurrentMessageLoop() override {
+ message_loop_->RemoveDestructionObserver(this);
+ message_loop_ = NULL;
+
+ on_will_be_destroyed_callback_.Run();
+ }
+
+ private:
+ // The MessageLoop that this class should watch. Is a weak pointer.
+ base::MessageLoop* message_loop_;
+
+ // The callback to run when |message_loop_| will be destroyed.
+ base::Closure on_will_be_destroyed_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessageLoopObserver);
+};
+
+// This class manages a Real Time Clock (RTC) alarm, a feature that is available
+// from linux version 3.11 onwards. It creates a file descriptor for the RTC
+// alarm timer and then watches that file descriptor to see when it can be read
+// without blocking, indicating that the timer has fired.
+//
+// A major problem for this class is that watching file descriptors is only
+// available on a MessageLoopForIO but there is no guarantee the timer is going
+// to be created on one. To get around this, the timer has a dedicated thread
+// with a MessageLoopForIO that posts tasks back to the thread that started the
+// timer.
+class AlarmTimer::Delegate
+ : public base::RefCountedThreadSafe<AlarmTimer::Delegate>,
+ public base::MessageLoopForIO::Watcher {
+ public:
+ // Construct a Delegate for the AlarmTimer. It should be safe to call
+ // |on_timer_fired_callback| multiple times.
+ explicit Delegate(base::Closure on_timer_fired_callback);
+
+ // Returns true if the system timer managed by this delegate is capable of
+ // waking the system from suspend.
+ bool CanWakeFromSuspend();
+
+ // Resets the timer to fire after |delay| has passed. Cancels any
+ // pre-existing delay.
+ void Reset(base::TimeDelta delay);
+
+ // Stops the currently running timer. It should be safe to call this even if
+ // the timer is not running.
+ void Stop();
+
+ // Sets a hook that will be called when the timer fires and a task has been
+ // queued on |origin_task_runner_|. Used by tests to wait until a task is
+ // pending in the MessageLoop.
+ void SetTimerFiredCallbackForTest(base::Closure test_callback);
+
+ // base::MessageLoopForIO::Watcher overrides.
+ void OnFileCanReadWithoutBlocking(int fd) override;
+ void OnFileCanWriteWithoutBlocking(int fd) override;
+
+ private:
+ friend class base::RefCountedThreadSafe<Delegate>;
+ ~Delegate() override;
+
+ // Actually performs the system calls to set up the timer. This must be
+ // called on a MessageLoopForIO.
+ void ResetImpl(base::TimeDelta delay, int reset_sequence_number);
+
+ // Callback that is run when the timer fires. Must be run on
+ // |origin_task_runner_|.
+ void OnTimerFired(int reset_sequence_number);
+
+ // File descriptor associated with the alarm timer.
+ int alarm_fd_;
+
+ // Task runner which initially started the timer.
+ scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_;
+
+ // Callback that should be run when the timer fires.
+ base::Closure on_timer_fired_callback_;
+
+ // Hook used by tests to be notified when the timer has fired and a task has
+ // been queued in the MessageLoop.
+ base::Closure on_timer_fired_callback_for_test_;
+
+ // Manages watching file descriptors.
+ std::unique_ptr<base::MessageLoopForIO::FileDescriptorWatcher> fd_watcher_;
+
+ // The sequence numbers of the last Reset() call handled respectively on
+ // |origin_task_runner_| and on the MessageLoopForIO used for watching the
+ // timer file descriptor. Note that these can be the same MessageLoop.
+ // OnTimerFired() runs |on_timer_fired_callback_| only if the sequence number
+ // it receives from the MessageLoopForIO matches
+ // |origin_reset_sequence_number_|.
+ int origin_reset_sequence_number_;
+ int io_reset_sequence_number_;
+
+ DISALLOW_COPY_AND_ASSIGN(Delegate);
+};
+
+AlarmTimer::Delegate::Delegate(base::Closure on_timer_fired_callback)
+ : alarm_fd_(timerfd_create(CLOCK_REALTIME_ALARM, 0)),
+ on_timer_fired_callback_(on_timer_fired_callback),
+ origin_reset_sequence_number_(0),
+ io_reset_sequence_number_(0) {
+ // The call to timerfd_create above may fail. This is the only indication
+ // that CLOCK_REALTIME_ALARM is not supported on this system.
+ DPLOG_IF(INFO, (alarm_fd_ == -1))
+ << "CLOCK_REALTIME_ALARM not supported on this system";
+}
+
+AlarmTimer::Delegate::~Delegate() {
+ if (alarm_fd_ != -1)
+ close(alarm_fd_);
+}
+
+bool AlarmTimer::Delegate::CanWakeFromSuspend() {
+ return alarm_fd_ != -1;
+}
+
+void AlarmTimer::Delegate::Reset(base::TimeDelta delay) {
+ // Get a task runner for the current message loop. When the timer fires, we
+ // will
+ // post tasks to this proxy to let the parent timer know.
+ origin_task_runner_ = base::ThreadTaskRunnerHandle::Get();
+
+ // Increment the sequence number. Used to invalidate any events that have
+ // been queued but not yet run since the last time Reset() was called.
+ origin_reset_sequence_number_++;
+
+ // Calling timerfd_settime with a zero delay actually clears the timer so if
+ // the user has requested a zero delay timer, we need to handle it
+ // differently. We queue the task here but we still go ahead and call
+ // timerfd_settime with the zero delay anyway to cancel any previous delay
+ // that might have been programmed.
+ if (delay <= base::TimeDelta::FromMicroseconds(0)) {
+ // The timerfd_settime documentation is vague on what happens when it is
+ // passed a negative delay. We can sidestep the issue by ensuring that
+ // the delay is 0.
+ delay = base::TimeDelta::FromMicroseconds(0);
+ origin_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&Delegate::OnTimerFired, scoped_refptr<Delegate>(this),
+ origin_reset_sequence_number_));
+ }
+
+ // Run ResetImpl() on a MessageLoopForIO.
+ if (base::MessageLoopForIO::IsCurrent()) {
+ ResetImpl(delay, origin_reset_sequence_number_);
+ } else {
+ g_io_thread.Pointer()->task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&Delegate::ResetImpl, scoped_refptr<Delegate>(this), delay,
+ origin_reset_sequence_number_));
+ }
+}
+
+void AlarmTimer::Delegate::Stop() {
+ // Stop the RTC from a MessageLoopForIO.
+ if (!base::MessageLoopForIO::IsCurrent()) {
+ g_io_thread.Pointer()->task_runner()->PostTask(
+ FROM_HERE, base::Bind(&Delegate::Stop, scoped_refptr<Delegate>(this)));
+ return;
+ }
+
+ // Stop watching for events.
+ fd_watcher_.reset();
+
+ // Now clear the timer.
+ DCHECK_NE(alarm_fd_, -1);
+#if defined(ANDROID)
+ itimerspec blank_time;
+ memset(&blank_time, 0, sizeof(blank_time));
+#else
+ itimerspec blank_time = {};
+#endif // defined(ANDROID)
+ if (timerfd_settime(alarm_fd_, 0, &blank_time, NULL) < 0)
+ PLOG(ERROR) << "Unable to clear alarm time. Timer may still fire.";
+}
+
+void AlarmTimer::Delegate::OnFileCanReadWithoutBlocking(int fd) {
+ DCHECK_EQ(alarm_fd_, fd);
+
+ // Read from the fd to ack the event.
+ char val[sizeof(uint64_t)];
+ if (!base::ReadFromFD(alarm_fd_, val, sizeof(uint64_t)))
+ PLOG(DFATAL) << "Unable to read from timer file descriptor.";
+
+ // Make sure that the parent timer is informed on the proper message loop.
+ if (origin_task_runner_->RunsTasksOnCurrentThread()) {
+ OnTimerFired(io_reset_sequence_number_);
+ } else {
+ origin_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&Delegate::OnTimerFired, scoped_refptr<Delegate>(this),
+ io_reset_sequence_number_));
+ }
+}
+
+void AlarmTimer::Delegate::OnFileCanWriteWithoutBlocking(int /*fd*/) {
+ NOTREACHED();
+}
+
+void AlarmTimer::Delegate::SetTimerFiredCallbackForTest(
+ base::Closure test_callback) {
+ on_timer_fired_callback_for_test_ = test_callback;
+}
+
+void AlarmTimer::Delegate::ResetImpl(base::TimeDelta delay,
+ int reset_sequence_number) {
+ DCHECK(base::MessageLoopForIO::IsCurrent());
+ DCHECK_NE(alarm_fd_, -1);
+
+ // Store the sequence number in the IO thread variable. When the timer
+ // fires, we will bind this value to the OnTimerFired callback to ensure
+ // that we do the right thing if the timer gets reset.
+ io_reset_sequence_number_ = reset_sequence_number;
+
+ // If we were already watching the fd, this will stop watching it.
+ fd_watcher_.reset(new base::MessageLoopForIO::FileDescriptorWatcher);
+
+ // Start watching the fd to see when the timer fires.
+ if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
+ alarm_fd_, false, base::MessageLoopForIO::WATCH_READ,
+ fd_watcher_.get(), this)) {
+ LOG(ERROR) << "Error while attempting to watch file descriptor for RTC "
+ << "alarm. Timer will not fire.";
+ }
+
+ // Actually set the timer. This will also clear the pre-existing timer, if
+ // any.
+#if defined(ANDROID)
+ itimerspec alarm_time;
+ memset(&alarm_time, 0, sizeof(alarm_time));
+#else
+ itimerspec alarm_time = {};
+#endif // defined(ANDROID)
+ alarm_time.it_value.tv_sec = delay.InSeconds();
+ alarm_time.it_value.tv_nsec =
+ (delay.InMicroseconds() % base::Time::kMicrosecondsPerSecond) *
+ base::Time::kNanosecondsPerMicrosecond;
+ if (timerfd_settime(alarm_fd_, 0, &alarm_time, NULL) < 0)
+ PLOG(ERROR) << "Error while setting alarm time. Timer will not fire";
+}
+
+void AlarmTimer::Delegate::OnTimerFired(int reset_sequence_number) {
+ DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
+
+ // If a test wants to be notified when this function is about to run, then
+ // re-queue this task in the MessageLoop and run the test's callback.
+ if (!on_timer_fired_callback_for_test_.is_null()) {
+ origin_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&Delegate::OnTimerFired, scoped_refptr<Delegate>(this),
+ reset_sequence_number));
+
+ on_timer_fired_callback_for_test_.Run();
+ on_timer_fired_callback_for_test_.Reset();
+ return;
+ }
+
+ // Check to make sure that the timer was not reset in the time between when
+ // this task was queued to run and now. If it was reset, then don't do
+ // anything.
+ if (reset_sequence_number != origin_reset_sequence_number_)
+ return;
+
+ on_timer_fired_callback_.Run();
+}
AlarmTimer::AlarmTimer(bool retain_user_task, bool is_repeating)
: base::Timer(retain_user_task, is_repeating),
- alarm_fd_(timerfd_create(CLOCK_REALTIME_ALARM, 0)),
- weak_factory_(this) {}
+ can_wake_from_suspend_(false),
+ origin_message_loop_(NULL),
+ weak_factory_(this) {
+ Init();
+}
+
+AlarmTimer::AlarmTimer(const tracked_objects::Location& posted_from,
+ base::TimeDelta delay,
+ const base::Closure& user_task,
+ bool is_repeating)
+ : base::Timer(posted_from, delay, user_task, is_repeating),
+ can_wake_from_suspend_(false),
+ origin_message_loop_(NULL),
+ weak_factory_(this) {
+ Init();
+}
AlarmTimer::~AlarmTimer() {
- DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
Stop();
}
-void AlarmTimer::Stop() {
- DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
+void AlarmTimer::SetTimerFiredCallbackForTest(base::Closure test_callback) {
+ delegate_->SetTimerFiredCallbackForTest(test_callback);
+}
+
+void AlarmTimer::Init() {
+ delegate_ = make_scoped_refptr(new AlarmTimer::Delegate(
+ base::Bind(&AlarmTimer::OnTimerFired, weak_factory_.GetWeakPtr())));
+ can_wake_from_suspend_ = delegate_->CanWakeFromSuspend();
+}
+void AlarmTimer::Stop() {
if (!base::Timer::is_running())
return;
- if (!CanWakeFromSuspend()) {
+ if (!can_wake_from_suspend_) {
base::Timer::Stop();
return;
}
- // Cancel any previous callbacks.
- weak_factory_.InvalidateWeakPtrs();
-
+ // Clear the running flag, stop the delegate, and delete the pending task.
base::Timer::set_is_running(false);
- alarm_fd_watcher_.reset();
+ delegate_->Stop();
pending_task_.reset();
+ // Stop watching |origin_message_loop_|.
+ origin_message_loop_ = NULL;
+ message_loop_observer_.reset();
+
if (!base::Timer::retain_user_task())
base::Timer::set_user_task(base::Closure());
}
void AlarmTimer::Reset() {
- DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
- DCHECK(!base::Timer::user_task().is_null());
-
- if (!CanWakeFromSuspend()) {
+ if (!can_wake_from_suspend_) {
base::Timer::Reset();
return;
}
- // Cancel any previous callbacks and stop watching |alarm_fd_|.
- weak_factory_.InvalidateWeakPtrs();
- alarm_fd_watcher_.reset();
-
- // Ensure that the delay is not negative.
- const base::TimeDelta delay =
- std::max(base::TimeDelta(), base::Timer::GetCurrentDelay());
+ DCHECK(!base::Timer::user_task().is_null());
+ DCHECK(!origin_message_loop_ ||
+ origin_message_loop_->task_runner()->RunsTasksOnCurrentThread());
+
+ // Make sure that the timer will stop if the underlying message loop is
+ // destroyed.
+ if (!origin_message_loop_) {
+ origin_message_loop_ = base::MessageLoop::current();
+ message_loop_observer_.reset(new MessageLoopObserver(
+ origin_message_loop_,
+ base::Bind(&AlarmTimer::WillDestroyCurrentMessageLoop,
+ weak_factory_.GetWeakPtr())));
+ }
// Set up the pending task.
- base::Timer::set_desired_run_time(
- delay.is_zero() ? base::TimeTicks() : base::TimeTicks::Now() + delay);
- pending_task_ = base::MakeUnique<base::PendingTask>(
- base::Timer::posted_from(), base::Timer::user_task(),
- base::Timer::desired_run_time(), true /* nestable */);
-
- // Set |alarm_fd_| to be signaled when the delay expires. If the delay is
- // zero, |alarm_fd_| will never be signaled. This overrides the previous
- // delay, if any.
- itimerspec alarm_time = {};
- alarm_time.it_value.tv_sec = delay.InSeconds();
- alarm_time.it_value.tv_nsec =
- (delay.InMicroseconds() % base::Time::kMicrosecondsPerSecond) *
- base::Time::kNanosecondsPerMicrosecond;
- if (timerfd_settime(alarm_fd_, 0, &alarm_time, NULL) < 0)
- PLOG(ERROR) << "Error while setting alarm time. Timer will not fire";
-
- // The timer is running.
- base::Timer::set_is_running(true);
-
- // If the delay is zero, post the task now.
- if (delay.is_zero()) {
- origin_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&AlarmTimer::OnTimerFired, weak_factory_.GetWeakPtr()));
+ if (base::Timer::GetCurrentDelay() > base::TimeDelta::FromMicroseconds(0)) {
+ base::Timer::set_desired_run_time(base::TimeTicks::Now() +
+ base::Timer::GetCurrentDelay());
+ pending_task_.reset(new base::PendingTask(
+ base::Timer::posted_from(), base::Timer::user_task(),
+ base::Timer::desired_run_time(), true /* nestable */));
} else {
- // Otherwise, if the delay is not zero, generate a tracing event to indicate
- // that the task was posted and watch |alarm_fd_|.
- base::debug::TaskAnnotator().DidQueueTask("AlarmTimer::Reset",
- *pending_task_);
- alarm_fd_watcher_ = base::FileDescriptorWatcher::WatchReadable(
- alarm_fd_, base::Bind(&AlarmTimer::OnAlarmFdReadableWithoutBlocking,
- weak_factory_.GetWeakPtr()));
+ base::Timer::set_desired_run_time(base::TimeTicks());
+ pending_task_.reset(new base::PendingTask(base::Timer::posted_from(),
+ base::Timer::user_task()));
}
-}
+ base::MessageLoop::current()->task_annotator()->DidQueueTask(
+ "AlarmTimer::Reset", *pending_task_);
-void AlarmTimer::OnAlarmFdReadableWithoutBlocking() {
- DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
- DCHECK(base::Timer::IsRunning());
-
- // Read from |alarm_fd_| to ack the event.
- char val[sizeof(uint64_t)];
- if (!base::ReadFromFD(alarm_fd_, val, sizeof(uint64_t)))
- PLOG(DFATAL) << "Unable to read from timer file descriptor.";
+ // Now start up the timer.
+ delegate_->Reset(base::Timer::GetCurrentDelay());
+ base::Timer::set_is_running(true);
+}
- OnTimerFired();
+void AlarmTimer::WillDestroyCurrentMessageLoop() {
+ Stop();
}
void AlarmTimer::OnTimerFired() {
- DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
- DCHECK(base::Timer::IsRunning());
+ if (!base::Timer::IsRunning())
+ return;
+
DCHECK(pending_task_.get());
- // Take ownership of the PendingTask to prevent it from being deleted if the
- // AlarmTimer is deleted.
- const auto pending_user_task = std::move(pending_task_);
+ // Take ownership of the pending user task, which is going to be cleared by
+ // the Stop() or Reset() functions below.
+ std::unique_ptr<base::PendingTask> pending_user_task(
+ std::move(pending_task_));
- base::WeakPtr<AlarmTimer> weak_ptr = weak_factory_.GetWeakPtr();
+ // Re-schedule or stop the timer as requested.
+ if (base::Timer::is_repeating())
+ Reset();
+ else
+ Stop();
- // Run the task.
TRACE_TASK_EXECUTION("AlarmTimer::OnTimerFired", *pending_user_task);
- base::debug::TaskAnnotator().RunTask("AlarmTimer::Reset",
- pending_user_task.get());
-
- // If the timer wasn't deleted, stopped or reset by the callback, reset or
- // stop it.
- if (weak_ptr.get()) {
- if (base::Timer::is_repeating())
- Reset();
- else
- Stop();
- }
-}
-bool AlarmTimer::CanWakeFromSuspend() const {
- return alarm_fd_ != -1;
+ // Now run the user task.
+ base::MessageLoop::current()->task_annotator()->RunTask("AlarmTimer::Reset",
+ *pending_user_task);
}
OneShotAlarmTimer::OneShotAlarmTimer() : AlarmTimer(false, false) {
@@ -157,12 +461,25 @@ OneShotAlarmTimer::~OneShotAlarmTimer() {
RepeatingAlarmTimer::RepeatingAlarmTimer() : AlarmTimer(true, true) {
}
+RepeatingAlarmTimer::RepeatingAlarmTimer(
+ const tracked_objects::Location& posted_from,
+ base::TimeDelta delay,
+ const base::Closure& user_task)
+ : AlarmTimer(posted_from, delay, user_task, true) {
+}
+
RepeatingAlarmTimer::~RepeatingAlarmTimer() {
}
SimpleAlarmTimer::SimpleAlarmTimer() : AlarmTimer(true, false) {
}
+SimpleAlarmTimer::SimpleAlarmTimer(const tracked_objects::Location& posted_from,
+ base::TimeDelta delay,
+ const base::Closure& user_task)
+ : AlarmTimer(posted_from, delay, user_task, false) {
+}
+
SimpleAlarmTimer::~SimpleAlarmTimer() {
}
diff --git a/components/timers/alarm_timer_chromeos.h b/components/timers/alarm_timer_chromeos.h
index d861aeeda0..313c9f9a93 100644
--- a/components/timers/alarm_timer_chromeos.h
+++ b/components/timers/alarm_timer_chromeos.h
@@ -7,69 +7,85 @@
#include <memory>
-#include "base/files/file_descriptor_watcher_posix.h"
+#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
-#include "base/threading/sequenced_task_runner_handle.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
namespace base {
+class MessageLoop;
struct PendingTask;
}
namespace timers {
// The class implements a timer that is capable of waking the system up from a
-// suspended state. For example, this is useful for running tasks that are
+// suspended state. For example, this is useful for running tasks that are
// needed for maintaining network connectivity, like sending heartbeat messages.
// Currently, this feature is only available on Chrome OS systems running linux
-// version 3.11 or higher. On all other platforms, the AlarmTimer behaves
+// version 3.11 or higher. On all other platforms, the AlarmTimer behaves
// exactly the same way as a regular Timer.
-//
-// An AlarmTimer instance can only be used from the sequence on which it was
-// instantiated. Start() and Stop() must be called from a thread that supports
-// FileDescriptorWatcher.
class AlarmTimer : public base::Timer {
public:
~AlarmTimer() override;
+ bool can_wake_from_suspend() const { return can_wake_from_suspend_; }
+
+ // Sets a hook that will be called when the timer fires and a task has been
+ // queued on |origin_message_loop_|. Used by tests to wait until a task is
+ // pending in the MessageLoop.
+ void SetTimerFiredCallbackForTest(base::Closure test_callback);
+
// Timer overrides.
void Stop() override;
void Reset() override;
protected:
+ // The constructors for this class are protected because consumers should
+ // instantiate one of the specialized sub-classes defined below instead.
AlarmTimer(bool retain_user_task, bool is_repeating);
+ AlarmTimer(const tracked_objects::Location& posted_from,
+ base::TimeDelta delay,
+ const base::Closure& user_task,
+ bool is_repeating);
private:
- // Called when |alarm_fd_| is readable without blocking. Reads data from
- // |alarm_fd_| and calls OnTimerFired().
- void OnAlarmFdReadableWithoutBlocking();
+ // Common initialization that must be performed by both constructors. This
+ // really should live in a delegated constructor but the way base::Timer's
+ // constructors are written makes it really hard to do so.
+ void Init();
- // Called when the timer fires. Runs the callback.
+ // Will be called by the delegate to indicate that the timer has fired and
+ // that the user task should be run.
void OnTimerFired();
- // Tracks whether the timer has the ability to wake the system up from
- // suspend. This is a runtime check because we won't know if the system
- // supports being woken up from suspend until the constructor actually tries
- // to set it up.
- bool CanWakeFromSuspend() const;
+ // Called when |origin_message_loop_| will be destroyed.
+ void WillDestroyCurrentMessageLoop();
- // Timer file descriptor.
- const int alarm_fd_;
+ // Delegate that will manage actually setting the timer.
+ class Delegate;
+ scoped_refptr<Delegate> delegate_;
- // Watches |alarm_fd_|.
- std::unique_ptr<base::FileDescriptorWatcher::Controller> alarm_fd_watcher_;
+ // Keeps track of the user task we want to run. A new one is constructed
+ // every time Reset() is called.
+ std::unique_ptr<base::PendingTask> pending_task_;
- // Posts tasks to the sequence on which this AlarmTimer was instantiated.
- const scoped_refptr<base::SequencedTaskRunner> origin_task_runner_ =
- base::SequencedTaskRunnerHandle::Get();
+ // Tracks whether the timer has the ability to wake the system up from
+ // suspend. This is a runtime check because we won't know if the system
+ // supports being woken up from suspend until the delegate actually tries to
+ // set it up.
+ bool can_wake_from_suspend_;
- // Keeps track of the user task we want to run. A new one is constructed every
- // time Reset() is called.
- std::unique_ptr<base::PendingTask> pending_task_;
+ // Pointer to the message loop that started the timer. Used to track the
+ // destruction of that message loop.
+ base::MessageLoop* origin_message_loop_;
+
+ // Observes |origin_message_loop_| and informs this class if it will be
+ // destroyed.
+ class MessageLoopObserver;
+ std::unique_ptr<MessageLoopObserver> message_loop_observer_;
- // Used to invalidate pending callbacks.
base::WeakPtrFactory<AlarmTimer> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(AlarmTimer);
@@ -80,6 +96,8 @@ class AlarmTimer : public base::Timer {
// repeat. Useful for fire-and-forget tasks.
class OneShotAlarmTimer : public AlarmTimer {
public:
+ // Constructs a basic OneShotAlarmTimer. An AlarmTimer constructed this way
+ // requires that Start() is called before Reset() is called.
OneShotAlarmTimer();
~OneShotAlarmTimer() override;
};
@@ -90,7 +108,18 @@ class OneShotAlarmTimer : public AlarmTimer {
// after it fires.
class RepeatingAlarmTimer : public AlarmTimer {
public:
+ // Constructs a basic RepeatingAlarmTimer. An AlarmTimer constructed this way
+ // requires that Start() is called before Reset() is called.
RepeatingAlarmTimer();
+
+ // Constructs a RepeatingAlarmTimer with pre-populated parameters but does not
+ // start it. Useful if |user_task| or |delay| are not going to change.
+ // Reset() can be called immediately after constructing an AlarmTimer in this
+ // way.
+ RepeatingAlarmTimer(const tracked_objects::Location& posted_from,
+ base::TimeDelta delay,
+ const base::Closure& user_task);
+
~RepeatingAlarmTimer() override;
};
@@ -99,7 +128,18 @@ class RepeatingAlarmTimer : public AlarmTimer {
// times but not at a regular interval.
class SimpleAlarmTimer : public AlarmTimer {
public:
+ // Constructs a basic SimpleAlarmTimer. An AlarmTimer constructed this way
+ // requires that Start() is called before Reset() is called.
SimpleAlarmTimer();
+
+ // Constructs a SimpleAlarmTimer with pre-populated parameters but does not
+ // start it. Useful if |user_task| or |delay| are not going to change.
+ // Reset() can be called immediately after constructing an AlarmTimer in this
+ // way.
+ SimpleAlarmTimer(const tracked_objects::Location& posted_from,
+ base::TimeDelta delay,
+ const base::Closure& user_task);
+
~SimpleAlarmTimer() override;
};
diff --git a/crypto/BUILD.gn b/crypto/BUILD.gn
index 6b45c9d633..a912d934c5 100644
--- a/crypto/BUILD.gn
+++ b/crypto/BUILD.gn
@@ -13,11 +13,14 @@ component("crypto") {
"apple_keychain.h",
"apple_keychain_ios.mm",
"apple_keychain_mac.mm",
+ "auto_cbb.h",
"capi_util.cc",
"capi_util.h",
"crypto_export.h",
"cssm_init.cc",
"cssm_init.h",
+ "curve25519.cc",
+ "curve25519.h",
"ec_private_key.cc",
"ec_private_key.h",
"ec_signature_creator.cc",
@@ -44,6 +47,8 @@ component("crypto") {
"nss_util.cc",
"nss_util.h",
"nss_util_internal.h",
+ "openssl_bio_string.cc",
+ "openssl_bio_string.h",
"openssl_util.cc",
"openssl_util.h",
"p224.cc",
@@ -79,10 +84,6 @@ component("crypto") {
"//base/third_party/dynamic_annotations",
]
- public_deps = [
- "//third_party/boringssl",
- ]
-
if (!is_mac && !is_ios) {
sources -= [
"apple_keychain.h",
@@ -132,6 +133,7 @@ component("crypto") {
test("crypto_unittests") {
sources = [
"aead_unittest.cc",
+ "curve25519_unittest.cc",
"ec_private_key_unittest.cc",
"ec_signature_creator_unittest.cc",
"encryptor_unittest.cc",
@@ -139,6 +141,7 @@ test("crypto_unittests") {
"hmac_unittest.cc",
"nss_key_util_unittest.cc",
"nss_util_unittest.cc",
+ "openssl_bio_string_unittest.cc",
"p224_spake_unittest.cc",
"p224_unittest.cc",
"random_unittest.cc",
diff --git a/crypto/apple_keychain.h b/crypto/apple_keychain.h
index 1037b7eccd..1ea2473547 100644
--- a/crypto/apple_keychain.h
+++ b/crypto/apple_keychain.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CRYPTO_APPLE_KEYCHAIN_H_
-#define CRYPTO_APPLE_KEYCHAIN_H_
+#ifndef CRYPTO_KEYCHAIN_MAC_H_
+#define CRYPTO_KEYCHAIN_MAC_H_
#include <Security/Security.h>
@@ -106,4 +106,4 @@ class CRYPTO_EXPORT AppleKeychain {
} // namespace crypto
-#endif // CRYPTO_APPLE_KEYCHAIN_H_
+#endif // CRYPTO_KEYCHAIN_MAC_H_
diff --git a/crypto/auto_cbb.h b/crypto/auto_cbb.h
new file mode 100644
index 0000000000..5206a214f9
--- /dev/null
+++ b/crypto/auto_cbb.h
@@ -0,0 +1,35 @@
+// Copyright 2016 The Chromium 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 CRYPTO_AUTO_CBB_H_
+#define CRYPTO_AUTO_CBB_H_
+
+#include <openssl/bytestring.h>
+
+#include "base/macros.h"
+
+namespace crypto {
+
+// AutoCBB is a wrapper over OpenSSL's CBB type that automatically releases
+// resources when going out of scope.
+class AutoCBB {
+ public:
+ AutoCBB() { CBB_zero(&cbb_); }
+ ~AutoCBB() { CBB_cleanup(&cbb_); }
+
+ CBB* get() { return &cbb_; }
+
+ void Reset() {
+ CBB_cleanup(&cbb_);
+ CBB_zero(&cbb_);
+ }
+
+ private:
+ CBB cbb_;
+ DISALLOW_COPY_AND_ASSIGN(AutoCBB);
+};
+
+} // namespace crypto
+
+#endif // CRYPTO_AUTO_CBB_H_
diff --git a/crypto/crypto.gyp b/crypto/crypto.gyp
new file mode 100644
index 0000000000..8ed2ab2ac9
--- /dev/null
+++ b/crypto/crypto.gyp
@@ -0,0 +1,236 @@
+# 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.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'includes': [
+ 'crypto.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'crypto',
+ 'type': '<(component)',
+ 'product_name': 'crcrypto', # Avoid colliding with OpenSSL's libcrypto
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ '../third_party/boringssl/boringssl.gyp:boringssl',
+ ],
+ 'defines': [
+ 'CRYPTO_IMPLEMENTATION',
+ ],
+ 'conditions': [
+ [ 'os_posix == 1 and OS != "mac" and OS != "ios" and OS != "android"', {
+ 'dependencies': [
+ '../build/linux/system.gyp:nss',
+ ],
+ 'export_dependent_settings': [
+ '../build/linux/system.gyp:nss',
+ ],
+ 'conditions': [
+ [ 'chromeos==1', {
+ 'sources/': [ ['include', '_chromeos\\.cc$'] ]
+ },
+ ],
+ ],
+ }],
+ [ 'OS != "mac" and OS != "ios"', {
+ 'sources!': [
+ 'apple_keychain.h',
+ 'mock_apple_keychain.cc',
+ 'mock_apple_keychain.h',
+ ],
+ }],
+ [ 'os_bsd==1', {
+ 'link_settings': {
+ 'libraries': [
+ '-L/usr/local/lib -lexecinfo',
+ ],
+ },
+ },
+ ],
+ [ 'OS == "mac"', {
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/Security.framework',
+ ],
+ },
+ }, { # OS != "mac"
+ 'sources!': [
+ 'cssm_init.cc',
+ 'cssm_init.h',
+ 'mac_security_services_lock.cc',
+ 'mac_security_services_lock.h',
+ ],
+ }],
+ [ 'OS != "win"', {
+ 'sources!': [
+ 'capi_util.h',
+ 'capi_util.cc',
+ ],
+ }],
+ [ 'OS == "win"', {
+ 'msvs_disabled_warnings': [
+ 4267, # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+ ],
+ }],
+ [ 'use_nss_certs==0', {
+ # Some files are built when NSS is used for the platform certificate library.
+ 'sources!': [
+ 'nss_key_util.cc',
+ 'nss_key_util.h',
+ 'nss_util.cc',
+ 'nss_util.h',
+ 'nss_util_internal.h',
+ ],
+ },],
+ ],
+ 'sources': [
+ '<@(crypto_sources)',
+ ],
+ },
+ {
+ 'target_name': 'crypto_unittests',
+ 'type': 'executable',
+ 'sources': [
+ 'aead_unittest.cc',
+ 'curve25519_unittest.cc',
+ 'ec_private_key_unittest.cc',
+ 'ec_signature_creator_unittest.cc',
+ 'encryptor_unittest.cc',
+ 'hkdf_unittest.cc',
+ 'hmac_unittest.cc',
+ 'nss_key_util_unittest.cc',
+ 'nss_util_unittest.cc',
+ 'openssl_bio_string_unittest.cc',
+ 'p224_unittest.cc',
+ 'p224_spake_unittest.cc',
+ 'random_unittest.cc',
+ 'rsa_private_key_unittest.cc',
+ 'secure_hash_unittest.cc',
+ 'sha2_unittest.cc',
+ 'signature_creator_unittest.cc',
+ 'signature_verifier_unittest.cc',
+ 'symmetric_key_unittest.cc',
+ ],
+ 'dependencies': [
+ 'crypto',
+ 'crypto_test_support',
+ '../base/base.gyp:base',
+ '../base/base.gyp:run_all_unittests',
+ '../base/base.gyp:test_support_base',
+ '../testing/gmock.gyp:gmock',
+ '../testing/gtest.gyp:gtest',
+ '../third_party/boringssl/boringssl.gyp:boringssl',
+ ],
+ 'conditions': [
+ [ 'use_nss_certs == 1', {
+ 'dependencies': [
+ '../build/linux/system.gyp:nss',
+ ],
+ }],
+ [ 'use_nss_certs == 0', {
+ # Some files are built when NSS is used for the platform certificate library.
+ 'sources!': [
+ 'nss_key_util_unittest.cc',
+ 'nss_util_unittest.cc',
+ ],
+ }],
+ [ 'OS == "win"', {
+ # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+ 'msvs_disabled_warnings': [4267, ],
+ }],
+ ],
+ },
+ ],
+ 'conditions': [
+ ['OS == "win" and target_arch=="ia32"', {
+ 'targets': [
+ {
+ 'target_name': 'crypto_nacl_win64',
+ # We use the native APIs for the helper.
+ 'type': '<(component)',
+ 'dependencies': [
+ '../base/base.gyp:base_win64',
+ '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations_win64',
+ '../third_party/boringssl/boringssl.gyp:boringssl_nacl_win64',
+ ],
+ 'sources': [
+ '<@(nacl_win64_sources)',
+ ],
+ 'defines': [
+ 'CRYPTO_IMPLEMENTATION',
+ '<@(nacl_win64_defines)',
+ ],
+ 'configurations': {
+ 'Common_Base': {
+ 'msvs_target_platform': 'x64',
+ },
+ },
+ },
+ ],
+ }],
+ ['use_nss_certs==1', {
+ 'targets': [
+ {
+ 'target_name': 'crypto_test_support',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ 'crypto',
+ ],
+ 'sources': [
+ 'scoped_test_nss_db.cc',
+ 'scoped_test_nss_db.h',
+ 'scoped_test_nss_chromeos_user.cc',
+ 'scoped_test_nss_chromeos_user.h',
+ 'scoped_test_system_nss_key_slot.cc',
+ 'scoped_test_system_nss_key_slot.h',
+ ],
+ 'conditions': [
+ ['use_nss_certs==0', {
+ 'sources!': [
+ 'scoped_test_nss_db.cc',
+ 'scoped_test_nss_db.h',
+ ],
+ }],
+ [ 'chromeos==0', {
+ 'sources!': [
+ 'scoped_test_nss_chromeos_user.cc',
+ 'scoped_test_nss_chromeos_user.h',
+ 'scoped_test_system_nss_key_slot.cc',
+ 'scoped_test_system_nss_key_slot.h',
+ ],
+ }],
+ ],
+ }
+ ]}, { # use_nss_certs==0
+ 'targets': [
+ {
+ 'target_name': 'crypto_test_support',
+ 'type': 'none',
+ 'sources': [],
+ }
+ ]}],
+ ['test_isolation_mode != "noop"', {
+ 'targets': [
+ {
+ 'target_name': 'crypto_unittests_run',
+ 'type': 'none',
+ 'dependencies': [
+ 'crypto_unittests',
+ ],
+ 'includes': [
+ '../build/isolate.gypi',
+ ],
+ 'sources': [
+ 'crypto_unittests.isolate',
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/crypto/crypto.gypi b/crypto/crypto.gypi
new file mode 100644
index 0000000000..dadc0ea2fe
--- /dev/null
+++ b/crypto/crypto.gypi
@@ -0,0 +1,88 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'variables': {
+ # Put all transitive dependencies for Windows HMAC here.
+ # This is required so that we can build them for nacl win64.
+ 'variables': {
+ 'hmac_win64_related_sources': [
+ 'crypto_export.h',
+ 'hmac.cc',
+ 'hmac.h',
+ 'openssl_util.cc',
+ 'openssl_util.h',
+ 'secure_util.cc',
+ 'secure_util.h',
+ 'symmetric_key.cc',
+ 'symmetric_key.h',
+ ],
+ },
+ 'crypto_sources': [
+ # NOTE: all transitive dependencies of HMAC on windows need
+ # to be placed in the source list above.
+ '<@(hmac_win64_related_sources)',
+ 'aead.cc',
+ 'aead.h',
+ 'apple_keychain.h',
+ 'apple_keychain_ios.mm',
+ 'apple_keychain_mac.mm',
+ 'auto_cbb.h',
+ 'capi_util.cc',
+ 'capi_util.h',
+ 'cssm_init.cc',
+ 'cssm_init.h',
+ 'curve25519.cc',
+ 'curve25519.h',
+ 'ec_private_key.cc',
+ 'ec_private_key.h',
+ 'ec_signature_creator.cc',
+ 'ec_signature_creator.h',
+ 'ec_signature_creator_impl.cc',
+ 'ec_signature_creator_impl.h',
+ 'encryptor.cc',
+ 'encryptor.h',
+ 'hkdf.cc',
+ 'hkdf.h',
+ 'mac_security_services_lock.cc',
+ 'mac_security_services_lock.h',
+ 'mock_apple_keychain.cc',
+ 'mock_apple_keychain.h',
+ 'mock_apple_keychain_ios.cc',
+ 'mock_apple_keychain_mac.cc',
+ 'p224_spake.cc',
+ 'p224_spake.h',
+ 'nss_crypto_module_delegate.h',
+ 'nss_key_util.cc',
+ 'nss_key_util.h',
+ 'nss_util.cc',
+ 'nss_util.h',
+ 'nss_util_internal.h',
+ 'openssl_bio_string.cc',
+ 'openssl_bio_string.h',
+ 'p224.cc',
+ 'p224.h',
+ 'random.h',
+ 'random.cc',
+ 'rsa_private_key.cc',
+ 'rsa_private_key.h',
+ 'scoped_capi_types.h',
+ 'scoped_nss_types.h',
+ 'secure_hash.cc',
+ 'secure_hash.h',
+ 'sha2.cc',
+ 'sha2.h',
+ 'signature_creator.cc',
+ 'signature_creator.h',
+ 'signature_verifier.cc',
+ 'signature_verifier.h',
+ 'wincrypt_shim.h',
+ ],
+ 'nacl_win64_sources': [
+ '<@(hmac_win64_related_sources)',
+ 'random.cc',
+ 'random.h',
+ ],
+ }
+}
diff --git a/crypto/crypto_nacl.gyp b/crypto/crypto_nacl.gyp
new file mode 100644
index 0000000000..c7c01a8f3e
--- /dev/null
+++ b/crypto/crypto_nacl.gyp
@@ -0,0 +1,44 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'includes': [
+ '../native_client/build/untrusted.gypi',
+ 'crypto.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'crypto_nacl',
+ 'type': 'none',
+ 'variables': {
+ 'nacl_untrusted_build': 1,
+ 'nlib_target': 'libcrypto_nacl.a',
+ 'build_glibc': 0,
+ 'build_newlib': 0,
+ 'build_pnacl_newlib': 1,
+ },
+ 'dependencies': [
+ '../third_party/boringssl/boringssl_nacl.gyp:boringssl_nacl',
+ '../native_client_sdk/native_client_sdk_untrusted.gyp:nacl_io_untrusted',
+ ],
+ 'defines': [
+ 'CRYPTO_IMPLEMENTATION',
+ ],
+ 'sources': [
+ '<@(crypto_sources)',
+ ],
+ 'sources/': [
+ ['exclude', '_nss\.(cc|h)$'],
+ ['exclude', '^(mock_)?apple_'],
+ ['exclude', '^capi_'],
+ ['exclude', '^cssm_'],
+ ['exclude', '^nss_'],
+ ['exclude', '^mac_'],
+ ],
+ },
+ ],
+}
diff --git a/crypto/crypto_unittests.isolate b/crypto/crypto_unittests.isolate
new file mode 100644
index 0000000000..de13aa23a7
--- /dev/null
+++ b/crypto/crypto_unittests.isolate
@@ -0,0 +1,42 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+{
+ 'variables': {
+ 'command': [
+ '../testing/test_env.py',
+ '<(PRODUCT_DIR)/crypto_unittests<(EXECUTABLE_SUFFIX)',
+ '--brave-new-test-launcher',
+ '--test-launcher-bot-mode',
+ '--asan=<(asan)',
+ '--msan=<(msan)',
+ '--tsan=<(tsan)',
+ ],
+ },
+ 'conditions': [
+ ['OS=="linux" or OS=="mac" or OS=="win"', {
+ 'variables': {
+ 'files': [
+ '../testing/test_env.py',
+ ],
+ },
+ }],
+ ['OS=="mac" and asan==1 and fastbuild==0', {
+ 'variables': {
+ 'files': [
+ '<(PRODUCT_DIR)/crypto_unittests.dSYM/',
+ ],
+ },
+ }],
+ ['OS=="win" and (fastbuild==0 or fastbuild==1)', {
+ 'variables': {
+ 'files': [
+ '<(PRODUCT_DIR)/crypto_unittests.exe.pdb',
+ ],
+ },
+ }],
+ ],
+ 'includes': [
+ '../base/base.isolate',
+ ],
+}
diff --git a/crypto/ec_private_key.h b/crypto/ec_private_key.h
index 432019be5d..a24219bef5 100644
--- a/crypto/ec_private_key.h
+++ b/crypto/ec_private_key.h
@@ -15,7 +15,17 @@
#include "base/macros.h"
#include "build/build_config.h"
#include "crypto/crypto_export.h"
-#include "third_party/boringssl/src/include/openssl/base.h"
+
+#if defined(USE_OPENSSL)
+// Forward declaration for openssl/*.h
+typedef struct evp_pkey_st EVP_PKEY;
+#else
+// Forward declaration.
+typedef struct CERTSubjectPublicKeyInfoStr CERTSubjectPublicKeyInfo;
+typedef struct PK11SlotInfoStr PK11SlotInfo;
+typedef struct SECKEYPrivateKeyStr SECKEYPrivateKey;
+typedef struct SECKEYPublicKeyStr SECKEYPublicKey;
+#endif
namespace crypto {
@@ -41,30 +51,57 @@ class CRYPTO_EXPORT ECPrivateKey {
// Creates a new instance by importing an existing key pair.
// The key pair is given as an ASN.1-encoded PKCS #8 EncryptedPrivateKeyInfo
- // block with empty password and an X.509 SubjectPublicKeyInfo block.
+ // block and an X.509 SubjectPublicKeyInfo block.
// Returns nullptr if initialization fails.
//
// This function is deprecated. Use CreateFromPrivateKeyInfo for new code.
// See https://crbug.com/603319.
static std::unique_ptr<ECPrivateKey> CreateFromEncryptedPrivateKeyInfo(
+ const std::string& password,
const std::vector<uint8_t>& encrypted_private_key_info,
const std::vector<uint8_t>& subject_public_key_info);
+#if !defined(USE_OPENSSL)
+ // Imports the key pair into |slot| and returns in |public_key| and |key|.
+ // Shortcut for code that needs to keep a reference directly to NSS types
+ // without having to create a ECPrivateKey object and make a copy of them.
+ // TODO(mattm): move this function to some NSS util file.
+ static bool ImportFromEncryptedPrivateKeyInfo(
+ PK11SlotInfo* slot,
+ const std::string& password,
+ const uint8_t* encrypted_private_key_info,
+ size_t encrypted_private_key_info_len,
+ CERTSubjectPublicKeyInfo* decoded_spki,
+ bool permanent,
+ bool sensitive,
+ SECKEYPrivateKey** key,
+ SECKEYPublicKey** public_key);
+#endif
+
// Returns a copy of the object.
std::unique_ptr<ECPrivateKey> Copy() const;
- EVP_PKEY* key() { return key_.get(); }
+#if defined(USE_OPENSSL)
+ EVP_PKEY* key() { return key_; }
+#else
+ SECKEYPrivateKey* key() { return key_; }
+ SECKEYPublicKey* public_key() { return public_key_; }
+#endif
// Exports the private key to a PKCS #8 PrivateKeyInfo block.
bool ExportPrivateKey(std::vector<uint8_t>* output) const;
// Exports the private key as an ASN.1-encoded PKCS #8 EncryptedPrivateKeyInfo
- // block wth empty password. This was historically used as a workaround for
- // NSS API deficiencies and does not provide security.
+ // block and the public key as an X.509 SubjectPublicKeyInfo block.
+ // The |password| and |iterations| are used as inputs to the key derivation
+ // function for generating the encryption key. PKCS #5 recommends a minimum
+ // of 1000 iterations, on modern systems a larger value may be preferrable.
//
// This function is deprecated. Use ExportPrivateKey for new code. See
// https://crbug.com/603319.
- bool ExportEncryptedPrivateKey(std::vector<uint8_t>* output) const;
+ bool ExportEncryptedPrivateKey(const std::string& password,
+ int iterations,
+ std::vector<uint8_t>* output) const;
// Exports the public key to an X.509 SubjectPublicKeyInfo block.
bool ExportPublicKey(std::vector<uint8_t>* output) const;
@@ -76,7 +113,12 @@ class CRYPTO_EXPORT ECPrivateKey {
// Constructor is private. Use one of the Create*() methods above instead.
ECPrivateKey();
- bssl::UniquePtr<EVP_PKEY> key_;
+#if defined(USE_OPENSSL)
+ EVP_PKEY* key_;
+#else
+ SECKEYPrivateKey* key_;
+ SECKEYPublicKey* public_key_;
+#endif
DISALLOW_COPY_AND_ASSIGN(ECPrivateKey);
};
diff --git a/crypto/ec_signature_creator_impl.h b/crypto/ec_signature_creator_impl.h
index bd06e253a4..21614f8201 100644
--- a/crypto/ec_signature_creator_impl.h
+++ b/crypto/ec_signature_creator_impl.h
@@ -7,8 +7,6 @@
#include <stdint.h>
-#include <vector>
-
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "crypto/ec_signature_creator.h"
diff --git a/crypto/hmac.cc b/crypto/hmac.cc
index bf89e182d9..af5580bb1d 100644
--- a/crypto/hmac.cc
+++ b/crypto/hmac.cc
@@ -7,26 +7,20 @@
#include <stddef.h>
#include <algorithm>
-#include <string>
#include "base/logging.h"
-#include "base/stl_util.h"
-#include "crypto/openssl_util.h"
#include "crypto/secure_util.h"
#include "crypto/symmetric_key.h"
-#include "third_party/boringssl/src/include/openssl/hmac.h"
namespace crypto {
-HMAC::HMAC(HashAlgorithm hash_alg) : hash_alg_(hash_alg), initialized_(false) {
- // Only SHA-1 and SHA-256 hash algorithms are supported now.
- DCHECK(hash_alg_ == SHA1 || hash_alg_ == SHA256);
-}
-
-HMAC::~HMAC() {
- // Zero out key copy.
- key_.assign(key_.size(), 0);
- base::STLClearObject(&key_);
+bool HMAC::Init(SymmetricKey* key) {
+ std::string raw_key;
+ bool result = key->GetRawKey(&raw_key) && Init(raw_key);
+ // Zero out key copy. This might get optimized away, but one can hope.
+ // Using std::string to store key info at all is a larger problem.
+ std::fill(raw_key.begin(), raw_key.end(), 0);
+ return result;
}
size_t HMAC::DigestLength() const {
@@ -41,35 +35,6 @@ size_t HMAC::DigestLength() const {
}
}
-bool HMAC::Init(const unsigned char* key, size_t key_length) {
- // Init must not be called more than once on the same HMAC object.
- DCHECK(!initialized_);
- initialized_ = true;
- key_.assign(key, key + key_length);
- return true;
-}
-
-bool HMAC::Init(SymmetricKey* key) {
- std::string raw_key;
- bool result = key->GetRawKey(&raw_key) && Init(raw_key);
- // Zero out key copy. This might get optimized away, but one can hope.
- // Using std::string to store key info at all is a larger problem.
- std::fill(raw_key.begin(), raw_key.end(), 0);
- return result;
-}
-
-bool HMAC::Sign(const base::StringPiece& data,
- unsigned char* digest,
- size_t digest_length) const {
- DCHECK(initialized_);
-
- ScopedOpenSSLSafeSizeBuffer<EVP_MAX_MD_SIZE> result(digest, digest_length);
- return !!::HMAC(hash_alg_ == SHA1 ? EVP_sha1() : EVP_sha256(), key_.data(),
- key_.size(),
- reinterpret_cast<const unsigned char*>(data.data()),
- data.size(), result.safe_buffer(), nullptr);
-}
-
bool HMAC::Verify(const base::StringPiece& data,
const base::StringPiece& digest) const {
if (digest.size() != DigestLength())
diff --git a/crypto/hmac.h b/crypto/hmac.h
index 24213338cc..ec32ed7cd1 100644
--- a/crypto/hmac.h
+++ b/crypto/hmac.h
@@ -11,7 +11,6 @@
#include <stddef.h>
#include <memory>
-#include <vector>
#include "base/compiler_specific.h"
#include "base/macros.h"
@@ -21,6 +20,7 @@
namespace crypto {
// Simplify the interface and reduce includes by abstracting out the internals.
+struct HMACPlatformData;
class SymmetricKey;
class CRYPTO_EXPORT HMAC {
@@ -86,8 +86,7 @@ class CRYPTO_EXPORT HMAC {
private:
HashAlgorithm hash_alg_;
- bool initialized_;
- std::vector<unsigned char> key_;
+ std::unique_ptr<HMACPlatformData> plat_;
DISALLOW_COPY_AND_ASSIGN(HMAC);
};
diff --git a/crypto/hmac_nss.cc b/crypto/hmac_nss.cc
new file mode 100644
index 0000000000..9d759b5d0c
--- /dev/null
+++ b/crypto/hmac_nss.cc
@@ -0,0 +1,118 @@
+// 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 "crypto/hmac.h"
+
+#include <nss.h>
+#include <pk11pub.h>
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "crypto/nss_util.h"
+#include "crypto/scoped_nss_types.h"
+
+namespace crypto {
+
+struct HMACPlatformData {
+ CK_MECHANISM_TYPE mechanism_;
+ ScopedPK11Slot slot_;
+ ScopedPK11SymKey sym_key_;
+};
+
+HMAC::HMAC(HashAlgorithm hash_alg)
+ : hash_alg_(hash_alg), plat_(new HMACPlatformData()) {
+ // Only SHA-1 and SHA-256 hash algorithms are supported.
+ switch (hash_alg_) {
+ case SHA1:
+ plat_->mechanism_ = CKM_SHA_1_HMAC;
+ break;
+ case SHA256:
+ plat_->mechanism_ = CKM_SHA256_HMAC;
+ break;
+ default:
+ NOTREACHED() << "Unsupported hash algorithm";
+ break;
+ }
+}
+
+HMAC::~HMAC() {
+}
+
+bool HMAC::Init(const unsigned char *key, size_t key_length) {
+ EnsureNSSInit();
+
+ if (plat_->slot_.get()) {
+ // Init must not be called more than twice on the same HMAC object.
+ NOTREACHED();
+ return false;
+ }
+
+ plat_->slot_.reset(PK11_GetInternalSlot());
+ if (!plat_->slot_.get()) {
+ NOTREACHED();
+ return false;
+ }
+
+ SECItem key_item;
+ key_item.type = siBuffer;
+ key_item.data = const_cast<unsigned char*>(key); // NSS API isn't const.
+ key_item.len = key_length;
+
+ plat_->sym_key_.reset(PK11_ImportSymKey(plat_->slot_.get(),
+ plat_->mechanism_,
+ PK11_OriginUnwrap,
+ CKA_SIGN,
+ &key_item,
+ NULL));
+ if (!plat_->sym_key_.get()) {
+ NOTREACHED();
+ return false;
+ }
+
+ return true;
+}
+
+bool HMAC::Sign(const base::StringPiece& data,
+ unsigned char* digest,
+ size_t digest_length) const {
+ if (!plat_->sym_key_.get()) {
+ // Init has not been called before Sign.
+ NOTREACHED();
+ return false;
+ }
+
+ SECItem param = { siBuffer, NULL, 0 };
+ ScopedPK11Context context(PK11_CreateContextBySymKey(plat_->mechanism_,
+ CKA_SIGN,
+ plat_->sym_key_.get(),
+ &param));
+ if (!context.get()) {
+ NOTREACHED();
+ return false;
+ }
+
+ if (PK11_DigestBegin(context.get()) != SECSuccess) {
+ NOTREACHED();
+ return false;
+ }
+
+ if (PK11_DigestOp(context.get(),
+ reinterpret_cast<const unsigned char*>(data.data()),
+ data.length()) != SECSuccess) {
+ NOTREACHED();
+ return false;
+ }
+
+ unsigned int len = 0;
+ if (PK11_DigestFinal(context.get(),
+ digest, &len, digest_length) != SECSuccess) {
+ NOTREACHED();
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace crypto
diff --git a/crypto/nss_crypto_module_delegate.h b/crypto/nss_crypto_module_delegate.h
index cf08f2859f..6c1da68161 100644
--- a/crypto/nss_crypto_module_delegate.h
+++ b/crypto/nss_crypto_module_delegate.h
@@ -35,6 +35,7 @@ class CryptoModuleBlockingPasswordDelegate {
// user entered.
virtual std::string RequestPassword(const std::string& slot_name, bool retry,
bool* cancelled) = 0;
+
};
// Extends CryptoModuleBlockingPasswordDelegate with the ability to return a
diff --git a/crypto/nss_util.cc b/crypto/nss_util.cc
index 5ed2fa0674..96ee060ca7 100644
--- a/crypto/nss_util.cc
+++ b/crypto/nss_util.cc
@@ -15,9 +15,6 @@
#include <memory>
#include <utility>
-#include "base/location.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
#include "crypto/nss_util_internal.h"
#if defined(OS_OPENBSD)
@@ -32,7 +29,6 @@
#include <map>
#include <vector>
-#include "base/base_paths.h"
#include "base/bind.h"
#include "base/cpu.h"
#include "base/debug/alias.h"
@@ -42,16 +38,27 @@
#include "base/files/file_util.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
-#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
#include "base/native_library.h"
-#include "base/path_service.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
-#include "base/synchronization/lock.h"
#include "base/threading/thread_checker.h"
#include "base/threading/thread_restrictions.h"
#include "base/threading/worker_pool.h"
#include "build/build_config.h"
+
+#if !defined(OS_CHROMEOS)
+#include "base/base_paths.h"
+#include "base/path_service.h"
+#endif
+
+// USE_NSS_CERTS means NSS is used for certificates and platform integration.
+// This requires additional support to manage the platform certificate and key
+// stores.
+#if defined(USE_NSS_CERTS)
+#include "base/synchronization/lock.h"
#include "crypto/nss_crypto_module_delegate.h"
+#endif // defined(USE_NSS_CERTS)
namespace crypto {
@@ -81,6 +88,7 @@ std::string GetNSSErrorMessage() {
return result;
}
+#if defined(USE_NSS_CERTS)
#if !defined(OS_CHROMEOS)
base::FilePath GetDefaultConfigDirectory() {
base::FilePath dir;
@@ -126,13 +134,13 @@ char* PKCS11PasswordFunc(PK11SlotInfo* slot, PRBool retry, void* arg) {
retry != PR_FALSE,
&cancelled);
if (cancelled)
- return nullptr;
+ return NULL;
char* result = PORT_Strdup(password.c_str());
password.replace(0, password.size(), password.size(), 0);
return result;
}
- DLOG(ERROR) << "PK11 password requested with nullptr arg";
- return nullptr;
+ DLOG(ERROR) << "PK11 password requested with NULL arg";
+ return NULL;
}
// NSS creates a local cache of the sqlite database if it detects that the
@@ -142,6 +150,10 @@ char* PKCS11PasswordFunc(PK11SlotInfo* slot, PRBool retry, void* arg) {
// the NSS environment variable NSS_SDB_USE_CACHE to "yes" to override NSS's
// detection when database_dir is on NFS. See http://crbug.com/48585.
//
+// TODO(wtc): port this function to other USE_NSS_CERTS platforms. It is
+// defined only for OS_LINUX and OS_OPENBSD simply because the statfs structure
+// is OS-specific.
+//
// Because this function sets an environment variable it must be run before we
// go multi-threaded.
void UseLocalCacheOfNSSDatabaseIfNFS(const base::FilePath& database_dir) {
@@ -166,13 +178,15 @@ void UseLocalCacheOfNSSDatabaseIfNFS(const base::FilePath& database_dir) {
}
}
+#endif // defined(USE_NSS_CERTS)
+
// A singleton to initialize/deinitialize NSPR.
// Separate from the NSS singleton because we initialize NSPR on the UI thread.
// Now that we're leaking the singleton, we could merge back with the NSS
// singleton.
class NSPRInitSingleton {
private:
- friend struct base::LazyInstanceTraitsBase<NSPRInitSingleton>;
+ friend struct base::DefaultLazyInstanceTraits<NSPRInitSingleton>;
NSPRInitSingleton() {
PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
@@ -218,8 +232,8 @@ class ChromeOSUserData {
}
ScopedPK11Slot GetPublicSlot() {
- return ScopedPK11Slot(public_slot_ ? PK11_ReferenceSlot(public_slot_.get())
- : nullptr);
+ return ScopedPK11Slot(
+ public_slot_ ? PK11_ReferenceSlot(public_slot_.get()) : NULL);
}
ScopedPK11Slot GetPrivateSlot(
@@ -264,13 +278,13 @@ class ChromeOSUserData {
};
class ScopedChapsLoadFixup {
- public:
- ScopedChapsLoadFixup();
- ~ScopedChapsLoadFixup();
+ public:
+ ScopedChapsLoadFixup();
+ ~ScopedChapsLoadFixup();
- private:
+ private:
#if defined(COMPONENT_BUILD)
- void* chaps_handle_;
+ void *chaps_handle_;
#endif
};
@@ -346,17 +360,17 @@ class NSSInitSingleton {
DCHECK(!initializing_tpm_token_);
// If EnableTPMTokenForNSS hasn't been called, return false.
if (!tpm_token_enabled_for_nss_) {
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::Bind(callback, false));
+ base::MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(callback, false));
return;
}
// If everything is already initialized, then return true.
// Note that only |tpm_slot_| is checked, since |chaps_module_| could be
- // nullptr in tests while |tpm_slot_| has been set to the test DB.
+ // NULL in tests while |tpm_slot_| has been set to the test DB.
if (tpm_slot_) {
- base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- base::Bind(callback, true));
+ base::MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(callback, true));
return;
}
@@ -368,15 +382,18 @@ class NSSInitSingleton {
if (base::WorkerPool::PostTaskAndReply(
FROM_HERE,
base::Bind(&NSSInitSingleton::InitializeTPMTokenOnWorkerThread,
- system_slot_id, tpm_args_ptr),
+ system_slot_id,
+ tpm_args_ptr),
base::Bind(&NSSInitSingleton::OnInitializedTPMTokenAndSystemSlot,
base::Unretained(this), // NSSInitSingleton is leaky
- callback, base::Passed(&tpm_args)),
- true /* task_is_slow */)) {
+ callback,
+ base::Passed(&tpm_args)),
+ true /* task_is_slow */
+ )) {
initializing_tpm_token_ = true;
} else {
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::Bind(callback, false));
+ base::MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(callback, false));
}
}
@@ -490,7 +507,7 @@ class NSSInitSingleton {
"%s %s", kUserNSSDatabaseName, username_hash.c_str());
ScopedPK11Slot public_slot(OpenPersistentNSSDBForPath(db_name, path));
chromeos_user_map_[username_hash] =
- base::MakeUnique<ChromeOSUserData>(std::move(public_slot));
+ new ChromeOSUserData(std::move(public_slot));
return true;
}
@@ -527,12 +544,15 @@ class NSSInitSingleton {
TPMModuleAndSlot* tpm_args_ptr = tpm_args.get();
base::WorkerPool::PostTaskAndReply(
FROM_HERE,
- base::Bind(&NSSInitSingleton::InitializeTPMTokenOnWorkerThread, slot_id,
+ base::Bind(&NSSInitSingleton::InitializeTPMTokenOnWorkerThread,
+ slot_id,
tpm_args_ptr),
base::Bind(&NSSInitSingleton::OnInitializedTPMForChromeOSUser,
base::Unretained(this), // NSSInitSingleton is leaky
- username_hash, base::Passed(&tpm_args)),
- true /* task_is_slow */);
+ username_hash,
+ base::Passed(&tpm_args)),
+ true /* task_is_slow */
+ );
}
void OnInitializedTPMForChromeOSUser(
@@ -581,7 +601,7 @@ class NSSInitSingleton {
if (username_hash.empty()) {
DVLOG(2) << "empty username_hash";
if (!callback.is_null()) {
- base::ThreadTaskRunnerHandle::Get()->PostTask(
+ base::MessageLoop::current()->PostTask(
FROM_HERE, base::Bind(callback, base::Passed(ScopedPK11Slot())));
}
return ScopedPK11Slot();
@@ -594,14 +614,15 @@ class NSSInitSingleton {
void CloseChromeOSUserForTesting(const std::string& username_hash) {
DCHECK(thread_checker_.CalledOnValidThread());
- auto i = chromeos_user_map_.find(username_hash);
+ ChromeOSUserMap::iterator i = chromeos_user_map_.find(username_hash);
DCHECK(i != chromeos_user_map_.end());
+ delete i->second;
chromeos_user_map_.erase(i);
}
void SetSystemKeySlotForTesting(ScopedPK11Slot slot) {
// Ensure that a previous value of test_system_slot_ is not overwritten.
- // Unsetting, i.e. setting a nullptr, however is allowed.
+ // Unsetting, i.e. setting a NULL, however is allowed.
DCHECK(!slot || !test_system_slot_);
test_system_slot_ = std::move(slot);
if (test_system_slot_) {
@@ -637,7 +658,7 @@ class NSSInitSingleton {
// TODO(mattm): chromeos::TPMTokenloader always calls
// InitializeTPMTokenAndSystemSlot with slot 0. If the system slot is
// disabled, tpm_slot_ will be the first user's slot instead. Can that be
- // detected and return nullptr instead?
+ // detected and return NULL instead?
base::Closure wrapped_callback;
if (!callback.is_null()) {
@@ -652,18 +673,20 @@ class NSSInitSingleton {
}
#endif
+#if defined(USE_NSS_CERTS)
base::Lock* write_lock() {
return &write_lock_;
}
+#endif // defined(USE_NSS_CERTS)
private:
- friend struct base::LazyInstanceTraitsBase<NSSInitSingleton>;
+ friend struct base::DefaultLazyInstanceTraits<NSSInitSingleton>;
NSSInitSingleton()
: tpm_token_enabled_for_nss_(false),
initializing_tpm_token_(false),
- chaps_module_(nullptr),
- root_(nullptr) {
+ chaps_module_(NULL),
+ root_(NULL) {
// It's safe to construct on any thread, since LazyInstance will prevent any
// other threads from accessing until the constructor is done.
thread_checker_.DetachFromThread();
@@ -686,52 +709,72 @@ class NSSInitSingleton {
}
SECStatus status = SECFailure;
- base::FilePath database_dir = GetInitialConfigDirectory();
- if (!database_dir.empty()) {
- // This duplicates the work which should have been done in
- // EarlySetupForNSSInit. However, this function is idempotent so
- // there's no harm done.
- UseLocalCacheOfNSSDatabaseIfNFS(database_dir);
-
- // Initialize with a persistent database (likely, ~/.pki/nssdb).
- // Use "sql:" which can be shared by multiple processes safely.
- std::string nss_config_dir =
- base::StringPrintf("sql:%s", database_dir.value().c_str());
+ bool nodb_init = false;
+
+#if !defined(USE_NSS_CERTS)
+ // Use the system certificate store, so initialize NSS without database.
+ nodb_init = true;
+#endif
+
+ if (nodb_init) {
+ status = NSS_NoDB_Init(NULL);
+ if (status != SECSuccess) {
+ CrashOnNSSInitFailure();
+ return;
+ }
+#if defined(OS_IOS)
+ root_ = InitDefaultRootCerts();
+#endif // defined(OS_IOS)
+ } else {
+#if defined(USE_NSS_CERTS)
+ base::FilePath database_dir = GetInitialConfigDirectory();
+ if (!database_dir.empty()) {
+ // This duplicates the work which should have been done in
+ // EarlySetupForNSSInit. However, this function is idempotent so
+ // there's no harm done.
+ UseLocalCacheOfNSSDatabaseIfNFS(database_dir);
+
+ // Initialize with a persistent database (likely, ~/.pki/nssdb).
+ // Use "sql:" which can be shared by multiple processes safely.
+ std::string nss_config_dir =
+ base::StringPrintf("sql:%s", database_dir.value().c_str());
#if defined(OS_CHROMEOS)
- status = NSS_Init(nss_config_dir.c_str());
+ status = NSS_Init(nss_config_dir.c_str());
#else
- status = NSS_InitReadWrite(nss_config_dir.c_str());
+ status = NSS_InitReadWrite(nss_config_dir.c_str());
#endif
- if (status != SECSuccess) {
- LOG(ERROR) << "Error initializing NSS with a persistent "
- "database (" << nss_config_dir
- << "): " << GetNSSErrorMessage();
+ if (status != SECSuccess) {
+ LOG(ERROR) << "Error initializing NSS with a persistent "
+ "database (" << nss_config_dir
+ << "): " << GetNSSErrorMessage();
+ }
}
- }
- if (status != SECSuccess) {
- VLOG(1) << "Initializing NSS without a persistent database.";
- status = NSS_NoDB_Init(nullptr);
if (status != SECSuccess) {
- CrashOnNSSInitFailure();
- return;
+ VLOG(1) << "Initializing NSS without a persistent database.";
+ status = NSS_NoDB_Init(NULL);
+ if (status != SECSuccess) {
+ CrashOnNSSInitFailure();
+ return;
+ }
}
- }
- PK11_SetPasswordFunc(PKCS11PasswordFunc);
-
- // If we haven't initialized the password for the NSS databases,
- // initialize an empty-string password so that we don't need to
- // log in.
- PK11SlotInfo* slot = PK11_GetInternalKeySlot();
- if (slot) {
- // PK11_InitPin may write to the keyDB, but no other thread can use NSS
- // yet, so we don't need to lock.
- if (PK11_NeedUserInit(slot))
- PK11_InitPin(slot, nullptr, nullptr);
- PK11_FreeSlot(slot);
- }
+ PK11_SetPasswordFunc(PKCS11PasswordFunc);
+
+ // If we haven't initialized the password for the NSS databases,
+ // initialize an empty-string password so that we don't need to
+ // log in.
+ PK11SlotInfo* slot = PK11_GetInternalKeySlot();
+ if (slot) {
+ // PK11_InitPin may write to the keyDB, but no other thread can use NSS
+ // yet, so we don't need to lock.
+ if (PK11_NeedUserInit(slot))
+ PK11_InitPin(slot, NULL, NULL);
+ PK11_FreeSlot(slot);
+ }
- root_ = InitDefaultRootCerts();
+ root_ = InitDefaultRootCerts();
+#endif // defined(USE_NSS_CERTS)
+ }
// Disable MD5 certificate signatures. (They are disabled by default in
// NSS 3.14.)
@@ -745,18 +788,18 @@ class NSSInitSingleton {
// down.
~NSSInitSingleton() {
#if defined(OS_CHROMEOS)
- chromeos_user_map_.clear();
+ STLDeleteValues(&chromeos_user_map_);
#endif
tpm_slot_.reset();
if (root_) {
SECMOD_UnloadUserModule(root_);
SECMOD_DestroyModule(root_);
- root_ = nullptr;
+ root_ = NULL;
}
if (chaps_module_) {
SECMOD_UnloadUserModule(chaps_module_);
SECMOD_DestroyModule(chaps_module_);
- chaps_module_ = nullptr;
+ chaps_module_ = NULL;
}
SECStatus status = NSS_Shutdown();
@@ -769,14 +812,14 @@ class NSSInitSingleton {
// Load nss's built-in root certs.
SECMODModule* InitDefaultRootCerts() {
- SECMODModule* root = LoadModule("Root Certs", "libnssckbi.so", nullptr);
+ SECMODModule* root = LoadModule("Root Certs", "libnssckbi.so", NULL);
if (root)
return root;
// Aw, snap. Can't find/load root cert shared library.
// This will make it hard to talk to anybody via https.
// TODO(mattm): Re-add the NOTREACHED here when crbug.com/310972 is fixed.
- return nullptr;
+ return NULL;
}
// Load the given module for this NSS session.
@@ -792,17 +835,17 @@ class NSSInitSingleton {
// https://bugzilla.mozilla.org/show_bug.cgi?id=642546 was filed
// on NSS codebase to address this.
SECMODModule* module = SECMOD_LoadUserModule(
- const_cast<char*>(modparams.c_str()), nullptr, PR_FALSE);
+ const_cast<char*>(modparams.c_str()), NULL, PR_FALSE);
if (!module) {
LOG(ERROR) << "Error loading " << name << " module into NSS: "
<< GetNSSErrorMessage();
- return nullptr;
+ return NULL;
}
if (!module->loaded) {
LOG(ERROR) << "After loading " << name << ", loaded==false: "
<< GetNSSErrorMessage();
SECMOD_DestroyModule(module);
- return nullptr;
+ return NULL;
}
return module;
}
@@ -815,12 +858,15 @@ class NSSInitSingleton {
crypto::ScopedPK11Slot tpm_slot_;
SECMODModule* root_;
#if defined(OS_CHROMEOS)
- std::map<std::string, std::unique_ptr<ChromeOSUserData>> chromeos_user_map_;
+ typedef std::map<std::string, ChromeOSUserData*> ChromeOSUserMap;
+ ChromeOSUserMap chromeos_user_map_;
ScopedPK11Slot test_system_slot_;
#endif
+#if defined(USE_NSS_CERTS)
// TODO(davidben): When https://bugzilla.mozilla.org/show_bug.cgi?id=564011
// is fixed, we will no longer need the lock.
base::Lock write_lock_;
+#endif // defined(USE_NSS_CERTS)
base::ThreadChecker thread_checker_;
};
@@ -829,6 +875,7 @@ base::LazyInstance<NSSInitSingleton>::Leaky
g_nss_singleton = LAZY_INSTANCE_INITIALIZER;
} // namespace
+#if defined(USE_NSS_CERTS)
ScopedPK11Slot OpenSoftwareNSSDB(const base::FilePath& path,
const std::string& description) {
const std::string modspec =
@@ -838,7 +885,7 @@ ScopedPK11Slot OpenSoftwareNSSDB(const base::FilePath& path,
PK11SlotInfo* db_slot = SECMOD_OpenUserDB(modspec.c_str());
if (db_slot) {
if (PK11_NeedUserInit(db_slot))
- PK11_InitPin(db_slot, nullptr, nullptr);
+ PK11_InitPin(db_slot, NULL, NULL);
} else {
LOG(ERROR) << "Error opening persistent database (" << modspec
<< "): " << GetNSSErrorMessage();
@@ -851,6 +898,7 @@ void EarlySetupForNSSInit() {
if (!database_dir.empty())
UseLocalCacheOfNSSDatabaseIfNFS(database_dir);
}
+#endif
void EnsureNSPRInit() {
g_nspr_singleton.Get();
@@ -868,12 +916,13 @@ bool CheckNSSVersion(const char* version) {
return !!NSS_VersionCheck(version);
}
+#if defined(USE_NSS_CERTS)
base::Lock* GetNSSWriteLock() {
return g_nss_singleton.Get().write_lock();
}
AutoNSSWriteLock::AutoNSSWriteLock() : lock_(GetNSSWriteLock()) {
- // May be nullptr if the lock is not needed in our version of NSS.
+ // May be NULL if the lock is not needed in our version of NSS.
if (lock_)
lock_->Acquire();
}
@@ -893,6 +942,7 @@ AutoSECMODListReadLock::AutoSECMODListReadLock()
AutoSECMODListReadLock::~AutoSECMODListReadLock() {
SECMOD_ReleaseReadLock(lock_);
}
+#endif // defined(USE_NSS_CERTS)
#if defined(OS_CHROMEOS)
ScopedPK11Slot GetSystemNSSKeySlot(
diff --git a/crypto/nss_util.h b/crypto/nss_util.h
index 5c34fc8f07..a8b57ff9f0 100644
--- a/crypto/nss_util.h
+++ b/crypto/nss_util.h
@@ -14,6 +14,7 @@
#include "crypto/crypto_export.h"
namespace base {
+class FilePath;
class Lock;
class Time;
} // namespace base
diff --git a/crypto/nss_util_internal.h b/crypto/nss_util_internal.h
index 080ac1026d..697e376e5a 100644
--- a/crypto/nss_util_internal.h
+++ b/crypto/nss_util_internal.h
@@ -7,8 +7,6 @@
#include <secmodt.h>
-#include <string>
-
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/macros.h"
diff --git a/crypto/openssl_bio_string.cc b/crypto/openssl_bio_string.cc
new file mode 100644
index 0000000000..48805001ef
--- /dev/null
+++ b/crypto/openssl_bio_string.cc
@@ -0,0 +1,77 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/openssl_bio_string.h"
+
+#include <openssl/bio.h>
+#include <string.h>
+
+namespace crypto {
+
+namespace {
+
+int bio_string_write(BIO* bio, const char* data, int len) {
+ reinterpret_cast<std::string*>(bio->ptr)->append(data, len);
+ return len;
+}
+
+int bio_string_puts(BIO* bio, const char* data) {
+ // Note: unlike puts(), BIO_puts does not add a newline.
+ return bio_string_write(bio, data, strlen(data));
+}
+
+long bio_string_ctrl(BIO* bio, int cmd, long num, void* ptr) {
+ std::string* str = reinterpret_cast<std::string*>(bio->ptr);
+ switch (cmd) {
+ case BIO_CTRL_RESET:
+ str->clear();
+ return 1;
+ case BIO_C_FILE_SEEK:
+ return -1;
+ case BIO_C_FILE_TELL:
+ return str->size();
+ case BIO_CTRL_FLUSH:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int bio_string_new(BIO* bio) {
+ bio->ptr = NULL;
+ bio->init = 0;
+ return 1;
+}
+
+int bio_string_free(BIO* bio) {
+ // The string is owned by the caller, so there's nothing to do here.
+ return bio != NULL;
+}
+
+BIO_METHOD bio_string_methods = {
+ // TODO(mattm): Should add some type number too? (bio.h uses 1-24)
+ BIO_TYPE_SOURCE_SINK,
+ "bio_string",
+ bio_string_write,
+ NULL, /* read */
+ bio_string_puts,
+ NULL, /* gets */
+ bio_string_ctrl,
+ bio_string_new,
+ bio_string_free,
+ NULL, /* callback_ctrl */
+};
+
+} // namespace
+
+BIO* BIO_new_string(std::string* out) {
+ BIO* bio = BIO_new(&bio_string_methods);
+ if (!bio)
+ return bio;
+ bio->ptr = out;
+ bio->init = 1;
+ return bio;
+}
+
+} // namespace crypto
diff --git a/crypto/openssl_bio_string.h b/crypto/openssl_bio_string.h
new file mode 100644
index 0000000000..ca46c12de8
--- /dev/null
+++ b/crypto/openssl_bio_string.h
@@ -0,0 +1,29 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRYPTO_OPENSSL_BIO_STRING_H_
+#define CRYPTO_OPENSSL_BIO_STRING_H_
+
+#include <string>
+
+#include "crypto/crypto_export.h"
+
+// From <openssl/bio.h>
+typedef struct bio_st BIO;
+
+namespace crypto {
+
+// Creates a new BIO that can be used with OpenSSL's various output functions,
+// and which will write all output directly into |out|. This is primarily
+// intended as a utility to reduce the amount of copying and separate
+// allocations when performing extensive string modifications or streaming
+// within OpenSSL.
+//
+// Note: |out| must remain valid for the duration of the BIO.
+CRYPTO_EXPORT BIO* BIO_new_string(std::string* out);
+
+} // namespace crypto
+
+#endif // CRYPTO_OPENSSL_BIO_STRING_H_
+
diff --git a/crypto/openssl_bio_string_unittest.cc b/crypto/openssl_bio_string_unittest.cc
new file mode 100644
index 0000000000..9dfa0e70f7
--- /dev/null
+++ b/crypto/openssl_bio_string_unittest.cc
@@ -0,0 +1,63 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/openssl_bio_string.h"
+
+#include <openssl/bio.h>
+
+#include "crypto/scoped_openssl_types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace crypto {
+
+TEST(OpenSSLBIOString, TestWrite) {
+ std::string s;
+ const std::string expected1("a one\nb 2\n");
+ const std::string expected2("c d e f");
+ const std::string expected3("g h i");
+ {
+ ScopedBIO bio(BIO_new_string(&s));
+ ASSERT_TRUE(bio.get());
+
+ EXPECT_EQ(static_cast<int>(expected1.size()),
+ BIO_printf(bio.get(), "a %s\nb %i\n", "one", 2));
+ EXPECT_EQ(expected1, s);
+
+ EXPECT_EQ(1, BIO_flush(bio.get()));
+ EXPECT_EQ(expected1, s);
+
+ EXPECT_EQ(static_cast<int>(expected2.size()),
+ BIO_write(bio.get(), expected2.data(), expected2.size()));
+ EXPECT_EQ(expected1 + expected2, s);
+
+ EXPECT_EQ(static_cast<int>(expected3.size()),
+ BIO_puts(bio.get(), expected3.c_str()));
+ EXPECT_EQ(expected1 + expected2 + expected3, s);
+ }
+ EXPECT_EQ(expected1 + expected2 + expected3, s);
+}
+
+TEST(OpenSSLBIOString, TestReset) {
+ std::string s;
+ const std::string expected1("a b c\n");
+ const std::string expected2("d e f g\n");
+ {
+ ScopedBIO bio(BIO_new_string(&s));
+ ASSERT_TRUE(bio.get());
+
+ EXPECT_EQ(static_cast<int>(expected1.size()),
+ BIO_write(bio.get(), expected1.data(), expected1.size()));
+ EXPECT_EQ(expected1, s);
+
+ EXPECT_EQ(1, BIO_reset(bio.get()));
+ EXPECT_EQ(std::string(), s);
+
+ EXPECT_EQ(static_cast<int>(expected2.size()),
+ BIO_write(bio.get(), expected2.data(), expected2.size()));
+ EXPECT_EQ(expected2, s);
+ }
+ EXPECT_EQ(expected2, s);
+}
+
+} // namespace crypto
diff --git a/crypto/openssl_util.cc b/crypto/openssl_util.cc
index 2349d42db3..78c6cbbb65 100644
--- a/crypto/openssl_util.cc
+++ b/crypto/openssl_util.cc
@@ -14,8 +14,6 @@
#include <stddef.h>
#include <stdint.h>
-#include <string>
-
#include "base/logging.h"
#include "base/strings/string_piece.h"
@@ -49,7 +47,7 @@ void EnsureOpenSSLInit() {
}
void ClearOpenSSLERRStack(const tracked_objects::Location& location) {
- if (DCHECK_IS_ON() && VLOG_IS_ON(1)) {
+ if (logging::DEBUG_MODE && VLOG_IS_ON(1)) {
uint32_t error_num = ERR_peek_error();
if (error_num == 0)
return;
diff --git a/crypto/openssl_util.h b/crypto/openssl_util.h
index 54f06d337f..d608cdeb4b 100644
--- a/crypto/openssl_util.h
+++ b/crypto/openssl_util.h
@@ -15,7 +15,7 @@ namespace crypto {
// Provides a buffer of at least MIN_SIZE bytes, for use when calling OpenSSL's
// SHA256, HMAC, etc functions, adapting the buffer sizing rules to meet those
-// of our base wrapper APIs.
+// of the our base wrapper APIs.
// This allows the library to write directly to the caller's buffer if it is of
// sufficient size, but if not it will write to temporary |min_sized_buffer_|
// of required size and then its content is automatically copied out on
diff --git a/crypto/p224_spake.cc b/crypto/p224_spake.cc
index 7275a45bc6..1574105372 100644
--- a/crypto/p224_spake.cc
+++ b/crypto/p224_spake.cc
@@ -5,14 +5,14 @@
// This code implements SPAKE2, a variant of EKE:
// http://www.di.ens.fr/~pointche/pub.php?reference=AbPo04
-#include "crypto/p224_spake.h"
+#include <crypto/p224_spake.h>
#include <algorithm>
-#include "base/logging.h"
-#include "crypto/p224.h"
-#include "crypto/random.h"
-#include "crypto/secure_util.h"
+#include <base/logging.h>
+#include <crypto/p224.h>
+#include <crypto/random.h>
+#include <crypto/secure_util.h>
namespace {
@@ -27,9 +27,6 @@ namespace {
// #include <openssl/obj_mac.h>
// #include <openssl/sha.h>
//
-// // Silence a presubmit.
-// #define PRINTF printf
-//
// static const char kSeed1[] = "P224 point generation seed (M)";
// static const char kSeed2[] = "P224 point generation seed (N)";
//
@@ -55,7 +52,7 @@ namespace {
// EC_POINT_get_affine_coordinates_GFp(p224, p, &x, &y, NULL);
// char* x_str = BN_bn2hex(&x);
// char* y_str = BN_bn2hex(&y);
-// PRINTF("Found after %u iterations:\n%s\n%s\n", i, x_str, y_str);
+// printf("Found after %u iterations:\n%s\n%s\n", i, x_str, y_str);
// OPENSSL_free(x_str);
// OPENSSL_free(y_str);
// BN_free(&x);
diff --git a/crypto/p224_spake.h b/crypto/p224_spake.h
index b5cc70ae9e..f9a44e70e1 100644
--- a/crypto/p224_spake.h
+++ b/crypto/p224_spake.h
@@ -5,14 +5,12 @@
#ifndef CRYPTO_P224_SPAKE_H_
#define CRYPTO_P224_SPAKE_H_
+#include <crypto/p224.h>
+#include <crypto/sha2.h>
#include <stdint.h>
-#include <string>
-
#include "base/gtest_prod_util.h"
#include "base/strings/string_piece.h"
-#include "crypto/p224.h"
-#include "crypto/sha2.h"
namespace crypto {
diff --git a/crypto/p224_spake_unittest.cc b/crypto/p224_spake_unittest.cc
index 5ecb6fd3b6..3bca430bea 100644
--- a/crypto/p224_spake_unittest.cc
+++ b/crypto/p224_spake_unittest.cc
@@ -127,7 +127,7 @@ TEST(MutualAuth, Fuzz) {
// We'll only be testing small values of i, but we don't want that to bias
// the test coverage. So we disperse the value of i by multiplying by the
- // FNV, 32-bit prime, producing a simplistic PRNG.
+ // FNV, 32-bit prime, producing a poor-man's PRNG.
const uint32_t rand = i * 16777619;
for (unsigned round = 0;; round++) {
diff --git a/crypto/p224_unittest.cc b/crypto/p224_unittest.cc
index 8cfe6e7dc0..faa08ebd36 100644
--- a/crypto/p224_unittest.cc
+++ b/crypto/p224_unittest.cc
@@ -778,8 +778,8 @@ TEST(P224, ExternalToInternalAndBack) {
const std::string external = point.ToString();
ASSERT_EQ(external.size(), 56u);
- EXPECT_EQ(0, memcmp(external.data(), kBasePointExternal,
- sizeof(kBasePointExternal)));
+ EXPECT_TRUE(memcmp(external.data(), kBasePointExternal,
+ sizeof(kBasePointExternal)) == 0);
}
TEST(P224, ScalarBaseMult) {
@@ -789,8 +789,8 @@ TEST(P224, ScalarBaseMult) {
p224::ScalarBaseMult(kNISTTestVectors[i].scalar, &point);
const std::string external = point.ToString();
ASSERT_EQ(external.size(), 56u);
- EXPECT_EQ(0, memcmp(external.data(), kNISTTestVectors[i].affine,
- external.size()));
+ EXPECT_TRUE(memcmp(external.data(), kNISTTestVectors[i].affine,
+ external.size()) == 0);
}
}
@@ -804,9 +804,9 @@ TEST(P224, Addition) {
p224::Negate(b, &minus_b);
p224::Add(a, b, &sum);
- EXPECT_NE(0, memcmp(&sum, &a, sizeof(sum)));
+ EXPECT_TRUE(memcmp(&sum, &a, sizeof(sum)) != 0);
p224::Add(minus_b, sum, &a_again);
- EXPECT_EQ(a_again.ToString(), a.ToString());
+ EXPECT_TRUE(a_again.ToString() == a.ToString());
}
TEST(P224, Infinity) {
@@ -816,7 +816,7 @@ TEST(P224, Infinity) {
// Test that x^0 = ∞.
Point a;
p224::ScalarBaseMult(reinterpret_cast<const uint8_t*>(zeros), &a);
- EXPECT_EQ(0, memcmp(zeros, a.ToString().data(), sizeof(zeros)));
+ EXPECT_TRUE(memcmp(zeros, a.ToString().data(), sizeof(zeros)) == 0);
// We shouldn't allow ∞ to be imported.
EXPECT_FALSE(a.SetFromString(std::string(zeros, sizeof(zeros))));
diff --git a/crypto/random.h b/crypto/random.h
index 61cde80719..002616bd30 100644
--- a/crypto/random.h
+++ b/crypto/random.h
@@ -18,4 +18,4 @@ CRYPTO_EXPORT void RandBytes(void *bytes, size_t length);
}
-#endif // CRYPTO_RANDOM_H_
+#endif
diff --git a/crypto/random_unittest.cc b/crypto/random_unittest.cc
index dfdcfd5077..caee512068 100644
--- a/crypto/random_unittest.cc
+++ b/crypto/random_unittest.cc
@@ -6,8 +6,6 @@
#include <stddef.h>
-#include <string>
-
#include "base/strings/string_util.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/crypto/rsa_private_key.cc b/crypto/rsa_private_key.cc
index 075f5e4041..c546c91ead 100644
--- a/crypto/rsa_private_key.cc
+++ b/crypto/rsa_private_key.cc
@@ -4,110 +4,385 @@
#include "crypto/rsa_private_key.h"
+#include <stddef.h>
#include <stdint.h>
-#include <memory>
-#include <utility>
+#include <algorithm>
#include "base/logging.h"
-#include "crypto/openssl_util.h"
-#include "third_party/boringssl/src/include/openssl/bn.h"
-#include "third_party/boringssl/src/include/openssl/bytestring.h"
-#include "third_party/boringssl/src/include/openssl/evp.h"
-#include "third_party/boringssl/src/include/openssl/mem.h"
-#include "third_party/boringssl/src/include/openssl/rsa.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_util.h"
+
+// This file manually encodes and decodes RSA private keys using PrivateKeyInfo
+// from PKCS #8 and RSAPrivateKey from PKCS #1. These structures are:
+//
+// PrivateKeyInfo ::= SEQUENCE {
+// version Version,
+// privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
+// privateKey PrivateKey,
+// attributes [0] IMPLICIT Attributes OPTIONAL
+// }
+//
+// RSAPrivateKey ::= SEQUENCE {
+// version Version,
+// modulus INTEGER,
+// publicExponent INTEGER,
+// privateExponent INTEGER,
+// prime1 INTEGER,
+// prime2 INTEGER,
+// exponent1 INTEGER,
+// exponent2 INTEGER,
+// coefficient INTEGER
+// }
+
+namespace {
+// Helper for error handling during key import.
+#define READ_ASSERT(truth) \
+ if (!(truth)) { \
+ NOTREACHED(); \
+ return false; \
+ }
+} // namespace
namespace crypto {
-// static
-std::unique_ptr<RSAPrivateKey> RSAPrivateKey::Create(uint16_t num_bits) {
- OpenSSLErrStackTracer err_tracer(FROM_HERE);
+const uint8_t PrivateKeyInfoCodec::kRsaAlgorithmIdentifier[] = {
+ 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86,
+ 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00};
+
+PrivateKeyInfoCodec::PrivateKeyInfoCodec(bool big_endian)
+ : big_endian_(big_endian) {}
+
+PrivateKeyInfoCodec::~PrivateKeyInfoCodec() {}
+
+bool PrivateKeyInfoCodec::Export(std::vector<uint8_t>* output) {
+ std::list<uint8_t> content;
+
+ // Version (always zero)
+ uint8_t version = 0;
+
+ PrependInteger(coefficient_, &content);
+ PrependInteger(exponent2_, &content);
+ PrependInteger(exponent1_, &content);
+ PrependInteger(prime2_, &content);
+ PrependInteger(prime1_, &content);
+ PrependInteger(private_exponent_, &content);
+ PrependInteger(public_exponent_, &content);
+ PrependInteger(modulus_, &content);
+ PrependInteger(&version, 1, &content);
+ PrependTypeHeaderAndLength(kSequenceTag, content.size(), &content);
+ PrependTypeHeaderAndLength(kOctetStringTag, content.size(), &content);
+
+ // RSA algorithm OID
+ for (size_t i = sizeof(kRsaAlgorithmIdentifier); i > 0; --i)
+ content.push_front(kRsaAlgorithmIdentifier[i - 1]);
+
+ PrependInteger(&version, 1, &content);
+ PrependTypeHeaderAndLength(kSequenceTag, content.size(), &content);
- bssl::UniquePtr<RSA> rsa_key(RSA_new());
- bssl::UniquePtr<BIGNUM> bn(BN_new());
- if (!rsa_key.get() || !bn.get() || !BN_set_word(bn.get(), 65537L))
- return nullptr;
+ // Copy everying into the output.
+ output->reserve(content.size());
+ output->assign(content.begin(), content.end());
- if (!RSA_generate_key_ex(rsa_key.get(), num_bits, bn.get(), nullptr))
- return nullptr;
+ return true;
+}
+
+bool PrivateKeyInfoCodec::ExportPublicKeyInfo(std::vector<uint8_t>* output) {
+ // Create a sequence with the modulus (n) and public exponent (e).
+ std::vector<uint8_t> bit_string;
+ if (!ExportPublicKey(&bit_string))
+ return false;
+
+ // Add the sequence as the contents of a bit string.
+ std::list<uint8_t> content;
+ PrependBitString(&bit_string[0], static_cast<int>(bit_string.size()),
+ &content);
+
+ // Add the RSA algorithm OID.
+ for (size_t i = sizeof(kRsaAlgorithmIdentifier); i > 0; --i)
+ content.push_front(kRsaAlgorithmIdentifier[i - 1]);
+
+ // Finally, wrap everything in a sequence.
+ PrependTypeHeaderAndLength(kSequenceTag, content.size(), &content);
+
+ // Copy everything into the output.
+ output->reserve(content.size());
+ output->assign(content.begin(), content.end());
+
+ return true;
+}
+
+bool PrivateKeyInfoCodec::ExportPublicKey(std::vector<uint8_t>* output) {
+ // Create a sequence with the modulus (n) and public exponent (e).
+ std::list<uint8_t> content;
+ PrependInteger(&public_exponent_[0],
+ static_cast<int>(public_exponent_.size()),
+ &content);
+ PrependInteger(&modulus_[0], static_cast<int>(modulus_.size()), &content);
+ PrependTypeHeaderAndLength(kSequenceTag, content.size(), &content);
+
+ // Copy everything into the output.
+ output->reserve(content.size());
+ output->assign(content.begin(), content.end());
+
+ return true;
+}
+
+bool PrivateKeyInfoCodec::Import(const std::vector<uint8_t>& input) {
+ if (input.empty()) {
+ return false;
+ }
+
+ // Parse the private key info up to the public key values, ignoring
+ // the subsequent private key values.
+ uint8_t* src = const_cast<uint8_t*>(&input.front());
+ uint8_t* end = src + input.size();
+ if (!ReadSequence(&src, end) ||
+ !ReadVersion(&src, end) ||
+ !ReadAlgorithmIdentifier(&src, end) ||
+ !ReadTypeHeaderAndLength(&src, end, kOctetStringTag, NULL) ||
+ !ReadSequence(&src, end) ||
+ !ReadVersion(&src, end) ||
+ !ReadInteger(&src, end, &modulus_))
+ return false;
+
+ int mod_size = modulus_.size();
+ READ_ASSERT(mod_size % 2 == 0);
+ int primes_size = mod_size / 2;
+
+ if (!ReadIntegerWithExpectedSize(&src, end, 4, &public_exponent_) ||
+ !ReadIntegerWithExpectedSize(&src, end, mod_size, &private_exponent_) ||
+ !ReadIntegerWithExpectedSize(&src, end, primes_size, &prime1_) ||
+ !ReadIntegerWithExpectedSize(&src, end, primes_size, &prime2_) ||
+ !ReadIntegerWithExpectedSize(&src, end, primes_size, &exponent1_) ||
+ !ReadIntegerWithExpectedSize(&src, end, primes_size, &exponent2_) ||
+ !ReadIntegerWithExpectedSize(&src, end, primes_size, &coefficient_))
+ return false;
+
+ READ_ASSERT(src == end);
+
+
+ return true;
+}
- std::unique_ptr<RSAPrivateKey> result(new RSAPrivateKey);
- result->key_.reset(EVP_PKEY_new());
- if (!result->key_ || !EVP_PKEY_set1_RSA(result->key_.get(), rsa_key.get()))
- return nullptr;
+void PrivateKeyInfoCodec::PrependInteger(const std::vector<uint8_t>& in,
+ std::list<uint8_t>* out) {
+ uint8_t* ptr = const_cast<uint8_t*>(&in.front());
+ PrependIntegerImpl(ptr, in.size(), out, big_endian_);
+}
- return result;
+// Helper to prepend an ASN.1 integer.
+void PrivateKeyInfoCodec::PrependInteger(uint8_t* val,
+ int num_bytes,
+ std::list<uint8_t>* data) {
+ PrependIntegerImpl(val, num_bytes, data, big_endian_);
}
-// static
-std::unique_ptr<RSAPrivateKey> RSAPrivateKey::CreateFromPrivateKeyInfo(
- const std::vector<uint8_t>& input) {
- OpenSSLErrStackTracer err_tracer(FROM_HERE);
+void PrivateKeyInfoCodec::PrependIntegerImpl(uint8_t* val,
+ int num_bytes,
+ std::list<uint8_t>* data,
+ bool big_endian) {
+ // Reverse input if little-endian.
+ std::vector<uint8_t> tmp;
+ if (!big_endian) {
+ tmp.assign(val, val + num_bytes);
+ std::reverse(tmp.begin(), tmp.end());
+ val = &tmp.front();
+ }
- CBS cbs;
- CBS_init(&cbs, input.data(), input.size());
- bssl::UniquePtr<EVP_PKEY> pkey(EVP_parse_private_key(&cbs));
- if (!pkey || CBS_len(&cbs) != 0 || EVP_PKEY_id(pkey.get()) != EVP_PKEY_RSA)
- return nullptr;
+ // ASN.1 integers are unpadded byte arrays, so skip any null padding bytes
+ // from the most-significant end of the integer.
+ int start = 0;
+ while (start < (num_bytes - 1) && val[start] == 0x00) {
+ start++;
+ num_bytes--;
+ }
+ PrependBytes(val, start, num_bytes, data);
- std::unique_ptr<RSAPrivateKey> result(new RSAPrivateKey);
- result->key_ = std::move(pkey);
- return result;
+ // ASN.1 integers are signed. To encode a positive integer whose sign bit
+ // (the most significant bit) would otherwise be set and make the number
+ // negative, ASN.1 requires a leading null byte to force the integer to be
+ // positive.
+ uint8_t front = data->front();
+ if ((front & 0x80) != 0) {
+ data->push_front(0x00);
+ num_bytes++;
+ }
+
+ PrependTypeHeaderAndLength(kIntegerTag, num_bytes, data);
}
-// static
-std::unique_ptr<RSAPrivateKey> RSAPrivateKey::CreateFromKey(EVP_PKEY* key) {
- DCHECK(key);
- if (EVP_PKEY_type(key->type) != EVP_PKEY_RSA)
- return nullptr;
- std::unique_ptr<RSAPrivateKey> copy(new RSAPrivateKey);
- EVP_PKEY_up_ref(key);
- copy->key_.reset(key);
- return copy;
+bool PrivateKeyInfoCodec::ReadInteger(uint8_t** pos,
+ uint8_t* end,
+ std::vector<uint8_t>* out) {
+ return ReadIntegerImpl(pos, end, out, big_endian_);
}
-RSAPrivateKey::RSAPrivateKey() {}
+bool PrivateKeyInfoCodec::ReadIntegerWithExpectedSize(
+ uint8_t** pos,
+ uint8_t* end,
+ size_t expected_size,
+ std::vector<uint8_t>* out) {
+ std::vector<uint8_t> temp;
+ if (!ReadIntegerImpl(pos, end, &temp, true)) // Big-Endian
+ return false;
+
+ int pad = expected_size - temp.size();
+ int index = 0;
+ if (out->size() == expected_size + 1) {
+ READ_ASSERT(out->front() == 0x00);
+ pad++;
+ index++;
+ } else {
+ READ_ASSERT(out->size() <= expected_size);
+ }
-RSAPrivateKey::~RSAPrivateKey() {}
+ out->insert(out->end(), pad, 0x00);
+ out->insert(out->end(), temp.begin(), temp.end());
-std::unique_ptr<RSAPrivateKey> RSAPrivateKey::Copy() const {
- std::unique_ptr<RSAPrivateKey> copy(new RSAPrivateKey);
- bssl::UniquePtr<RSA> rsa(EVP_PKEY_get1_RSA(key_.get()));
- if (!rsa)
- return nullptr;
- copy->key_.reset(EVP_PKEY_new());
- if (!EVP_PKEY_set1_RSA(copy->key_.get(), rsa.get()))
- return nullptr;
- return copy;
+ // Reverse output if little-endian.
+ if (!big_endian_)
+ std::reverse(out->begin(), out->end());
+ return true;
}
-bool RSAPrivateKey::ExportPrivateKey(std::vector<uint8_t>* output) const {
- OpenSSLErrStackTracer err_tracer(FROM_HERE);
- uint8_t *der;
- size_t der_len;
- bssl::ScopedCBB cbb;
- if (!CBB_init(cbb.get(), 0) ||
- !EVP_marshal_private_key(cbb.get(), key_.get()) ||
- !CBB_finish(cbb.get(), &der, &der_len)) {
+bool PrivateKeyInfoCodec::ReadIntegerImpl(uint8_t** pos,
+ uint8_t* end,
+ std::vector<uint8_t>* out,
+ bool big_endian) {
+ uint32_t length = 0;
+ if (!ReadTypeHeaderAndLength(pos, end, kIntegerTag, &length) || !length)
return false;
+
+ // The first byte can be zero to force positiveness. We can ignore this.
+ if (**pos == 0x00) {
+ ++(*pos);
+ --length;
}
- output->assign(der, der + der_len);
- OPENSSL_free(der);
+
+ if (length)
+ out->insert(out->end(), *pos, (*pos) + length);
+
+ (*pos) += length;
+
+ // Reverse output if little-endian.
+ if (!big_endian)
+ std::reverse(out->begin(), out->end());
return true;
}
-bool RSAPrivateKey::ExportPublicKey(std::vector<uint8_t>* output) const {
- OpenSSLErrStackTracer err_tracer(FROM_HERE);
- uint8_t *der;
- size_t der_len;
- bssl::ScopedCBB cbb;
- if (!CBB_init(cbb.get(), 0) ||
- !EVP_marshal_public_key(cbb.get(), key_.get()) ||
- !CBB_finish(cbb.get(), &der, &der_len)) {
+void PrivateKeyInfoCodec::PrependBytes(uint8_t* val,
+ int start,
+ int num_bytes,
+ std::list<uint8_t>* data) {
+ while (num_bytes > 0) {
+ --num_bytes;
+ data->push_front(val[start + num_bytes]);
+ }
+}
+
+void PrivateKeyInfoCodec::PrependLength(size_t size, std::list<uint8_t>* data) {
+ // The high bit is used to indicate whether additional octets are needed to
+ // represent the length.
+ if (size < 0x80) {
+ data->push_front(static_cast<uint8_t>(size));
+ } else {
+ uint8_t num_bytes = 0;
+ while (size > 0) {
+ data->push_front(static_cast<uint8_t>(size & 0xFF));
+ size >>= 8;
+ num_bytes++;
+ }
+ CHECK_LE(num_bytes, 4);
+ data->push_front(0x80 | num_bytes);
+ }
+}
+
+void PrivateKeyInfoCodec::PrependTypeHeaderAndLength(
+ uint8_t type,
+ uint32_t length,
+ std::list<uint8_t>* output) {
+ PrependLength(length, output);
+ output->push_front(type);
+}
+
+void PrivateKeyInfoCodec::PrependBitString(uint8_t* val,
+ int num_bytes,
+ std::list<uint8_t>* output) {
+ // Start with the data.
+ PrependBytes(val, 0, num_bytes, output);
+ // Zero unused bits.
+ output->push_front(0);
+ // Add the length.
+ PrependLength(num_bytes + 1, output);
+ // Finally, add the bit string tag.
+ output->push_front((uint8_t)kBitStringTag);
+}
+
+bool PrivateKeyInfoCodec::ReadLength(uint8_t** pos,
+ uint8_t* end,
+ uint32_t* result) {
+ READ_ASSERT(*pos < end);
+ int length = 0;
+
+ // If the MSB is not set, the length is just the byte itself.
+ if (!(**pos & 0x80)) {
+ length = **pos;
+ (*pos)++;
+ } else {
+ // Otherwise, the lower 7 indicate the length of the length.
+ int length_of_length = **pos & 0x7F;
+ READ_ASSERT(length_of_length <= 4);
+ (*pos)++;
+ READ_ASSERT(*pos + length_of_length < end);
+
+ length = 0;
+ for (int i = 0; i < length_of_length; ++i) {
+ length <<= 8;
+ length |= **pos;
+ (*pos)++;
+ }
+ }
+
+ READ_ASSERT(*pos + length <= end);
+ if (result) *result = length;
+ return true;
+}
+
+bool PrivateKeyInfoCodec::ReadTypeHeaderAndLength(uint8_t** pos,
+ uint8_t* end,
+ uint8_t expected_tag,
+ uint32_t* length) {
+ READ_ASSERT(*pos < end);
+ READ_ASSERT(**pos == expected_tag);
+ (*pos)++;
+
+ return ReadLength(pos, end, length);
+}
+
+bool PrivateKeyInfoCodec::ReadSequence(uint8_t** pos, uint8_t* end) {
+ return ReadTypeHeaderAndLength(pos, end, kSequenceTag, NULL);
+}
+
+bool PrivateKeyInfoCodec::ReadAlgorithmIdentifier(uint8_t** pos, uint8_t* end) {
+ READ_ASSERT(*pos + sizeof(kRsaAlgorithmIdentifier) < end);
+ READ_ASSERT(memcmp(*pos, kRsaAlgorithmIdentifier,
+ sizeof(kRsaAlgorithmIdentifier)) == 0);
+ (*pos) += sizeof(kRsaAlgorithmIdentifier);
+ return true;
+}
+
+bool PrivateKeyInfoCodec::ReadVersion(uint8_t** pos, uint8_t* end) {
+ uint32_t length = 0;
+ if (!ReadTypeHeaderAndLength(pos, end, kIntegerTag, &length))
return false;
+
+ // The version should be zero.
+ for (uint32_t i = 0; i < length; ++i) {
+ READ_ASSERT(**pos == 0x00);
+ (*pos)++;
}
- output->assign(der, der + der_len);
- OPENSSL_free(der);
+
return true;
}
diff --git a/crypto/rsa_private_key.h b/crypto/rsa_private_key.h
index fc4c80c232..d4808f5aae 100644
--- a/crypto/rsa_private_key.h
+++ b/crypto/rsa_private_key.h
@@ -7,17 +7,162 @@
#include <stddef.h>
#include <stdint.h>
-#include <openssl/base.h>
-#include <memory>
+#include <list>
#include <vector>
#include "base/macros.h"
#include "build/build_config.h"
#include "crypto/crypto_export.h"
+#if defined(USE_OPENSSL)
+// Forward declaration for openssl/*.h
+typedef struct evp_pkey_st EVP_PKEY;
+#else
+// Forward declaration.
+typedef struct PK11SlotInfoStr PK11SlotInfo;
+typedef struct SECKEYPrivateKeyStr SECKEYPrivateKey;
+typedef struct SECKEYPublicKeyStr SECKEYPublicKey;
+#endif
+
+
namespace crypto {
+// Used internally by RSAPrivateKey for serializing and deserializing
+// PKCS #8 PrivateKeyInfo and PublicKeyInfo.
+class PrivateKeyInfoCodec {
+ public:
+ // ASN.1 encoding of the AlgorithmIdentifier from PKCS #8.
+ static const uint8_t kRsaAlgorithmIdentifier[];
+
+ // ASN.1 tags for some types we use.
+ static const uint8_t kBitStringTag = 0x03;
+ static const uint8_t kIntegerTag = 0x02;
+ static const uint8_t kNullTag = 0x05;
+ static const uint8_t kOctetStringTag = 0x04;
+ static const uint8_t kSequenceTag = 0x30;
+
+ // |big_endian| here specifies the byte-significance of the integer components
+ // that will be parsed & serialized (modulus(), etc...) during Import(),
+ // Export() and ExportPublicKeyInfo() -- not the ASN.1 DER encoding of the
+ // PrivateKeyInfo/PublicKeyInfo (which is always big-endian).
+ explicit PrivateKeyInfoCodec(bool big_endian);
+
+ ~PrivateKeyInfoCodec();
+
+ // Exports the contents of the integer components to the ASN.1 DER encoding
+ // of the PrivateKeyInfo structure to |output|.
+ bool Export(std::vector<uint8_t>* output);
+
+ // Exports the contents of the integer components to the ASN.1 DER encoding
+ // of the PublicKeyInfo structure to |output|.
+ bool ExportPublicKeyInfo(std::vector<uint8_t>* output);
+
+ // Exports the contents of the integer components to the ASN.1 DER encoding
+ // of the RSAPublicKey structure to |output|.
+ bool ExportPublicKey(std::vector<uint8_t>* output);
+
+ // Parses the ASN.1 DER encoding of the PrivateKeyInfo structure in |input|
+ // and populates the integer components with |big_endian_| byte-significance.
+ // IMPORTANT NOTE: This is currently *not* security-approved for importing
+ // keys from unstrusted sources.
+ bool Import(const std::vector<uint8_t>& input);
+
+ // Accessors to the contents of the integer components of the PrivateKeyInfo
+ // structure.
+ std::vector<uint8_t>* modulus() { return &modulus_; }
+ std::vector<uint8_t>* public_exponent() { return &public_exponent_; }
+ std::vector<uint8_t>* private_exponent() { return &private_exponent_; }
+ std::vector<uint8_t>* prime1() { return &prime1_; }
+ std::vector<uint8_t>* prime2() { return &prime2_; }
+ std::vector<uint8_t>* exponent1() { return &exponent1_; }
+ std::vector<uint8_t>* exponent2() { return &exponent2_; }
+ std::vector<uint8_t>* coefficient() { return &coefficient_; }
+
+ private:
+ // Utility wrappers for PrependIntegerImpl that use the class's |big_endian_|
+ // value.
+ void PrependInteger(const std::vector<uint8_t>& in, std::list<uint8_t>* out);
+ void PrependInteger(uint8_t* val, int num_bytes, std::list<uint8_t>* data);
+
+ // Prepends the integer stored in |val| - |val + num_bytes| with |big_endian|
+ // byte-significance into |data| as an ASN.1 integer.
+ void PrependIntegerImpl(uint8_t* val,
+ int num_bytes,
+ std::list<uint8_t>* data,
+ bool big_endian);
+
+ // Utility wrappers for ReadIntegerImpl that use the class's |big_endian_|
+ // value.
+ bool ReadInteger(uint8_t** pos, uint8_t* end, std::vector<uint8_t>* out);
+ bool ReadIntegerWithExpectedSize(uint8_t** pos,
+ uint8_t* end,
+ size_t expected_size,
+ std::vector<uint8_t>* out);
+
+ // Reads an ASN.1 integer from |pos|, and stores the result into |out| with
+ // |big_endian| byte-significance.
+ bool ReadIntegerImpl(uint8_t** pos,
+ uint8_t* end,
+ std::vector<uint8_t>* out,
+ bool big_endian);
+
+ // Prepends the integer stored in |val|, starting a index |start|, for
+ // |num_bytes| bytes onto |data|.
+ void PrependBytes(uint8_t* val,
+ int start,
+ int num_bytes,
+ std::list<uint8_t>* data);
+
+ // Helper to prepend an ASN.1 length field.
+ void PrependLength(size_t size, std::list<uint8_t>* data);
+
+ // Helper to prepend an ASN.1 type header.
+ void PrependTypeHeaderAndLength(uint8_t type,
+ uint32_t length,
+ std::list<uint8_t>* output);
+
+ // Helper to prepend an ASN.1 bit string
+ void PrependBitString(uint8_t* val,
+ int num_bytes,
+ std::list<uint8_t>* output);
+
+ // Read an ASN.1 length field. This also checks that the length does not
+ // extend beyond |end|.
+ bool ReadLength(uint8_t** pos, uint8_t* end, uint32_t* result);
+
+ // Read an ASN.1 type header and its length.
+ bool ReadTypeHeaderAndLength(uint8_t** pos,
+ uint8_t* end,
+ uint8_t expected_tag,
+ uint32_t* length);
+
+ // Read an ASN.1 sequence declaration. This consumes the type header and
+ // length field, but not the contents of the sequence.
+ bool ReadSequence(uint8_t** pos, uint8_t* end);
+
+ // Read the RSA AlgorithmIdentifier.
+ bool ReadAlgorithmIdentifier(uint8_t** pos, uint8_t* end);
+
+ // Read one of the two version fields in PrivateKeyInfo.
+ bool ReadVersion(uint8_t** pos, uint8_t* end);
+
+ // The byte-significance of the stored components (modulus, etc..).
+ bool big_endian_;
+
+ // Component integers of the PrivateKeyInfo
+ std::vector<uint8_t> modulus_;
+ std::vector<uint8_t> public_exponent_;
+ std::vector<uint8_t> private_exponent_;
+ std::vector<uint8_t> prime1_;
+ std::vector<uint8_t> prime2_;
+ std::vector<uint8_t> exponent1_;
+ std::vector<uint8_t> exponent2_;
+ std::vector<uint8_t> coefficient_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrivateKeyInfoCodec);
+};
+
// Encapsulates an RSA private key. Can be used to generate new keys, export
// keys to other formats, or to extract a public key.
// TODO(hclam): This class should be ref-counted so it can be reused easily.
@@ -26,23 +171,34 @@ class CRYPTO_EXPORT RSAPrivateKey {
~RSAPrivateKey();
// Create a new random instance. Can return NULL if initialization fails.
- static std::unique_ptr<RSAPrivateKey> Create(uint16_t num_bits);
+ static RSAPrivateKey* Create(uint16_t num_bits);
// Create a new instance by importing an existing private key. The format is
// an ASN.1-encoded PrivateKeyInfo block from PKCS #8. This can return NULL if
// initialization fails.
- static std::unique_ptr<RSAPrivateKey> CreateFromPrivateKeyInfo(
+ static RSAPrivateKey* CreateFromPrivateKeyInfo(
const std::vector<uint8_t>& input);
+#if defined(USE_OPENSSL)
// Create a new instance from an existing EVP_PKEY, taking a
// reference to it. |key| must be an RSA key. Returns NULL on
// failure.
- static std::unique_ptr<RSAPrivateKey> CreateFromKey(EVP_PKEY* key);
-
- EVP_PKEY* key() { return key_.get(); }
+ static RSAPrivateKey* CreateFromKey(EVP_PKEY* key);
+#else
+ // Create a new instance by referencing an existing private key
+ // structure. Does not import the key.
+ static RSAPrivateKey* CreateFromKey(SECKEYPrivateKey* key);
+#endif
+
+#if defined(USE_OPENSSL)
+ EVP_PKEY* key() { return key_; }
+#else
+ SECKEYPrivateKey* key() { return key_; }
+ SECKEYPublicKey* public_key() { return public_key_; }
+#endif
// Creates a copy of the object.
- std::unique_ptr<RSAPrivateKey> Copy() const;
+ RSAPrivateKey* Copy() const;
// Exports the private key to a PKCS #8 PrivateKeyInfo block.
bool ExportPrivateKey(std::vector<uint8_t>* output) const;
@@ -54,7 +210,12 @@ class CRYPTO_EXPORT RSAPrivateKey {
// Constructor is private. Use one of the Create*() methods above instead.
RSAPrivateKey();
- bssl::UniquePtr<EVP_PKEY> key_;
+#if defined(USE_OPENSSL)
+ EVP_PKEY* key_;
+#else
+ SECKEYPrivateKey* key_;
+ SECKEYPublicKey* public_key_;
+#endif
DISALLOW_COPY_AND_ASSIGN(RSAPrivateKey);
};
diff --git a/crypto/rsa_private_key_nss.cc b/crypto/rsa_private_key_nss.cc
new file mode 100644
index 0000000000..b1026c1edb
--- /dev/null
+++ b/crypto/rsa_private_key_nss.cc
@@ -0,0 +1,151 @@
+// 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 "crypto/rsa_private_key.h"
+
+#include <cryptohi.h>
+#include <keyhi.h>
+#include <pk11pub.h>
+#include <stdint.h>
+
+#include <list>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_util.h"
+#include "crypto/nss_key_util.h"
+#include "crypto/nss_util.h"
+#include "crypto/scoped_nss_types.h"
+
+// TODO(rafaelw): Consider using NSS's ASN.1 encoder.
+namespace {
+
+static bool ReadAttribute(SECKEYPrivateKey* key,
+ CK_ATTRIBUTE_TYPE type,
+ std::vector<uint8_t>* output) {
+ SECItem item;
+ SECStatus rv;
+ rv = PK11_ReadRawAttribute(PK11_TypePrivKey, key, type, &item);
+ if (rv != SECSuccess) {
+ NOTREACHED();
+ return false;
+ }
+
+ output->assign(item.data, item.data + item.len);
+ SECITEM_FreeItem(&item, PR_FALSE);
+ return true;
+}
+
+} // namespace
+
+namespace crypto {
+
+RSAPrivateKey::~RSAPrivateKey() {
+ if (key_)
+ SECKEY_DestroyPrivateKey(key_);
+ if (public_key_)
+ SECKEY_DestroyPublicKey(public_key_);
+}
+
+// static
+RSAPrivateKey* RSAPrivateKey::Create(uint16_t num_bits) {
+ EnsureNSSInit();
+
+ ScopedPK11Slot slot(PK11_GetInternalSlot());
+ if (!slot) {
+ NOTREACHED();
+ return nullptr;
+ }
+
+ ScopedSECKEYPublicKey public_key;
+ ScopedSECKEYPrivateKey private_key;
+ if (!GenerateRSAKeyPairNSS(slot.get(), num_bits, false /* not permanent */,
+ &public_key, &private_key)) {
+ return nullptr;
+ }
+
+ RSAPrivateKey* rsa_key = new RSAPrivateKey;
+ rsa_key->public_key_ = public_key.release();
+ rsa_key->key_ = private_key.release();
+ return rsa_key;
+}
+
+// static
+RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfo(
+ const std::vector<uint8_t>& input) {
+ EnsureNSSInit();
+
+ ScopedPK11Slot slot(PK11_GetInternalSlot());
+ if (!slot) {
+ NOTREACHED();
+ return nullptr;
+ }
+ ScopedSECKEYPrivateKey key(ImportNSSKeyFromPrivateKeyInfo(
+ slot.get(), input, false /* not permanent */));
+ if (!key || SECKEY_GetPrivateKeyType(key.get()) != rsaKey)
+ return nullptr;
+ return RSAPrivateKey::CreateFromKey(key.get());
+}
+
+// static
+RSAPrivateKey* RSAPrivateKey::CreateFromKey(SECKEYPrivateKey* key) {
+ DCHECK(key);
+ if (SECKEY_GetPrivateKeyType(key) != rsaKey)
+ return NULL;
+ RSAPrivateKey* copy = new RSAPrivateKey();
+ copy->key_ = SECKEY_CopyPrivateKey(key);
+ copy->public_key_ = SECKEY_ConvertToPublicKey(key);
+ if (!copy->key_ || !copy->public_key_) {
+ NOTREACHED();
+ delete copy;
+ return NULL;
+ }
+ return copy;
+}
+
+RSAPrivateKey* RSAPrivateKey::Copy() const {
+ RSAPrivateKey* copy = new RSAPrivateKey();
+ copy->key_ = SECKEY_CopyPrivateKey(key_);
+ copy->public_key_ = SECKEY_CopyPublicKey(public_key_);
+ return copy;
+}
+
+bool RSAPrivateKey::ExportPrivateKey(std::vector<uint8_t>* output) const {
+ PrivateKeyInfoCodec private_key_info(true);
+
+ // Manually read the component attributes of the private key and build up
+ // the PrivateKeyInfo.
+ if (!ReadAttribute(key_, CKA_MODULUS, private_key_info.modulus()) ||
+ !ReadAttribute(key_, CKA_PUBLIC_EXPONENT,
+ private_key_info.public_exponent()) ||
+ !ReadAttribute(key_, CKA_PRIVATE_EXPONENT,
+ private_key_info.private_exponent()) ||
+ !ReadAttribute(key_, CKA_PRIME_1, private_key_info.prime1()) ||
+ !ReadAttribute(key_, CKA_PRIME_2, private_key_info.prime2()) ||
+ !ReadAttribute(key_, CKA_EXPONENT_1, private_key_info.exponent1()) ||
+ !ReadAttribute(key_, CKA_EXPONENT_2, private_key_info.exponent2()) ||
+ !ReadAttribute(key_, CKA_COEFFICIENT, private_key_info.coefficient())) {
+ NOTREACHED();
+ return false;
+ }
+
+ return private_key_info.Export(output);
+}
+
+bool RSAPrivateKey::ExportPublicKey(std::vector<uint8_t>* output) const {
+ ScopedSECItem der_pubkey(SECKEY_EncodeDERSubjectPublicKeyInfo(public_key_));
+ if (!der_pubkey.get()) {
+ NOTREACHED();
+ return false;
+ }
+
+ output->assign(der_pubkey->data, der_pubkey->data + der_pubkey->len);
+ return true;
+}
+
+RSAPrivateKey::RSAPrivateKey() : key_(NULL), public_key_(NULL) {
+ EnsureNSSInit();
+}
+
+} // namespace crypto
diff --git a/crypto/rsa_private_key_unittest.cc b/crypto/rsa_private_key_unittest.cc
index f9549f3418..393a24c536 100644
--- a/crypto/rsa_private_key_unittest.cc
+++ b/crypto/rsa_private_key_unittest.cc
@@ -103,8 +103,10 @@ TEST(RSAPrivateKeyUnitTest, InitRandomTest) {
ASSERT_EQ(privkey1.size(), privkey3.size());
ASSERT_EQ(privkey2.size(), privkey4.size());
- ASSERT_EQ(0, memcmp(&privkey1.front(), &privkey3.front(), privkey1.size()));
- ASSERT_EQ(0, memcmp(&privkey2.front(), &privkey4.front(), privkey2.size()));
+ ASSERT_TRUE(0 == memcmp(&privkey1.front(), &privkey3.front(),
+ privkey1.size()));
+ ASSERT_TRUE(0 == memcmp(&privkey2.front(), &privkey4.front(),
+ privkey2.size()));
}
// Test Copy() method.
@@ -193,8 +195,8 @@ TEST(RSAPrivateKeyUnitTest, PublicKeyTest) {
std::vector<uint8_t> output;
ASSERT_TRUE(key->ExportPublicKey(&output));
- ASSERT_EQ(0,
- memcmp(expected_public_key_info, &output.front(), output.size()));
+ ASSERT_TRUE(
+ memcmp(expected_public_key_info, &output.front(), output.size()) == 0);
}
// These two test keys each contain an integer that has 0x00 for its most
@@ -347,8 +349,10 @@ TEST(RSAPrivateKeyUnitTest, ShortIntegers) {
ASSERT_EQ(input1.size(), output1.size());
ASSERT_EQ(input2.size(), output2.size());
- ASSERT_EQ(0, memcmp(&output1.front(), &input1.front(), input1.size()));
- ASSERT_EQ(0, memcmp(&output2.front(), &input2.front(), input2.size()));
+ ASSERT_TRUE(0 == memcmp(&output1.front(), &input1.front(),
+ input1.size()));
+ ASSERT_TRUE(0 == memcmp(&output2.front(), &input2.front(),
+ input2.size()));
}
TEST(RSAPrivateKeyUnitTest, CreateFromKeyTest) {
diff --git a/crypto/scoped_openssl_types.h b/crypto/scoped_openssl_types.h
new file mode 100644
index 0000000000..622fed298f
--- /dev/null
+++ b/crypto/scoped_openssl_types.h
@@ -0,0 +1,62 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRYPTO_SCOPED_OPENSSL_TYPES_H_
+#define CRYPTO_SCOPED_OPENSSL_TYPES_H_
+
+#include <openssl/bio.h>
+#include <openssl/bn.h>
+#include <openssl/dsa.h>
+#include <openssl/ec.h>
+#include <openssl/ecdsa.h>
+#include <openssl/evp.h>
+#ifdef OPENSSL_IS_BORINGSSL
+#include <openssl/mem.h>
+#endif
+#include <openssl/rsa.h>
+#include <stdint.h>
+
+#include <memory>
+
+namespace crypto {
+
+// Simplistic helper that wraps a call to a deleter function. In a C++11 world,
+// this would be std::function<>. An alternative would be to re-use
+// base::internal::RunnableAdapter<>, but that's far too heavy weight.
+template <typename Type, void (*Destroyer)(Type*)>
+struct OpenSSLDestroyer {
+ void operator()(Type* ptr) const { Destroyer(ptr); }
+};
+
+template <typename PointerType, void (*Destroyer)(PointerType*)>
+using ScopedOpenSSL =
+ std::unique_ptr<PointerType, OpenSSLDestroyer<PointerType, Destroyer>>;
+
+struct OpenSSLFree {
+ void operator()(uint8_t* ptr) const { OPENSSL_free(ptr); }
+};
+
+// Several typedefs are provided for crypto-specific primitives, for
+// short-hand and prevalence. Note that OpenSSL types related to X.509 are
+// intentionally not included, as crypto/ does not generally deal with
+// certificates or PKI.
+using ScopedBIGNUM = ScopedOpenSSL<BIGNUM, BN_free>;
+using ScopedEC_Key = ScopedOpenSSL<EC_KEY, EC_KEY_free>;
+using ScopedBIO = ScopedOpenSSL<BIO, BIO_free_all>;
+using ScopedDSA = ScopedOpenSSL<DSA, DSA_free>;
+using ScopedECDSA_SIG = ScopedOpenSSL<ECDSA_SIG, ECDSA_SIG_free>;
+using ScopedEC_GROUP = ScopedOpenSSL<EC_GROUP, EC_GROUP_free>;
+using ScopedEC_KEY = ScopedOpenSSL<EC_KEY, EC_KEY_free>;
+using ScopedEC_POINT = ScopedOpenSSL<EC_POINT, EC_POINT_free>;
+using ScopedEVP_MD_CTX = ScopedOpenSSL<EVP_MD_CTX, EVP_MD_CTX_destroy>;
+using ScopedEVP_PKEY = ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free>;
+using ScopedEVP_PKEY_CTX = ScopedOpenSSL<EVP_PKEY_CTX, EVP_PKEY_CTX_free>;
+using ScopedRSA = ScopedOpenSSL<RSA, RSA_free>;
+
+// The bytes must have been allocated with OPENSSL_malloc.
+using ScopedOpenSSLBytes = std::unique_ptr<uint8_t, OpenSSLFree>;
+
+} // namespace crypto
+
+#endif // CRYPTO_SCOPED_OPENSSL_TYPES_H_
diff --git a/crypto/scoped_test_nss_chromeos_user.cc b/crypto/scoped_test_nss_chromeos_user.cc
index 49b92d4859..aec25d8dff 100644
--- a/crypto/scoped_test_nss_chromeos_user.cc
+++ b/crypto/scoped_test_nss_chromeos_user.cc
@@ -18,7 +18,7 @@ ScopedTestNSSChromeOSUser::ScopedTestNSSChromeOSUser(
// This opens a software DB in the given folder. In production code that is in
// the home folder, but for testing the temp folder is used.
constructed_successfully_ =
- InitializeNSSForChromeOSUser(username_hash, temp_dir_.GetPath());
+ InitializeNSSForChromeOSUser(username_hash, temp_dir_.path());
}
ScopedTestNSSChromeOSUser::~ScopedTestNSSChromeOSUser() {
diff --git a/crypto/scoped_test_nss_db.cc b/crypto/scoped_test_nss_db.cc
index b334109e03..dc58031ce5 100644
--- a/crypto/scoped_test_nss_db.cc
+++ b/crypto/scoped_test_nss_db.cc
@@ -24,7 +24,7 @@ ScopedTestNSSDB::ScopedTestNSSDB() {
return;
const char kTestDescription[] = "Test DB";
- slot_ = OpenSoftwareNSSDB(temp_dir_.GetPath(), kTestDescription);
+ slot_ = OpenSoftwareNSSDB(temp_dir_.path(), kTestDescription);
}
ScopedTestNSSDB::~ScopedTestNSSDB() {
diff --git a/crypto/scoped_test_system_nss_key_slot.h b/crypto/scoped_test_system_nss_key_slot.h
index ae9b2cd8a5..eb8fbc97a8 100644
--- a/crypto/scoped_test_system_nss_key_slot.h
+++ b/crypto/scoped_test_system_nss_key_slot.h
@@ -27,7 +27,7 @@ class ScopedTestNSSDB;
// At most one instance of this helper must be used at a time.
class CRYPTO_EXPORT ScopedTestSystemNSSKeySlot {
public:
- ScopedTestSystemNSSKeySlot();
+ explicit ScopedTestSystemNSSKeySlot();
~ScopedTestSystemNSSKeySlot();
bool ConstructedSuccessfully() const;
diff --git a/crypto/secure_hash.cc b/crypto/secure_hash.cc
index d47f783c05..9003b9cb69 100644
--- a/crypto/secure_hash.cc
+++ b/crypto/secure_hash.cc
@@ -27,7 +27,7 @@ class SecureHashSHA256 : public SecureHash {
SHA256_Init(&ctx_);
}
- SecureHashSHA256(const SecureHashSHA256& other) {
+ SecureHashSHA256(const SecureHashSHA256& other) : SecureHash() {
memcpy(&ctx_, &other.ctx_, sizeof(ctx_));
}
diff --git a/crypto/sha2.cc b/crypto/sha2.cc
index 1b302b34f6..e97b8f4037 100644
--- a/crypto/sha2.cc
+++ b/crypto/sha2.cc
@@ -21,7 +21,7 @@ void SHA256HashString(const base::StringPiece& str, void* output, size_t len) {
std::string SHA256HashString(const base::StringPiece& str) {
std::string output(kSHA256Length, 0);
- SHA256HashString(str, base::string_as_array(&output), output.size());
+ SHA256HashString(str, string_as_array(&output), output.size());
return output;
}
diff --git a/crypto/signature_creator.h b/crypto/signature_creator.h
index 674bd4cccb..1e8e856a02 100644
--- a/crypto/signature_creator.h
+++ b/crypto/signature_creator.h
@@ -14,8 +14,13 @@
#include "build/build_config.h"
#include "crypto/crypto_export.h"
+#if defined(USE_OPENSSL)
// Forward declaration for openssl/*.h
typedef struct env_md_ctx_st EVP_MD_CTX;
+#elif defined(USE_NSS_CERTS) || defined(OS_WIN) || defined(OS_MACOSX)
+// Forward declaration.
+struct SGNContextStr;
+#endif
namespace crypto {
@@ -57,7 +62,11 @@ class CRYPTO_EXPORT SignatureCreator {
// Private constructor. Use the Create() method instead.
SignatureCreator();
+#if defined(USE_OPENSSL)
EVP_MD_CTX* sign_context_;
+#elif defined(USE_NSS_CERTS) || defined(OS_WIN) || defined(OS_MACOSX)
+ SGNContextStr* sign_context_;
+#endif
DISALLOW_COPY_AND_ASSIGN(SignatureCreator);
};
diff --git a/crypto/signature_creator_nss.cc b/crypto/signature_creator_nss.cc
new file mode 100644
index 0000000000..bf204134b9
--- /dev/null
+++ b/crypto/signature_creator_nss.cc
@@ -0,0 +1,119 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/signature_creator.h"
+
+#include <cryptohi.h>
+#include <keyhi.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "crypto/nss_util.h"
+#include "crypto/rsa_private_key.h"
+
+namespace crypto {
+
+namespace {
+
+SECOidTag ToNSSSigOid(SignatureCreator::HashAlgorithm hash_alg) {
+ switch (hash_alg) {
+ case SignatureCreator::SHA1:
+ return SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
+ case SignatureCreator::SHA256:
+ return SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION;
+ }
+ return SEC_OID_UNKNOWN;
+}
+
+SECOidTag ToNSSHashOid(SignatureCreator::HashAlgorithm hash_alg) {
+ switch (hash_alg) {
+ case SignatureCreator::SHA1:
+ return SEC_OID_SHA1;
+ case SignatureCreator::SHA256:
+ return SEC_OID_SHA256;
+ }
+ return SEC_OID_UNKNOWN;
+}
+
+} // namespace
+
+SignatureCreator::~SignatureCreator() {
+ if (sign_context_) {
+ SGN_DestroyContext(sign_context_, PR_TRUE);
+ sign_context_ = NULL;
+ }
+}
+
+// static
+SignatureCreator* SignatureCreator::Create(RSAPrivateKey* key,
+ HashAlgorithm hash_alg) {
+ scoped_ptr<SignatureCreator> result(new SignatureCreator);
+ result->sign_context_ = SGN_NewContext(ToNSSSigOid(hash_alg), key->key());
+ if (!result->sign_context_) {
+ NOTREACHED();
+ return NULL;
+ }
+
+ SECStatus rv = SGN_Begin(result->sign_context_);
+ if (rv != SECSuccess) {
+ NOTREACHED();
+ return NULL;
+ }
+
+ return result.release();
+}
+
+// static
+bool SignatureCreator::Sign(RSAPrivateKey* key,
+ HashAlgorithm hash_alg,
+ const uint8_t* data,
+ int data_len,
+ std::vector<uint8_t>* signature) {
+ SECItem data_item;
+ data_item.type = siBuffer;
+ data_item.data = const_cast<unsigned char*>(data);
+ data_item.len = data_len;
+
+ SECItem signature_item;
+ SECStatus rv = SGN_Digest(key->key(), ToNSSHashOid(hash_alg), &signature_item,
+ &data_item);
+ if (rv != SECSuccess) {
+ NOTREACHED();
+ return false;
+ }
+ signature->assign(signature_item.data,
+ signature_item.data + signature_item.len);
+ SECITEM_FreeItem(&signature_item, PR_FALSE);
+ return true;
+}
+
+bool SignatureCreator::Update(const uint8_t* data_part, int data_part_len) {
+ SECStatus rv = SGN_Update(sign_context_, data_part, data_part_len);
+ if (rv != SECSuccess) {
+ NOTREACHED();
+ return false;
+ }
+
+ return true;
+}
+
+bool SignatureCreator::Final(std::vector<uint8_t>* signature) {
+ SECItem signature_item;
+ SECStatus rv = SGN_End(sign_context_, &signature_item);
+ if (rv != SECSuccess) {
+ return false;
+ }
+ signature->assign(signature_item.data,
+ signature_item.data + signature_item.len);
+ SECITEM_FreeItem(&signature_item, PR_FALSE);
+ return true;
+}
+
+SignatureCreator::SignatureCreator() : sign_context_(NULL) {
+ EnsureNSSInit();
+}
+
+} // namespace crypto
diff --git a/crypto/signature_creator_unittest.cc b/crypto/signature_creator_unittest.cc
index 2f135cc709..819e663dac 100644
--- a/crypto/signature_creator_unittest.cc
+++ b/crypto/signature_creator_unittest.cc
@@ -7,7 +7,6 @@
#include <stdint.h>
#include <memory>
-#include <string>
#include <vector>
#include "base/sha1.h"
diff --git a/crypto/signature_verifier.h b/crypto/signature_verifier.h
index f1ea58062c..5b7369fb51 100644
--- a/crypto/signature_verifier.h
+++ b/crypto/signature_verifier.h
@@ -7,14 +7,19 @@
#include <stdint.h>
-#include <memory>
#include <vector>
#include "build/build_config.h"
#include "crypto/crypto_export.h"
+#if defined(USE_OPENSSL)
typedef struct env_md_st EVP_MD;
typedef struct evp_pkey_ctx_st EVP_PKEY_CTX;
+#else
+typedef struct HASHContextStr HASHContext;
+typedef struct SECKEYPublicKeyStr SECKEYPublicKey;
+typedef struct VFYContextStr VFYContext;
+#endif
namespace crypto {
@@ -91,6 +96,7 @@ class CRYPTO_EXPORT SignatureVerifier {
bool VerifyFinal();
private:
+#if defined(USE_OPENSSL)
bool CommonInit(int pkey_type,
const EVP_MD* digest,
const uint8_t* signature,
@@ -98,13 +104,29 @@ class CRYPTO_EXPORT SignatureVerifier {
const uint8_t* public_key_info,
int public_key_info_len,
EVP_PKEY_CTX** pkey_ctx);
+#else
+ static SECKEYPublicKey* DecodePublicKeyInfo(const uint8_t* public_key_info,
+ int public_key_info_len);
+#endif
void Reset();
std::vector<uint8_t> signature_;
+#if defined(USE_OPENSSL)
struct VerifyContext;
- std::unique_ptr<VerifyContext> verify_context_;
+ VerifyContext* verify_context_;
+#else
+ // Used for all signature types except RSA-PSS.
+ VFYContext* vfy_context_;
+
+ // Used for RSA-PSS signatures.
+ HashAlgorithm hash_alg_;
+ HashAlgorithm mask_hash_alg_;
+ unsigned int salt_len_;
+ SECKEYPublicKey* public_key_;
+ HASHContext* hash_context_;
+#endif
};
} // namespace crypto
diff --git a/crypto/signature_verifier_nss.cc b/crypto/signature_verifier_nss.cc
new file mode 100644
index 0000000000..edbd3f6a98
--- /dev/null
+++ b/crypto/signature_verifier_nss.cc
@@ -0,0 +1,213 @@
+// 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 "crypto/signature_verifier.h"
+
+#include <cryptohi.h>
+#include <keyhi.h>
+#include <pk11pub.h>
+#include <secerr.h>
+#include <sechash.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "base/logging.h"
+#include "crypto/nss_util.h"
+#include "crypto/third_party/nss/chromium-nss.h"
+
+namespace crypto {
+
+namespace {
+
+HASH_HashType ToNSSHashType(SignatureVerifier::HashAlgorithm hash_alg) {
+ switch (hash_alg) {
+ case SignatureVerifier::SHA1:
+ return HASH_AlgSHA1;
+ case SignatureVerifier::SHA256:
+ return HASH_AlgSHA256;
+ }
+ return HASH_AlgNULL;
+}
+
+SECOidTag ToNSSSignatureType(SignatureVerifier::SignatureAlgorithm sig_alg) {
+ switch (sig_alg) {
+ case SignatureVerifier::RSA_PKCS1_SHA1:
+ return SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
+ case SignatureVerifier::RSA_PKCS1_SHA256:
+ return SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION;
+ case SignatureVerifier::ECDSA_SHA256:
+ return SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE;
+ }
+ return SEC_OID_UNKNOWN;
+}
+
+SECStatus VerifyRSAPSS_End(SECKEYPublicKey* public_key,
+ HASHContext* hash_context,
+ HASH_HashType mask_hash_alg,
+ unsigned int salt_len,
+ const unsigned char* signature,
+ unsigned int signature_len) {
+ unsigned int hash_len = HASH_ResultLenContext(hash_context);
+ std::vector<unsigned char> hash(hash_len);
+ HASH_End(hash_context, &hash[0], &hash_len, hash.size());
+
+ unsigned int modulus_len = SECKEY_PublicKeyStrength(public_key);
+ if (signature_len != modulus_len) {
+ PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
+ return SECFailure;
+ }
+ std::vector<unsigned char> enc(signature_len);
+ SECStatus rv = PK11_PubEncryptRaw(public_key, &enc[0],
+ const_cast<unsigned char*>(signature),
+ signature_len, NULL);
+ if (rv != SECSuccess) {
+ LOG(WARNING) << "PK11_PubEncryptRaw failed";
+ return rv;
+ }
+ return emsa_pss_verify(&hash[0], &enc[0], enc.size(),
+ HASH_GetType(hash_context), mask_hash_alg,
+ salt_len);
+}
+
+} // namespace
+
+SignatureVerifier::SignatureVerifier()
+ : vfy_context_(NULL),
+ hash_alg_(SHA1),
+ mask_hash_alg_(SHA1),
+ salt_len_(0),
+ public_key_(NULL),
+ hash_context_(NULL) {
+ EnsureNSSInit();
+}
+
+SignatureVerifier::~SignatureVerifier() {
+ Reset();
+}
+
+bool SignatureVerifier::VerifyInit(SignatureAlgorithm signature_algorithm,
+ const uint8_t* signature,
+ int signature_len,
+ const uint8_t* public_key_info,
+ int public_key_info_len) {
+ if (vfy_context_ || hash_context_)
+ return false;
+
+ signature_.assign(signature, signature + signature_len);
+
+ SECKEYPublicKey* public_key = DecodePublicKeyInfo(public_key_info,
+ public_key_info_len);
+ if (!public_key)
+ return false;
+
+ SECItem sig;
+ sig.type = siBuffer;
+ sig.data = const_cast<uint8_t*>(signature);
+ sig.len = signature_len;
+ vfy_context_ = VFY_CreateContext(
+ public_key, &sig, ToNSSSignatureType(signature_algorithm), nullptr);
+ SECKEY_DestroyPublicKey(public_key); // Done with public_key.
+ if (!vfy_context_) {
+ // A corrupted RSA signature could be detected without the data, so
+ // VFY_CreateContextWithAlgorithmID may fail with SEC_ERROR_BAD_SIGNATURE
+ // (-8182).
+ return false;
+ }
+
+ if (VFY_Begin(vfy_context_) != SECSuccess) {
+ NOTREACHED();
+ return false;
+ }
+ return true;
+}
+
+bool SignatureVerifier::VerifyInitRSAPSS(HashAlgorithm hash_alg,
+ HashAlgorithm mask_hash_alg,
+ int salt_len,
+ const uint8_t* signature,
+ int signature_len,
+ const uint8_t* public_key_info,
+ int public_key_info_len) {
+ if (vfy_context_ || hash_context_)
+ return false;
+
+ signature_.assign(signature, signature + signature_len);
+
+ SECKEYPublicKey* public_key = DecodePublicKeyInfo(public_key_info,
+ public_key_info_len);
+ if (!public_key)
+ return false;
+
+ public_key_ = public_key;
+ hash_alg_ = hash_alg;
+ mask_hash_alg_ = mask_hash_alg;
+ salt_len_ = salt_len;
+ hash_context_ = HASH_Create(ToNSSHashType(hash_alg_));
+ if (!hash_context_)
+ return false;
+ HASH_Begin(hash_context_);
+ return true;
+}
+
+void SignatureVerifier::VerifyUpdate(const uint8_t* data_part,
+ int data_part_len) {
+ if (vfy_context_) {
+ SECStatus rv = VFY_Update(vfy_context_, data_part, data_part_len);
+ DCHECK_EQ(SECSuccess, rv);
+ } else {
+ HASH_Update(hash_context_, data_part, data_part_len);
+ }
+}
+
+bool SignatureVerifier::VerifyFinal() {
+ SECStatus rv;
+ if (vfy_context_) {
+ rv = VFY_End(vfy_context_);
+ } else {
+ rv = VerifyRSAPSS_End(public_key_, hash_context_,
+ ToNSSHashType(mask_hash_alg_), salt_len_,
+ signature_.data(),
+ signature_.size());
+ }
+ Reset();
+
+ // If signature verification fails, the error code is
+ // SEC_ERROR_BAD_SIGNATURE (-8182).
+ return (rv == SECSuccess);
+}
+
+// static
+SECKEYPublicKey* SignatureVerifier::DecodePublicKeyInfo(
+ const uint8_t* public_key_info,
+ int public_key_info_len) {
+ CERTSubjectPublicKeyInfo* spki = NULL;
+ SECItem spki_der;
+ spki_der.type = siBuffer;
+ spki_der.data = const_cast<uint8_t*>(public_key_info);
+ spki_der.len = public_key_info_len;
+ spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_der);
+ if (!spki)
+ return NULL;
+ SECKEYPublicKey* public_key = SECKEY_ExtractPublicKey(spki);
+ SECKEY_DestroySubjectPublicKeyInfo(spki); // Done with spki.
+ return public_key;
+}
+
+void SignatureVerifier::Reset() {
+ if (vfy_context_) {
+ VFY_DestroyContext(vfy_context_, PR_TRUE);
+ vfy_context_ = NULL;
+ }
+ if (hash_context_) {
+ HASH_Destroy(hash_context_);
+ hash_context_ = NULL;
+ }
+ if (public_key_) {
+ SECKEY_DestroyPublicKey(public_key_);
+ public_key_ = NULL;
+ }
+ signature_.clear();
+}
+
+} // namespace crypto
diff --git a/crypto/signature_verifier_unittest.cc b/crypto/signature_verifier_unittest.cc
index 2cda4596a1..d71ea822be 100644
--- a/crypto/signature_verifier_unittest.cc
+++ b/crypto/signature_verifier_unittest.cc
@@ -7,7 +7,6 @@
#include <stddef.h>
#include <stdint.h>
-#include "base/logging.h"
#include "base/macros.h"
#include "base/numerics/safe_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/crypto/symmetric_key.cc b/crypto/symmetric_key.cc
index 6a19f84089..e3ecf624bc 100644
--- a/crypto/symmetric_key.cc
+++ b/crypto/symmetric_key.cc
@@ -4,6 +4,8 @@
#include "crypto/symmetric_key.h"
+#include <openssl/evp.h>
+#include <openssl/rand.h>
#include <stddef.h>
#include <stdint.h>
@@ -13,8 +15,6 @@
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "crypto/openssl_util.h"
-#include "third_party/boringssl/src/include/openssl/evp.h"
-#include "third_party/boringssl/src/include/openssl/rand.h"
namespace crypto {
diff --git a/crypto/symmetric_key.h b/crypto/symmetric_key.h
index 7494634b5e..88627084c6 100644
--- a/crypto/symmetric_key.h
+++ b/crypto/symmetric_key.h
@@ -14,6 +14,15 @@
#include "build/build_config.h"
#include "crypto/crypto_export.h"
+#if defined(NACL_WIN64)
+// See comments for crypto_nacl_win64 in crypto.gyp.
+// Must test for NACL_WIN64 before OS_WIN since former is a subset of latter.
+#include "crypto/scoped_capi_types.h"
+#elif defined(USE_NSS_CERTS) || \
+ (!defined(USE_OPENSSL) && (defined(OS_WIN) || defined(OS_MACOSX)))
+#include "crypto/scoped_nss_types.h"
+#endif
+
namespace crypto {
// Wraps a platform-specific symmetric key and allows it to be held in a
@@ -54,8 +63,13 @@ class CRYPTO_EXPORT SymmetricKey {
// size for use with |algorithm|. The caller owns the returned SymmetricKey.
static std::unique_ptr<SymmetricKey> Import(Algorithm algorithm,
const std::string& raw_key);
-
+#if defined(NACL_WIN64)
+ HCRYPTKEY key() const { return key_.get(); }
+#elif defined(USE_OPENSSL)
const std::string& key() { return key_; }
+#elif defined(USE_NSS_CERTS) || defined(OS_WIN) || defined(OS_MACOSX)
+ PK11SymKey* key() const { return key_.get(); }
+#endif
// Extracts the raw key from the platform specific data.
// Warning: |raw_key| holds the raw key as bytes and thus must be handled
@@ -63,9 +77,27 @@ class CRYPTO_EXPORT SymmetricKey {
bool GetRawKey(std::string* raw_key);
private:
- SymmetricKey();
-
+#if defined(NACL_WIN64)
+ SymmetricKey(HCRYPTPROV provider, HCRYPTKEY key,
+ const void* key_data, size_t key_size_in_bytes);
+
+ ScopedHCRYPTPROV provider_;
+ ScopedHCRYPTKEY key_;
+
+ // Contains the raw key, if it is known during initialization and when it
+ // is likely that the associated |provider_| will be unable to export the
+ // |key_|. This is the case of HMAC keys when the key size exceeds 16 bytes
+ // when using the default RSA provider.
+ // TODO(rsleevi): See if KP_EFFECTIVE_KEYLEN is the reason why CryptExportKey
+ // fails with NTE_BAD_KEY/NTE_BAD_LEN
+ std::string raw_key_;
+#elif defined(USE_OPENSSL)
+ SymmetricKey() {}
std::string key_;
+#elif defined(USE_NSS_CERTS) || defined(OS_WIN) || defined(OS_MACOSX)
+ explicit SymmetricKey(PK11SymKey* key);
+ ScopedPK11SymKey key_;
+#endif
DISALLOW_COPY_AND_ASSIGN(SymmetricKey);
};
diff --git a/crypto/symmetric_key_nss.cc b/crypto/symmetric_key_nss.cc
new file mode 100644
index 0000000000..e3aacc745b
--- /dev/null
+++ b/crypto/symmetric_key_nss.cc
@@ -0,0 +1,151 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/symmetric_key.h"
+
+#include <nss.h>
+#include <pk11pub.h>
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "crypto/nss_util.h"
+#include "crypto/scoped_nss_types.h"
+
+namespace crypto {
+
+SymmetricKey::~SymmetricKey() {}
+
+// static
+SymmetricKey* SymmetricKey::GenerateRandomKey(Algorithm algorithm,
+ size_t key_size_in_bits) {
+ DCHECK_EQ(AES, algorithm);
+
+ EnsureNSSInit();
+
+ // Whitelist supported key sizes to avoid accidentaly relying on
+ // algorithms available in NSS but not BoringSSL and vice
+ // versa. Note that BoringSSL does not support AES-192.
+ if (key_size_in_bits != 128 && key_size_in_bits != 256)
+ return NULL;
+
+ ScopedPK11Slot slot(PK11_GetInternalSlot());
+ if (!slot.get())
+ return NULL;
+
+ PK11SymKey* sym_key = PK11_KeyGen(slot.get(), CKM_AES_KEY_GEN, NULL,
+ key_size_in_bits / 8, NULL);
+ if (!sym_key)
+ return NULL;
+
+ return new SymmetricKey(sym_key);
+}
+
+// static
+SymmetricKey* SymmetricKey::DeriveKeyFromPassword(Algorithm algorithm,
+ const std::string& password,
+ const std::string& salt,
+ size_t iterations,
+ size_t key_size_in_bits) {
+ EnsureNSSInit();
+ if (salt.empty() || iterations == 0 || key_size_in_bits == 0)
+ return NULL;
+
+ if (algorithm == AES) {
+ // Whitelist supported key sizes to avoid accidentaly relying on
+ // algorithms available in NSS but not BoringSSL and vice
+ // versa. Note that BoringSSL does not support AES-192.
+ if (key_size_in_bits != 128 && key_size_in_bits != 256)
+ return NULL;
+ }
+
+ SECItem password_item;
+ password_item.type = siBuffer;
+ password_item.data = reinterpret_cast<unsigned char*>(
+ const_cast<char *>(password.data()));
+ password_item.len = password.size();
+
+ SECItem salt_item;
+ salt_item.type = siBuffer;
+ salt_item.data = reinterpret_cast<unsigned char*>(
+ const_cast<char *>(salt.data()));
+ salt_item.len = salt.size();
+
+ SECOidTag cipher_algorithm =
+ algorithm == AES ? SEC_OID_AES_256_CBC : SEC_OID_HMAC_SHA1;
+ ScopedSECAlgorithmID alg_id(PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2,
+ cipher_algorithm,
+ SEC_OID_HMAC_SHA1,
+ key_size_in_bits / 8,
+ iterations,
+ &salt_item));
+ if (!alg_id.get())
+ return NULL;
+
+ ScopedPK11Slot slot(PK11_GetInternalSlot());
+ if (!slot.get())
+ return NULL;
+
+ PK11SymKey* sym_key = PK11_PBEKeyGen(slot.get(), alg_id.get(), &password_item,
+ PR_FALSE, NULL);
+ if (!sym_key)
+ return NULL;
+
+ return new SymmetricKey(sym_key);
+}
+
+// static
+SymmetricKey* SymmetricKey::Import(Algorithm algorithm,
+ const std::string& raw_key) {
+ EnsureNSSInit();
+
+ if (algorithm == AES) {
+ // Whitelist supported key sizes to avoid accidentaly relying on
+ // algorithms available in NSS but not BoringSSL and vice
+ // versa. Note that BoringSSL does not support AES-192.
+ if (raw_key.size() != 128/8 && raw_key.size() != 256/8)
+ return NULL;
+ }
+
+ CK_MECHANISM_TYPE cipher =
+ algorithm == AES ? CKM_AES_CBC : CKM_SHA_1_HMAC;
+
+ SECItem key_item;
+ key_item.type = siBuffer;
+ key_item.data = reinterpret_cast<unsigned char*>(
+ const_cast<char *>(raw_key.data()));
+ key_item.len = raw_key.size();
+
+ ScopedPK11Slot slot(PK11_GetInternalSlot());
+ if (!slot.get())
+ return NULL;
+
+ // The exact value of the |origin| argument doesn't matter to NSS as long as
+ // it's not PK11_OriginFortezzaHack, so we pass PK11_OriginUnwrap as a
+ // placeholder.
+ PK11SymKey* sym_key = PK11_ImportSymKey(slot.get(), cipher, PK11_OriginUnwrap,
+ CKA_ENCRYPT, &key_item, NULL);
+ if (!sym_key)
+ return NULL;
+
+ return new SymmetricKey(sym_key);
+}
+
+bool SymmetricKey::GetRawKey(std::string* raw_key) {
+ SECStatus rv = PK11_ExtractKeyValue(key_.get());
+ if (SECSuccess != rv)
+ return false;
+
+ SECItem* key_item = PK11_GetKeyData(key_.get());
+ if (!key_item)
+ return false;
+
+ raw_key->assign(reinterpret_cast<char*>(key_item->data), key_item->len);
+ return true;
+}
+
+SymmetricKey::SymmetricKey(PK11SymKey* key) : key_(key) {
+ DCHECK(key);
+}
+
+} // namespace crypto
diff --git a/crypto/third_party/nss/chromium-blapi.h b/crypto/third_party/nss/chromium-blapi.h
new file mode 100644
index 0000000000..2ca772e4d3
--- /dev/null
+++ b/crypto/third_party/nss/chromium-blapi.h
@@ -0,0 +1,101 @@
+/*
+ * crypto.h - public data structures and prototypes for the crypto library
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+/* $Id: blapi.h,v 1.27 2007/11/09 18:49:32 wtc%google.com Exp $ */
+
+#ifndef CRYPTO_THIRD_PARTY_NSS_CHROMIUM_BLAPI_H_
+#define CRYPTO_THIRD_PARTY_NSS_CHROMIUM_BLAPI_H_
+
+#include "crypto/third_party/nss/chromium-blapit.h"
+
+/******************************************/
+
+extern SHA256Context *SHA256_NewContext(void);
+extern void SHA256_DestroyContext(SHA256Context *cx, PRBool freeit);
+extern void SHA256_Begin(SHA256Context *cx);
+extern void SHA256_Update(SHA256Context *cx, const unsigned char *input,
+ unsigned int inputLen);
+extern void SHA256_End(SHA256Context *cx, unsigned char *digest,
+ unsigned int *digestLen, unsigned int maxDigestLen);
+extern SECStatus SHA256_HashBuf(unsigned char *dest, const unsigned char *src,
+ unsigned int src_length);
+extern SECStatus SHA256_Hash(unsigned char *dest, const char *src);
+extern void SHA256_TraceState(SHA256Context *cx);
+extern unsigned int SHA256_FlattenSize(SHA256Context *cx);
+extern SECStatus SHA256_Flatten(SHA256Context *cx,unsigned char *space);
+extern SHA256Context * SHA256_Resurrect(unsigned char *space, void *arg);
+extern void SHA256_Clone(SHA256Context *dest, SHA256Context *src);
+
+/******************************************/
+
+extern SHA512Context *SHA512_NewContext(void);
+extern void SHA512_DestroyContext(SHA512Context *cx, PRBool freeit);
+extern void SHA512_Begin(SHA512Context *cx);
+extern void SHA512_Update(SHA512Context *cx, const unsigned char *input,
+ unsigned int inputLen);
+extern void SHA512_End(SHA512Context *cx, unsigned char *digest,
+ unsigned int *digestLen, unsigned int maxDigestLen);
+extern SECStatus SHA512_HashBuf(unsigned char *dest, const unsigned char *src,
+ unsigned int src_length);
+extern SECStatus SHA512_Hash(unsigned char *dest, const char *src);
+extern void SHA512_TraceState(SHA512Context *cx);
+extern unsigned int SHA512_FlattenSize(SHA512Context *cx);
+extern SECStatus SHA512_Flatten(SHA512Context *cx,unsigned char *space);
+extern SHA512Context * SHA512_Resurrect(unsigned char *space, void *arg);
+extern void SHA512_Clone(SHA512Context *dest, SHA512Context *src);
+
+/******************************************/
+
+extern SHA384Context *SHA384_NewContext(void);
+extern void SHA384_DestroyContext(SHA384Context *cx, PRBool freeit);
+extern void SHA384_Begin(SHA384Context *cx);
+extern void SHA384_Update(SHA384Context *cx, const unsigned char *input,
+ unsigned int inputLen);
+extern void SHA384_End(SHA384Context *cx, unsigned char *digest,
+ unsigned int *digestLen, unsigned int maxDigestLen);
+extern SECStatus SHA384_HashBuf(unsigned char *dest, const unsigned char *src,
+ unsigned int src_length);
+extern SECStatus SHA384_Hash(unsigned char *dest, const char *src);
+extern void SHA384_TraceState(SHA384Context *cx);
+extern unsigned int SHA384_FlattenSize(SHA384Context *cx);
+extern SECStatus SHA384_Flatten(SHA384Context *cx,unsigned char *space);
+extern SHA384Context * SHA384_Resurrect(unsigned char *space, void *arg);
+extern void SHA384_Clone(SHA384Context *dest, SHA384Context *src);
+
+#endif /* CRYPTO_THIRD_PARTY_NSS_CHROMIUM_BLAPI_H_ */
diff --git a/crypto/third_party/nss/chromium-blapit.h b/crypto/third_party/nss/chromium-blapit.h
new file mode 100644
index 0000000000..938547a2c0
--- /dev/null
+++ b/crypto/third_party/nss/chromium-blapit.h
@@ -0,0 +1,91 @@
+/*
+ * blapit.h - public data structures for the crypto library
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Dr Vipul Gupta <vipul.gupta@sun.com> and
+ * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+/* $Id: blapit.h,v 1.20 2007/02/28 19:47:37 rrelyea%redhat.com Exp $ */
+
+#ifndef CRYPTO_THIRD_PARTY_NSS_CHROMIUM_BLAPIT_H_
+#define CRYPTO_THIRD_PARTY_NSS_CHROMIUM_BLAPIT_H_
+
+#include "crypto/third_party/nss/chromium-prtypes.h"
+
+/*
+** A status code. Status's are used by procedures that return status
+** values. Again the motivation is so that a compiler can generate
+** warnings when return values are wrong. Correct testing of status codes:
+**
+** SECStatus rv;
+** rv = some_function (some_argument);
+** if (rv != SECSuccess)
+** do_an_error_thing();
+**
+*/
+typedef enum _SECStatus {
+ SECWouldBlock = -2,
+ SECFailure = -1,
+ SECSuccess = 0
+} SECStatus;
+
+#define SHA256_LENGTH 32 /* bytes */
+#define SHA384_LENGTH 48 /* bytes */
+#define SHA512_LENGTH 64 /* bytes */
+#define HASH_LENGTH_MAX SHA512_LENGTH
+
+/*
+ * Input block size for each hash algorithm.
+ */
+
+#define SHA256_BLOCK_LENGTH 64 /* bytes */
+#define SHA384_BLOCK_LENGTH 128 /* bytes */
+#define SHA512_BLOCK_LENGTH 128 /* bytes */
+#define HASH_BLOCK_LENGTH_MAX SHA512_BLOCK_LENGTH
+
+/***************************************************************************
+** Opaque objects
+*/
+
+struct SHA256ContextStr ;
+struct SHA512ContextStr ;
+
+typedef struct SHA256ContextStr SHA256Context;
+typedef struct SHA512ContextStr SHA512Context;
+/* SHA384Context is really a SHA512ContextStr. This is not a mistake. */
+typedef struct SHA512ContextStr SHA384Context;
+
+#endif /* CRYPTO_THIRD_PARTY_NSS_CHROMIUM_BLAPIT_H_ */
diff --git a/crypto/third_party/nss/chromium-nss.h b/crypto/third_party/nss/chromium-nss.h
new file mode 100644
index 0000000000..437e6bd5cd
--- /dev/null
+++ b/crypto/third_party/nss/chromium-nss.h
@@ -0,0 +1,79 @@
+ /* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef CRYPTO_THIRD_PARTY_NSS_CHROMIUM_NSS_H_
+#define CRYPTO_THIRD_PARTY_NSS_CHROMIUM_NSS_H_
+
+// This file contains some functions we borrowed from NSS.
+
+#include <prtypes.h>
+#include <hasht.h>
+#include <keyhi.h>
+#include <secmod.h>
+
+#include "crypto/crypto_export.h"
+
+extern "C" SECStatus emsa_pss_verify(const unsigned char *mHash,
+ const unsigned char *em,
+ unsigned int emLen,
+ HASH_HashType hashAlg,
+ HASH_HashType maskHashAlg,
+ unsigned int sLen);
+
+// Like PK11_ImportEncryptedPrivateKeyInfo, but hardcoded for EC, and returns
+// the SECKEYPrivateKey.
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=211546
+// When we use NSS 3.13.2 or later,
+// PK11_ImportEncryptedPrivateKeyInfoAndReturnKey can be used instead.
+SECStatus ImportEncryptedECPrivateKeyInfoAndReturnKey(
+ PK11SlotInfo* slot,
+ SECKEYEncryptedPrivateKeyInfo* epki,
+ SECItem* password,
+ SECItem* nickname,
+ SECItem* public_value,
+ PRBool permanent,
+ PRBool sensitive,
+ SECKEYPrivateKey** private_key,
+ void* wincx);
+
+// Like SEC_DerSignData.
+CRYPTO_EXPORT SECStatus DerSignData(PLArenaPool *arena,
+ SECItem *result,
+ SECItem *input,
+ SECKEYPrivateKey *key,
+ SECOidTag algo_id);
+
+#endif // CRYPTO_THIRD_PARTY_NSS_CHROMIUM_NSS_H_
diff --git a/crypto/third_party/nss/chromium-prtypes.h b/crypto/third_party/nss/chromium-prtypes.h
new file mode 100644
index 0000000000..d5ea8a9d20
--- /dev/null
+++ b/crypto/third_party/nss/chromium-prtypes.h
@@ -0,0 +1,77 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/* Emulates the real prtypes.h. Defines the types and macros that sha512.cc
+ * needs. */
+
+#ifndef CRYPTO_THIRD_PARTY_NSS_CHROMIUM_PRTYPES_H_
+#define CRYPTO_THIRD_PARTY_NSS_CHROMIUM_PRTYPES_H_
+
+#include <limits.h>
+#include <stdint.h>
+
+#include "build/build_config.h"
+
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+#define IS_LITTLE_ENDIAN 1
+#else
+#define IS_BIG_ENDIAN 1
+#endif
+
+/*
+ * The C language requires that 'long' be at least 32 bits. 2147483647 is the
+ * largest signed 32-bit integer.
+ */
+#if LONG_MAX > 2147483647L
+#define PR_BYTES_PER_LONG 8
+#else
+#define PR_BYTES_PER_LONG 4
+#endif
+
+#define HAVE_LONG_LONG
+
+#if defined(__linux__)
+#define LINUX
+#endif
+
+typedef uint8_t PRUint8;
+typedef uint32_t PRUint32;
+
+typedef int PRBool;
+
+#define PR_MIN(x,y) ((x)<(y)?(x):(y))
+
+#endif /* CRYPTO_THIRD_PARTY_NSS_CHROMIUM_PRTYPES_H_ */
diff --git a/crypto/third_party/nss/chromium-sha256.h b/crypto/third_party/nss/chromium-sha256.h
new file mode 100644
index 0000000000..52815ca7f1
--- /dev/null
+++ b/crypto/third_party/nss/chromium-sha256.h
@@ -0,0 +1,51 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef CRYPTO_THIRD_PARTY_NSS_CHROMIUM_SHA_256_H_
+#define CRYPTO_THIRD_PARTY_NSS_CHROMIUM_SHA_256_H_
+
+#include "crypto/third_party/nss/chromium-prtypes.h"
+
+struct SHA256ContextStr {
+ union {
+ PRUint32 w[64]; /* message schedule, input buffer, plus 48 words */
+ PRUint8 b[256];
+ } u;
+ PRUint32 h[8]; /* 8 state variables */
+ PRUint32 sizeHi,sizeLo; /* 64-bit count of hashed bytes. */
+};
+
+#endif /* CRYPTO_THIRD_PARTY_NSS_CHROMIUM_SHA_256_H_ */
diff --git a/crypto/third_party/nss/rsawrapr.c b/crypto/third_party/nss/rsawrapr.c
new file mode 100644
index 0000000000..73e498f937
--- /dev/null
+++ b/crypto/third_party/nss/rsawrapr.c
@@ -0,0 +1,160 @@
+/*
+ * PKCS#1 encoding and decoding functions.
+ * This file is believed to contain no code licensed from other parties.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "seccomon.h"
+#include "secerr.h"
+#include "sechash.h"
+
+/* Needed for RSA-PSS functions */
+static const unsigned char eightZeros[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+/*
+ * Mask generation function MGF1 as defined in PKCS #1 v2.1 / RFC 3447.
+ */
+static SECStatus
+MGF1(HASH_HashType hashAlg, unsigned char *mask, unsigned int maskLen,
+ const unsigned char *mgfSeed, unsigned int mgfSeedLen)
+{
+ unsigned int digestLen;
+ PRUint32 counter, rounds;
+ unsigned char *tempHash, *temp;
+ const SECHashObject *hash;
+ void *hashContext;
+ unsigned char C[4];
+
+ hash = HASH_GetHashObject(hashAlg);
+ if (hash == NULL)
+ return SECFailure;
+
+ hashContext = (*hash->create)();
+ rounds = (maskLen + hash->length - 1) / hash->length;
+ for (counter = 0; counter < rounds; counter++) {
+ C[0] = (unsigned char)((counter >> 24) & 0xff);
+ C[1] = (unsigned char)((counter >> 16) & 0xff);
+ C[2] = (unsigned char)((counter >> 8) & 0xff);
+ C[3] = (unsigned char)(counter & 0xff);
+
+ /* This could be optimized when the clone functions in
+ * rawhash.c are implemented. */
+ (*hash->begin)(hashContext);
+ (*hash->update)(hashContext, mgfSeed, mgfSeedLen);
+ (*hash->update)(hashContext, C, sizeof C);
+
+ tempHash = mask + counter * hash->length;
+ if (counter != (rounds-1)) {
+ (*hash->end)(hashContext, tempHash, &digestLen, hash->length);
+ } else { /* we're in the last round and need to cut the hash */
+ temp = (unsigned char *)PORT_Alloc(hash->length);
+ (*hash->end)(hashContext, temp, &digestLen, hash->length);
+ PORT_Memcpy(tempHash, temp, maskLen - counter * hash->length);
+ PORT_Free(temp);
+ }
+ }
+ (*hash->destroy)(hashContext, PR_TRUE);
+
+ return SECSuccess;
+}
+
+/*
+ * Verify a RSA-PSS signature.
+ * Described in RFC 3447, section 9.1.2.
+ * We use mHash instead of M as input.
+ * emBits from the RFC is just modBits - 1, see section 8.1.2.
+ * We only support MGF1 as the MGF.
+ *
+ * NOTE: this code assumes modBits is a multiple of 8.
+ */
+SECStatus
+emsa_pss_verify(const unsigned char *mHash,
+ const unsigned char *em, unsigned int emLen,
+ HASH_HashType hashAlg, HASH_HashType maskHashAlg,
+ unsigned int sLen)
+{
+ const SECHashObject *hash;
+ void *hash_context;
+ unsigned char *db;
+ unsigned char *H_; /* H' from the RFC */
+ unsigned int i, dbMaskLen;
+ SECStatus rv;
+
+ hash = HASH_GetHashObject(hashAlg);
+ dbMaskLen = emLen - hash->length - 1;
+
+ /* Step 3 + 4 + 6 */
+ if ((emLen < (hash->length + sLen + 2)) ||
+ (em[emLen - 1] != 0xbc) ||
+ ((em[0] & 0x80) != 0)) {
+ PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
+ return SECFailure;
+ }
+
+ /* Step 7 */
+ db = (unsigned char *)PORT_Alloc(dbMaskLen);
+ if (db == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+ /* &em[dbMaskLen] points to H, used as mgfSeed */
+ MGF1(maskHashAlg, db, dbMaskLen, &em[dbMaskLen], hash->length);
+
+ /* Step 8 */
+ for (i = 0; i < dbMaskLen; i++) {
+ db[i] ^= em[i];
+ }
+
+ /* Step 9 */
+ db[0] &= 0x7f;
+
+ /* Step 10 */
+ for (i = 0; i < (dbMaskLen - sLen - 1); i++) {
+ if (db[i] != 0) {
+ PORT_Free(db);
+ PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
+ return SECFailure;
+ }
+ }
+ if (db[dbMaskLen - sLen - 1] != 0x01) {
+ PORT_Free(db);
+ PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
+ return SECFailure;
+ }
+
+ /* Step 12 + 13 */
+ H_ = (unsigned char *)PORT_Alloc(hash->length);
+ if (H_ == NULL) {
+ PORT_Free(db);
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+ hash_context = (*hash->create)();
+ if (hash_context == NULL) {
+ PORT_Free(db);
+ PORT_Free(H_);
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+ (*hash->begin)(hash_context);
+ (*hash->update)(hash_context, eightZeros, 8);
+ (*hash->update)(hash_context, mHash, hash->length);
+ (*hash->update)(hash_context, &db[dbMaskLen - sLen], sLen);
+ (*hash->end)(hash_context, H_, &i, hash->length);
+ (*hash->destroy)(hash_context, PR_TRUE);
+
+ PORT_Free(db);
+
+ /* Step 14 */
+ if (PORT_Memcmp(H_, &em[dbMaskLen], hash->length) != 0) {
+ PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
+ rv = SECFailure;
+ } else {
+ rv = SECSuccess;
+ }
+
+ PORT_Free(H_);
+ return rv;
+}
diff --git a/crypto/third_party/nss/sha512.cc b/crypto/third_party/nss/sha512.cc
new file mode 100644
index 0000000000..78950cb51f
--- /dev/null
+++ b/crypto/third_party/nss/sha512.cc
@@ -0,0 +1,1390 @@
+/*
+ * sha512.c - implementation of SHA256, SHA384 and SHA512
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+/* $Id: sha512.c,v 1.9 2006/10/13 16:54:04 wtchang%redhat.com Exp $ */
+
+// Prevent manual unrolling in the sha256 code, which reduces the binary code
+// size from ~10k to ~1k. The performance should be reasonable for our use.
+#define NOUNROLL256 1
+
+#include "crypto/third_party/nss/chromium-prtypes.h" /* for PRUintXX */
+#if defined(_X86_) || defined(SHA_NO_LONG_LONG)
+#define NOUNROLL512 1
+#undef HAVE_LONG_LONG
+#endif
+#include "crypto/third_party/nss/chromium-blapi.h"
+#include "crypto/third_party/nss/chromium-sha256.h" /* for struct SHA256ContextStr */
+
+#include <stdlib.h>
+#include <string.h>
+#define PORT_New(type) static_cast<type*>(malloc(sizeof(type)))
+#define PORT_ZFree(ptr, len) do { memset(ptr, 0, len); free(ptr); } while (0)
+#define PORT_Strlen(s) static_cast<unsigned int>(strlen(s))
+#define PORT_Memcpy memcpy
+
+/* ============= Common constants and defines ======================= */
+
+#define W ctx->u.w
+#define B ctx->u.b
+#define H ctx->h
+
+#define SHR(x,n) (x >> n)
+#define SHL(x,n) (x << n)
+#define Ch(x,y,z) ((x & y) ^ (~x & z))
+#define Maj(x,y,z) ((x & y) ^ (x & z) ^ (y & z))
+
+/* Padding used with all flavors of SHA */
+static const PRUint8 pad[240] = {
+0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+ /* compiler will fill the rest in with zeros */
+};
+
+/* ============= SHA256 implemenmtation ================================== */
+
+/* SHA-256 constants, K256. */
+static const PRUint32 K256[64] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
+
+/* SHA-256 initial hash values */
+static const PRUint32 H256[8] = {
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
+ 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
+};
+
+#if defined(_MSC_VER) && defined(_X86_)
+#ifndef FORCEINLINE
+#if (_MSC_VER >= 1200)
+#define FORCEINLINE __forceinline
+#else
+#define FORCEINLINE __inline
+#endif
+#endif
+#define FASTCALL __fastcall
+
+static FORCEINLINE PRUint32 FASTCALL
+swap4b(PRUint32 dwd)
+{
+ __asm {
+ mov eax,dwd
+ bswap eax
+ }
+}
+
+#define SHA_HTONL(x) swap4b(x)
+#define BYTESWAP4(x) x = SHA_HTONL(x)
+
+#elif defined(LINUX) && defined(_X86_)
+#undef __OPTIMIZE__
+#define __OPTIMIZE__ 1
+#undef __pentium__
+#define __pentium__ 1
+#include <byteswap.h>
+#define SHA_HTONL(x) bswap_32(x)
+#define BYTESWAP4(x) x = SHA_HTONL(x)
+
+#else /* neither windows nor Linux PC */
+#define SWAP4MASK 0x00FF00FF
+#define SHA_HTONL(x) (t1 = (x), t1 = (t1 << 16) | (t1 >> 16), \
+ ((t1 & SWAP4MASK) << 8) | ((t1 >> 8) & SWAP4MASK))
+#define BYTESWAP4(x) x = SHA_HTONL(x)
+#endif
+
+#if defined(_MSC_VER) && defined(_X86_)
+#pragma intrinsic (_lrotr, _lrotl)
+#define ROTR32(x,n) _lrotr(x,n)
+#define ROTL32(x,n) _lrotl(x,n)
+#else
+#define ROTR32(x,n) ((x >> n) | (x << ((8 * sizeof x) - n)))
+#define ROTL32(x,n) ((x << n) | (x >> ((8 * sizeof x) - n)))
+#endif
+
+/* Capitol Sigma and lower case sigma functions */
+#define S0(x) (ROTR32(x, 2) ^ ROTR32(x,13) ^ ROTR32(x,22))
+#define S1(x) (ROTR32(x, 6) ^ ROTR32(x,11) ^ ROTR32(x,25))
+#define s0(x) (t1 = x, ROTR32(t1, 7) ^ ROTR32(t1,18) ^ SHR(t1, 3))
+#define s1(x) (t2 = x, ROTR32(t2,17) ^ ROTR32(t2,19) ^ SHR(t2,10))
+
+SHA256Context *
+SHA256_NewContext(void)
+{
+ SHA256Context *ctx = PORT_New(SHA256Context);
+ return ctx;
+}
+
+void
+SHA256_DestroyContext(SHA256Context *ctx, PRBool freeit)
+{
+ if (freeit) {
+ PORT_ZFree(ctx, sizeof *ctx);
+ }
+}
+
+void
+SHA256_Begin(SHA256Context *ctx)
+{
+ memset(ctx, 0, sizeof *ctx);
+ memcpy(H, H256, sizeof H256);
+}
+
+static void
+SHA256_Compress(SHA256Context *ctx)
+{
+ {
+ register PRUint32 t1, t2;
+
+#if defined(IS_LITTLE_ENDIAN)
+ BYTESWAP4(W[0]);
+ BYTESWAP4(W[1]);
+ BYTESWAP4(W[2]);
+ BYTESWAP4(W[3]);
+ BYTESWAP4(W[4]);
+ BYTESWAP4(W[5]);
+ BYTESWAP4(W[6]);
+ BYTESWAP4(W[7]);
+ BYTESWAP4(W[8]);
+ BYTESWAP4(W[9]);
+ BYTESWAP4(W[10]);
+ BYTESWAP4(W[11]);
+ BYTESWAP4(W[12]);
+ BYTESWAP4(W[13]);
+ BYTESWAP4(W[14]);
+ BYTESWAP4(W[15]);
+#endif
+
+#define INITW(t) W[t] = (s1(W[t-2]) + W[t-7] + s0(W[t-15]) + W[t-16])
+
+ /* prepare the "message schedule" */
+#ifdef NOUNROLL256
+ {
+ int t;
+ for (t = 16; t < 64; ++t) {
+ INITW(t);
+ }
+ }
+#else
+ INITW(16);
+ INITW(17);
+ INITW(18);
+ INITW(19);
+
+ INITW(20);
+ INITW(21);
+ INITW(22);
+ INITW(23);
+ INITW(24);
+ INITW(25);
+ INITW(26);
+ INITW(27);
+ INITW(28);
+ INITW(29);
+
+ INITW(30);
+ INITW(31);
+ INITW(32);
+ INITW(33);
+ INITW(34);
+ INITW(35);
+ INITW(36);
+ INITW(37);
+ INITW(38);
+ INITW(39);
+
+ INITW(40);
+ INITW(41);
+ INITW(42);
+ INITW(43);
+ INITW(44);
+ INITW(45);
+ INITW(46);
+ INITW(47);
+ INITW(48);
+ INITW(49);
+
+ INITW(50);
+ INITW(51);
+ INITW(52);
+ INITW(53);
+ INITW(54);
+ INITW(55);
+ INITW(56);
+ INITW(57);
+ INITW(58);
+ INITW(59);
+
+ INITW(60);
+ INITW(61);
+ INITW(62);
+ INITW(63);
+
+#endif
+#undef INITW
+ }
+ {
+ PRUint32 a, b, c, d, e, f, g, h;
+
+ a = H[0];
+ b = H[1];
+ c = H[2];
+ d = H[3];
+ e = H[4];
+ f = H[5];
+ g = H[6];
+ h = H[7];
+
+#define ROUND(n,a,b,c,d,e,f,g,h) \
+ h += S1(e) + Ch(e,f,g) + K256[n] + W[n]; \
+ d += h; \
+ h += S0(a) + Maj(a,b,c);
+
+#ifdef NOUNROLL256
+ {
+ int t;
+ for (t = 0; t < 64; t+= 8) {
+ ROUND(t+0,a,b,c,d,e,f,g,h)
+ ROUND(t+1,h,a,b,c,d,e,f,g)
+ ROUND(t+2,g,h,a,b,c,d,e,f)
+ ROUND(t+3,f,g,h,a,b,c,d,e)
+ ROUND(t+4,e,f,g,h,a,b,c,d)
+ ROUND(t+5,d,e,f,g,h,a,b,c)
+ ROUND(t+6,c,d,e,f,g,h,a,b)
+ ROUND(t+7,b,c,d,e,f,g,h,a)
+ }
+ }
+#else
+ ROUND( 0,a,b,c,d,e,f,g,h)
+ ROUND( 1,h,a,b,c,d,e,f,g)
+ ROUND( 2,g,h,a,b,c,d,e,f)
+ ROUND( 3,f,g,h,a,b,c,d,e)
+ ROUND( 4,e,f,g,h,a,b,c,d)
+ ROUND( 5,d,e,f,g,h,a,b,c)
+ ROUND( 6,c,d,e,f,g,h,a,b)
+ ROUND( 7,b,c,d,e,f,g,h,a)
+
+ ROUND( 8,a,b,c,d,e,f,g,h)
+ ROUND( 9,h,a,b,c,d,e,f,g)
+ ROUND(10,g,h,a,b,c,d,e,f)
+ ROUND(11,f,g,h,a,b,c,d,e)
+ ROUND(12,e,f,g,h,a,b,c,d)
+ ROUND(13,d,e,f,g,h,a,b,c)
+ ROUND(14,c,d,e,f,g,h,a,b)
+ ROUND(15,b,c,d,e,f,g,h,a)
+
+ ROUND(16,a,b,c,d,e,f,g,h)
+ ROUND(17,h,a,b,c,d,e,f,g)
+ ROUND(18,g,h,a,b,c,d,e,f)
+ ROUND(19,f,g,h,a,b,c,d,e)
+ ROUND(20,e,f,g,h,a,b,c,d)
+ ROUND(21,d,e,f,g,h,a,b,c)
+ ROUND(22,c,d,e,f,g,h,a,b)
+ ROUND(23,b,c,d,e,f,g,h,a)
+
+ ROUND(24,a,b,c,d,e,f,g,h)
+ ROUND(25,h,a,b,c,d,e,f,g)
+ ROUND(26,g,h,a,b,c,d,e,f)
+ ROUND(27,f,g,h,a,b,c,d,e)
+ ROUND(28,e,f,g,h,a,b,c,d)
+ ROUND(29,d,e,f,g,h,a,b,c)
+ ROUND(30,c,d,e,f,g,h,a,b)
+ ROUND(31,b,c,d,e,f,g,h,a)
+
+ ROUND(32,a,b,c,d,e,f,g,h)
+ ROUND(33,h,a,b,c,d,e,f,g)
+ ROUND(34,g,h,a,b,c,d,e,f)
+ ROUND(35,f,g,h,a,b,c,d,e)
+ ROUND(36,e,f,g,h,a,b,c,d)
+ ROUND(37,d,e,f,g,h,a,b,c)
+ ROUND(38,c,d,e,f,g,h,a,b)
+ ROUND(39,b,c,d,e,f,g,h,a)
+
+ ROUND(40,a,b,c,d,e,f,g,h)
+ ROUND(41,h,a,b,c,d,e,f,g)
+ ROUND(42,g,h,a,b,c,d,e,f)
+ ROUND(43,f,g,h,a,b,c,d,e)
+ ROUND(44,e,f,g,h,a,b,c,d)
+ ROUND(45,d,e,f,g,h,a,b,c)
+ ROUND(46,c,d,e,f,g,h,a,b)
+ ROUND(47,b,c,d,e,f,g,h,a)
+
+ ROUND(48,a,b,c,d,e,f,g,h)
+ ROUND(49,h,a,b,c,d,e,f,g)
+ ROUND(50,g,h,a,b,c,d,e,f)
+ ROUND(51,f,g,h,a,b,c,d,e)
+ ROUND(52,e,f,g,h,a,b,c,d)
+ ROUND(53,d,e,f,g,h,a,b,c)
+ ROUND(54,c,d,e,f,g,h,a,b)
+ ROUND(55,b,c,d,e,f,g,h,a)
+
+ ROUND(56,a,b,c,d,e,f,g,h)
+ ROUND(57,h,a,b,c,d,e,f,g)
+ ROUND(58,g,h,a,b,c,d,e,f)
+ ROUND(59,f,g,h,a,b,c,d,e)
+ ROUND(60,e,f,g,h,a,b,c,d)
+ ROUND(61,d,e,f,g,h,a,b,c)
+ ROUND(62,c,d,e,f,g,h,a,b)
+ ROUND(63,b,c,d,e,f,g,h,a)
+#endif
+
+ H[0] += a;
+ H[1] += b;
+ H[2] += c;
+ H[3] += d;
+ H[4] += e;
+ H[5] += f;
+ H[6] += g;
+ H[7] += h;
+ }
+#undef ROUND
+}
+
+#undef s0
+#undef s1
+#undef S0
+#undef S1
+
+void
+SHA256_Update(SHA256Context *ctx, const unsigned char *input,
+ unsigned int inputLen)
+{
+ unsigned int inBuf = ctx->sizeLo & 0x3f;
+ if (!inputLen)
+ return;
+
+ /* Add inputLen into the count of bytes processed, before processing */
+ if ((ctx->sizeLo += inputLen) < inputLen)
+ ctx->sizeHi++;
+
+ /* if data already in buffer, attemp to fill rest of buffer */
+ if (inBuf) {
+ unsigned int todo = SHA256_BLOCK_LENGTH - inBuf;
+ if (inputLen < todo)
+ todo = inputLen;
+ memcpy(B + inBuf, input, todo);
+ input += todo;
+ inputLen -= todo;
+ if (inBuf + todo == SHA256_BLOCK_LENGTH)
+ SHA256_Compress(ctx);
+ }
+
+ /* if enough data to fill one or more whole buffers, process them. */
+ while (inputLen >= SHA256_BLOCK_LENGTH) {
+ memcpy(B, input, SHA256_BLOCK_LENGTH);
+ input += SHA256_BLOCK_LENGTH;
+ inputLen -= SHA256_BLOCK_LENGTH;
+ SHA256_Compress(ctx);
+ }
+ /* if data left over, fill it into buffer */
+ if (inputLen)
+ memcpy(B, input, inputLen);
+}
+
+void
+SHA256_End(SHA256Context *ctx, unsigned char *digest,
+ unsigned int *digestLen, unsigned int maxDigestLen)
+{
+ unsigned int inBuf = ctx->sizeLo & 0x3f;
+ unsigned int padLen = (inBuf < 56) ? (56 - inBuf) : (56 + 64 - inBuf);
+ PRUint32 hi, lo;
+#ifdef SWAP4MASK
+ PRUint32 t1;
+#endif
+
+ hi = (ctx->sizeHi << 3) | (ctx->sizeLo >> 29);
+ lo = (ctx->sizeLo << 3);
+
+ SHA256_Update(ctx, pad, padLen);
+
+#if defined(IS_LITTLE_ENDIAN)
+ W[14] = SHA_HTONL(hi);
+ W[15] = SHA_HTONL(lo);
+#else
+ W[14] = hi;
+ W[15] = lo;
+#endif
+ SHA256_Compress(ctx);
+
+ /* now output the answer */
+#if defined(IS_LITTLE_ENDIAN)
+ BYTESWAP4(H[0]);
+ BYTESWAP4(H[1]);
+ BYTESWAP4(H[2]);
+ BYTESWAP4(H[3]);
+ BYTESWAP4(H[4]);
+ BYTESWAP4(H[5]);
+ BYTESWAP4(H[6]);
+ BYTESWAP4(H[7]);
+#endif
+ padLen = PR_MIN(SHA256_LENGTH, maxDigestLen);
+ memcpy(digest, H, padLen);
+ if (digestLen)
+ *digestLen = padLen;
+}
+
+void SHA256_Clone(SHA256Context* dest, SHA256Context* src)
+{
+ memcpy(dest, src, sizeof *dest);
+}
+
+/* Comment out unused code, mostly the SHA384 and SHA512 implementations. */
+#if 0
+SECStatus
+SHA256_HashBuf(unsigned char *dest, const unsigned char *src,
+ unsigned int src_length)
+{
+ SHA256Context ctx;
+ unsigned int outLen;
+
+ SHA256_Begin(&ctx);
+ SHA256_Update(&ctx, src, src_length);
+ SHA256_End(&ctx, dest, &outLen, SHA256_LENGTH);
+
+ return SECSuccess;
+}
+
+
+SECStatus
+SHA256_Hash(unsigned char *dest, const char *src)
+{
+ return SHA256_HashBuf(dest, (const unsigned char *)src, PORT_Strlen(src));
+}
+
+
+void SHA256_TraceState(SHA256Context *ctx) { }
+
+unsigned int
+SHA256_FlattenSize(SHA256Context *ctx)
+{
+ return sizeof *ctx;
+}
+
+SECStatus
+SHA256_Flatten(SHA256Context *ctx,unsigned char *space)
+{
+ PORT_Memcpy(space, ctx, sizeof *ctx);
+ return SECSuccess;
+}
+
+SHA256Context *
+SHA256_Resurrect(unsigned char *space, void *arg)
+{
+ SHA256Context *ctx = SHA256_NewContext();
+ if (ctx)
+ PORT_Memcpy(ctx, space, sizeof *ctx);
+ return ctx;
+}
+
+/* ======= SHA512 and SHA384 common constants and defines ================= */
+
+/* common #defines for SHA512 and SHA384 */
+#if defined(HAVE_LONG_LONG)
+#define ROTR64(x,n) ((x >> n) | (x << (64 - n)))
+#define ROTL64(x,n) ((x << n) | (x >> (64 - n)))
+
+#define S0(x) (ROTR64(x,28) ^ ROTR64(x,34) ^ ROTR64(x,39))
+#define S1(x) (ROTR64(x,14) ^ ROTR64(x,18) ^ ROTR64(x,41))
+#define s0(x) (t1 = x, ROTR64(t1, 1) ^ ROTR64(t1, 8) ^ SHR(t1,7))
+#define s1(x) (t2 = x, ROTR64(t2,19) ^ ROTR64(t2,61) ^ SHR(t2,6))
+
+#if PR_BYTES_PER_LONG == 8
+#define ULLC(hi,lo) 0x ## hi ## lo ## UL
+#elif defined(_MSC_VER)
+#define ULLC(hi,lo) 0x ## hi ## lo ## ui64
+#else
+#define ULLC(hi,lo) 0x ## hi ## lo ## ULL
+#endif
+
+#define SHA_MASK16 ULLC(0000FFFF,0000FFFF)
+#define SHA_MASK8 ULLC(00FF00FF,00FF00FF)
+#define SHA_HTONLL(x) (t1 = x, \
+ t1 = ((t1 & SHA_MASK8 ) << 8) | ((t1 >> 8) & SHA_MASK8 ), \
+ t1 = ((t1 & SHA_MASK16) << 16) | ((t1 >> 16) & SHA_MASK16), \
+ (t1 >> 32) | (t1 << 32))
+#define BYTESWAP8(x) x = SHA_HTONLL(x)
+
+#else /* no long long */
+
+#if defined(IS_LITTLE_ENDIAN)
+#define ULLC(hi,lo) { 0x ## lo ## U, 0x ## hi ## U }
+#else
+#define ULLC(hi,lo) { 0x ## hi ## U, 0x ## lo ## U }
+#endif
+
+#define SHA_HTONLL(x) ( BYTESWAP4(x.lo), BYTESWAP4(x.hi), \
+ x.hi ^= x.lo ^= x.hi ^= x.lo, x)
+#define BYTESWAP8(x) do { PRUint32 tmp; BYTESWAP4(x.lo); BYTESWAP4(x.hi); \
+ tmp = x.lo; x.lo = x.hi; x.hi = tmp; } while (0)
+#endif
+
+/* SHA-384 and SHA-512 constants, K512. */
+static const PRUint64 K512[80] = {
+#if PR_BYTES_PER_LONG == 8
+ 0x428a2f98d728ae22UL , 0x7137449123ef65cdUL ,
+ 0xb5c0fbcfec4d3b2fUL , 0xe9b5dba58189dbbcUL ,
+ 0x3956c25bf348b538UL , 0x59f111f1b605d019UL ,
+ 0x923f82a4af194f9bUL , 0xab1c5ed5da6d8118UL ,
+ 0xd807aa98a3030242UL , 0x12835b0145706fbeUL ,
+ 0x243185be4ee4b28cUL , 0x550c7dc3d5ffb4e2UL ,
+ 0x72be5d74f27b896fUL , 0x80deb1fe3b1696b1UL ,
+ 0x9bdc06a725c71235UL , 0xc19bf174cf692694UL ,
+ 0xe49b69c19ef14ad2UL , 0xefbe4786384f25e3UL ,
+ 0x0fc19dc68b8cd5b5UL , 0x240ca1cc77ac9c65UL ,
+ 0x2de92c6f592b0275UL , 0x4a7484aa6ea6e483UL ,
+ 0x5cb0a9dcbd41fbd4UL , 0x76f988da831153b5UL ,
+ 0x983e5152ee66dfabUL , 0xa831c66d2db43210UL ,
+ 0xb00327c898fb213fUL , 0xbf597fc7beef0ee4UL ,
+ 0xc6e00bf33da88fc2UL , 0xd5a79147930aa725UL ,
+ 0x06ca6351e003826fUL , 0x142929670a0e6e70UL ,
+ 0x27b70a8546d22ffcUL , 0x2e1b21385c26c926UL ,
+ 0x4d2c6dfc5ac42aedUL , 0x53380d139d95b3dfUL ,
+ 0x650a73548baf63deUL , 0x766a0abb3c77b2a8UL ,
+ 0x81c2c92e47edaee6UL , 0x92722c851482353bUL ,
+ 0xa2bfe8a14cf10364UL , 0xa81a664bbc423001UL ,
+ 0xc24b8b70d0f89791UL , 0xc76c51a30654be30UL ,
+ 0xd192e819d6ef5218UL , 0xd69906245565a910UL ,
+ 0xf40e35855771202aUL , 0x106aa07032bbd1b8UL ,
+ 0x19a4c116b8d2d0c8UL , 0x1e376c085141ab53UL ,
+ 0x2748774cdf8eeb99UL , 0x34b0bcb5e19b48a8UL ,
+ 0x391c0cb3c5c95a63UL , 0x4ed8aa4ae3418acbUL ,
+ 0x5b9cca4f7763e373UL , 0x682e6ff3d6b2b8a3UL ,
+ 0x748f82ee5defb2fcUL , 0x78a5636f43172f60UL ,
+ 0x84c87814a1f0ab72UL , 0x8cc702081a6439ecUL ,
+ 0x90befffa23631e28UL , 0xa4506cebde82bde9UL ,
+ 0xbef9a3f7b2c67915UL , 0xc67178f2e372532bUL ,
+ 0xca273eceea26619cUL , 0xd186b8c721c0c207UL ,
+ 0xeada7dd6cde0eb1eUL , 0xf57d4f7fee6ed178UL ,
+ 0x06f067aa72176fbaUL , 0x0a637dc5a2c898a6UL ,
+ 0x113f9804bef90daeUL , 0x1b710b35131c471bUL ,
+ 0x28db77f523047d84UL , 0x32caab7b40c72493UL ,
+ 0x3c9ebe0a15c9bebcUL , 0x431d67c49c100d4cUL ,
+ 0x4cc5d4becb3e42b6UL , 0x597f299cfc657e2aUL ,
+ 0x5fcb6fab3ad6faecUL , 0x6c44198c4a475817UL
+#else
+ ULLC(428a2f98,d728ae22), ULLC(71374491,23ef65cd),
+ ULLC(b5c0fbcf,ec4d3b2f), ULLC(e9b5dba5,8189dbbc),
+ ULLC(3956c25b,f348b538), ULLC(59f111f1,b605d019),
+ ULLC(923f82a4,af194f9b), ULLC(ab1c5ed5,da6d8118),
+ ULLC(d807aa98,a3030242), ULLC(12835b01,45706fbe),
+ ULLC(243185be,4ee4b28c), ULLC(550c7dc3,d5ffb4e2),
+ ULLC(72be5d74,f27b896f), ULLC(80deb1fe,3b1696b1),
+ ULLC(9bdc06a7,25c71235), ULLC(c19bf174,cf692694),
+ ULLC(e49b69c1,9ef14ad2), ULLC(efbe4786,384f25e3),
+ ULLC(0fc19dc6,8b8cd5b5), ULLC(240ca1cc,77ac9c65),
+ ULLC(2de92c6f,592b0275), ULLC(4a7484aa,6ea6e483),
+ ULLC(5cb0a9dc,bd41fbd4), ULLC(76f988da,831153b5),
+ ULLC(983e5152,ee66dfab), ULLC(a831c66d,2db43210),
+ ULLC(b00327c8,98fb213f), ULLC(bf597fc7,beef0ee4),
+ ULLC(c6e00bf3,3da88fc2), ULLC(d5a79147,930aa725),
+ ULLC(06ca6351,e003826f), ULLC(14292967,0a0e6e70),
+ ULLC(27b70a85,46d22ffc), ULLC(2e1b2138,5c26c926),
+ ULLC(4d2c6dfc,5ac42aed), ULLC(53380d13,9d95b3df),
+ ULLC(650a7354,8baf63de), ULLC(766a0abb,3c77b2a8),
+ ULLC(81c2c92e,47edaee6), ULLC(92722c85,1482353b),
+ ULLC(a2bfe8a1,4cf10364), ULLC(a81a664b,bc423001),
+ ULLC(c24b8b70,d0f89791), ULLC(c76c51a3,0654be30),
+ ULLC(d192e819,d6ef5218), ULLC(d6990624,5565a910),
+ ULLC(f40e3585,5771202a), ULLC(106aa070,32bbd1b8),
+ ULLC(19a4c116,b8d2d0c8), ULLC(1e376c08,5141ab53),
+ ULLC(2748774c,df8eeb99), ULLC(34b0bcb5,e19b48a8),
+ ULLC(391c0cb3,c5c95a63), ULLC(4ed8aa4a,e3418acb),
+ ULLC(5b9cca4f,7763e373), ULLC(682e6ff3,d6b2b8a3),
+ ULLC(748f82ee,5defb2fc), ULLC(78a5636f,43172f60),
+ ULLC(84c87814,a1f0ab72), ULLC(8cc70208,1a6439ec),
+ ULLC(90befffa,23631e28), ULLC(a4506ceb,de82bde9),
+ ULLC(bef9a3f7,b2c67915), ULLC(c67178f2,e372532b),
+ ULLC(ca273ece,ea26619c), ULLC(d186b8c7,21c0c207),
+ ULLC(eada7dd6,cde0eb1e), ULLC(f57d4f7f,ee6ed178),
+ ULLC(06f067aa,72176fba), ULLC(0a637dc5,a2c898a6),
+ ULLC(113f9804,bef90dae), ULLC(1b710b35,131c471b),
+ ULLC(28db77f5,23047d84), ULLC(32caab7b,40c72493),
+ ULLC(3c9ebe0a,15c9bebc), ULLC(431d67c4,9c100d4c),
+ ULLC(4cc5d4be,cb3e42b6), ULLC(597f299c,fc657e2a),
+ ULLC(5fcb6fab,3ad6faec), ULLC(6c44198c,4a475817)
+#endif
+};
+
+struct SHA512ContextStr {
+ union {
+ PRUint64 w[80]; /* message schedule, input buffer, plus 64 words */
+ PRUint32 l[160];
+ PRUint8 b[640];
+ } u;
+ PRUint64 h[8]; /* 8 state variables */
+ PRUint64 sizeLo; /* 64-bit count of hashed bytes. */
+};
+
+/* =========== SHA512 implementation ===================================== */
+
+/* SHA-512 initial hash values */
+static const PRUint64 H512[8] = {
+#if PR_BYTES_PER_LONG == 8
+ 0x6a09e667f3bcc908UL , 0xbb67ae8584caa73bUL ,
+ 0x3c6ef372fe94f82bUL , 0xa54ff53a5f1d36f1UL ,
+ 0x510e527fade682d1UL , 0x9b05688c2b3e6c1fUL ,
+ 0x1f83d9abfb41bd6bUL , 0x5be0cd19137e2179UL
+#else
+ ULLC(6a09e667,f3bcc908), ULLC(bb67ae85,84caa73b),
+ ULLC(3c6ef372,fe94f82b), ULLC(a54ff53a,5f1d36f1),
+ ULLC(510e527f,ade682d1), ULLC(9b05688c,2b3e6c1f),
+ ULLC(1f83d9ab,fb41bd6b), ULLC(5be0cd19,137e2179)
+#endif
+};
+
+
+SHA512Context *
+SHA512_NewContext(void)
+{
+ SHA512Context *ctx = PORT_New(SHA512Context);
+ return ctx;
+}
+
+void
+SHA512_DestroyContext(SHA512Context *ctx, PRBool freeit)
+{
+ if (freeit) {
+ PORT_ZFree(ctx, sizeof *ctx);
+ }
+}
+
+void
+SHA512_Begin(SHA512Context *ctx)
+{
+ memset(ctx, 0, sizeof *ctx);
+ memcpy(H, H512, sizeof H512);
+}
+
+#if defined(SHA512_TRACE)
+#if defined(HAVE_LONG_LONG)
+#define DUMP(n,a,d,e,h) printf(" t = %2d, %s = %016lx, %s = %016lx\n", \
+ n, #e, d, #a, h);
+#else
+#define DUMP(n,a,d,e,h) printf(" t = %2d, %s = %08x%08x, %s = %08x%08x\n", \
+ n, #e, d.hi, d.lo, #a, h.hi, h.lo);
+#endif
+#else
+#define DUMP(n,a,d,e,h)
+#endif
+
+#if defined(HAVE_LONG_LONG)
+
+#define ADDTO(x,y) y += x
+
+#define INITW(t) W[t] = (s1(W[t-2]) + W[t-7] + s0(W[t-15]) + W[t-16])
+
+#define ROUND(n,a,b,c,d,e,f,g,h) \
+ h += S1(e) + Ch(e,f,g) + K512[n] + W[n]; \
+ d += h; \
+ h += S0(a) + Maj(a,b,c); \
+ DUMP(n,a,d,e,h)
+
+#else /* use only 32-bit variables, and don't unroll loops */
+
+#undef NOUNROLL512
+#define NOUNROLL512 1
+
+#define ADDTO(x,y) y.lo += x.lo; y.hi += x.hi + (x.lo > y.lo)
+
+#define ROTR64a(x,n,lo,hi) (x.lo >> n | x.hi << (32-n))
+#define ROTR64A(x,n,lo,hi) (x.lo << (64-n) | x.hi >> (n-32))
+#define SHR64a(x,n,lo,hi) (x.lo >> n | x.hi << (32-n))
+
+/* Capitol Sigma and lower case sigma functions */
+#define s0lo(x) (ROTR64a(x,1,lo,hi) ^ ROTR64a(x,8,lo,hi) ^ SHR64a(x,7,lo,hi))
+#define s0hi(x) (ROTR64a(x,1,hi,lo) ^ ROTR64a(x,8,hi,lo) ^ (x.hi >> 7))
+
+#define s1lo(x) (ROTR64a(x,19,lo,hi) ^ ROTR64A(x,61,lo,hi) ^ SHR64a(x,6,lo,hi))
+#define s1hi(x) (ROTR64a(x,19,hi,lo) ^ ROTR64A(x,61,hi,lo) ^ (x.hi >> 6))
+
+#define S0lo(x)(ROTR64a(x,28,lo,hi) ^ ROTR64A(x,34,lo,hi) ^ ROTR64A(x,39,lo,hi))
+#define S0hi(x)(ROTR64a(x,28,hi,lo) ^ ROTR64A(x,34,hi,lo) ^ ROTR64A(x,39,hi,lo))
+
+#define S1lo(x)(ROTR64a(x,14,lo,hi) ^ ROTR64a(x,18,lo,hi) ^ ROTR64A(x,41,lo,hi))
+#define S1hi(x)(ROTR64a(x,14,hi,lo) ^ ROTR64a(x,18,hi,lo) ^ ROTR64A(x,41,hi,lo))
+
+/* 32-bit versions of Ch and Maj */
+#define Chxx(x,y,z,lo) ((x.lo & y.lo) ^ (~x.lo & z.lo))
+#define Majx(x,y,z,lo) ((x.lo & y.lo) ^ (x.lo & z.lo) ^ (y.lo & z.lo))
+
+#define INITW(t) \
+ do { \
+ PRUint32 lo, tm; \
+ PRUint32 cy = 0; \
+ lo = s1lo(W[t-2]); \
+ lo += (tm = W[t-7].lo); if (lo < tm) cy++; \
+ lo += (tm = s0lo(W[t-15])); if (lo < tm) cy++; \
+ lo += (tm = W[t-16].lo); if (lo < tm) cy++; \
+ W[t].lo = lo; \
+ W[t].hi = cy + s1hi(W[t-2]) + W[t-7].hi + s0hi(W[t-15]) + W[t-16].hi; \
+ } while (0)
+
+#define ROUND(n,a,b,c,d,e,f,g,h) \
+ { \
+ PRUint32 lo, tm, cy; \
+ lo = S1lo(e); \
+ lo += (tm = Chxx(e,f,g,lo)); cy = (lo < tm); \
+ lo += (tm = K512[n].lo); if (lo < tm) cy++; \
+ lo += (tm = W[n].lo); if (lo < tm) cy++; \
+ h.lo += lo; if (h.lo < lo) cy++; \
+ h.hi += cy + S1hi(e) + Chxx(e,f,g,hi) + K512[n].hi + W[n].hi; \
+ d.lo += h.lo; \
+ d.hi += h.hi + (d.lo < h.lo); \
+ lo = S0lo(a); \
+ lo += (tm = Majx(a,b,c,lo)); cy = (lo < tm); \
+ h.lo += lo; if (h.lo < lo) cy++; \
+ h.hi += cy + S0hi(a) + Majx(a,b,c,hi); \
+ DUMP(n,a,d,e,h) \
+ }
+#endif
+
+static void
+SHA512_Compress(SHA512Context *ctx)
+{
+#if defined(IS_LITTLE_ENDIAN)
+ {
+#if defined(HAVE_LONG_LONG)
+ PRUint64 t1;
+#else
+ PRUint32 t1;
+#endif
+ BYTESWAP8(W[0]);
+ BYTESWAP8(W[1]);
+ BYTESWAP8(W[2]);
+ BYTESWAP8(W[3]);
+ BYTESWAP8(W[4]);
+ BYTESWAP8(W[5]);
+ BYTESWAP8(W[6]);
+ BYTESWAP8(W[7]);
+ BYTESWAP8(W[8]);
+ BYTESWAP8(W[9]);
+ BYTESWAP8(W[10]);
+ BYTESWAP8(W[11]);
+ BYTESWAP8(W[12]);
+ BYTESWAP8(W[13]);
+ BYTESWAP8(W[14]);
+ BYTESWAP8(W[15]);
+ }
+#endif
+
+ {
+ PRUint64 t1, t2;
+#ifdef NOUNROLL512
+ {
+ /* prepare the "message schedule" */
+ int t;
+ for (t = 16; t < 80; ++t) {
+ INITW(t);
+ }
+ }
+#else
+ INITW(16);
+ INITW(17);
+ INITW(18);
+ INITW(19);
+
+ INITW(20);
+ INITW(21);
+ INITW(22);
+ INITW(23);
+ INITW(24);
+ INITW(25);
+ INITW(26);
+ INITW(27);
+ INITW(28);
+ INITW(29);
+
+ INITW(30);
+ INITW(31);
+ INITW(32);
+ INITW(33);
+ INITW(34);
+ INITW(35);
+ INITW(36);
+ INITW(37);
+ INITW(38);
+ INITW(39);
+
+ INITW(40);
+ INITW(41);
+ INITW(42);
+ INITW(43);
+ INITW(44);
+ INITW(45);
+ INITW(46);
+ INITW(47);
+ INITW(48);
+ INITW(49);
+
+ INITW(50);
+ INITW(51);
+ INITW(52);
+ INITW(53);
+ INITW(54);
+ INITW(55);
+ INITW(56);
+ INITW(57);
+ INITW(58);
+ INITW(59);
+
+ INITW(60);
+ INITW(61);
+ INITW(62);
+ INITW(63);
+ INITW(64);
+ INITW(65);
+ INITW(66);
+ INITW(67);
+ INITW(68);
+ INITW(69);
+
+ INITW(70);
+ INITW(71);
+ INITW(72);
+ INITW(73);
+ INITW(74);
+ INITW(75);
+ INITW(76);
+ INITW(77);
+ INITW(78);
+ INITW(79);
+#endif
+ }
+#ifdef SHA512_TRACE
+ {
+ int i;
+ for (i = 0; i < 80; ++i) {
+#ifdef HAVE_LONG_LONG
+ printf("W[%2d] = %016lx\n", i, W[i]);
+#else
+ printf("W[%2d] = %08x%08x\n", i, W[i].hi, W[i].lo);
+#endif
+ }
+ }
+#endif
+ {
+ PRUint64 a, b, c, d, e, f, g, h;
+
+ a = H[0];
+ b = H[1];
+ c = H[2];
+ d = H[3];
+ e = H[4];
+ f = H[5];
+ g = H[6];
+ h = H[7];
+
+#ifdef NOUNROLL512
+ {
+ int t;
+ for (t = 0; t < 80; t+= 8) {
+ ROUND(t+0,a,b,c,d,e,f,g,h)
+ ROUND(t+1,h,a,b,c,d,e,f,g)
+ ROUND(t+2,g,h,a,b,c,d,e,f)
+ ROUND(t+3,f,g,h,a,b,c,d,e)
+ ROUND(t+4,e,f,g,h,a,b,c,d)
+ ROUND(t+5,d,e,f,g,h,a,b,c)
+ ROUND(t+6,c,d,e,f,g,h,a,b)
+ ROUND(t+7,b,c,d,e,f,g,h,a)
+ }
+ }
+#else
+ ROUND( 0,a,b,c,d,e,f,g,h)
+ ROUND( 1,h,a,b,c,d,e,f,g)
+ ROUND( 2,g,h,a,b,c,d,e,f)
+ ROUND( 3,f,g,h,a,b,c,d,e)
+ ROUND( 4,e,f,g,h,a,b,c,d)
+ ROUND( 5,d,e,f,g,h,a,b,c)
+ ROUND( 6,c,d,e,f,g,h,a,b)
+ ROUND( 7,b,c,d,e,f,g,h,a)
+
+ ROUND( 8,a,b,c,d,e,f,g,h)
+ ROUND( 9,h,a,b,c,d,e,f,g)
+ ROUND(10,g,h,a,b,c,d,e,f)
+ ROUND(11,f,g,h,a,b,c,d,e)
+ ROUND(12,e,f,g,h,a,b,c,d)
+ ROUND(13,d,e,f,g,h,a,b,c)
+ ROUND(14,c,d,e,f,g,h,a,b)
+ ROUND(15,b,c,d,e,f,g,h,a)
+
+ ROUND(16,a,b,c,d,e,f,g,h)
+ ROUND(17,h,a,b,c,d,e,f,g)
+ ROUND(18,g,h,a,b,c,d,e,f)
+ ROUND(19,f,g,h,a,b,c,d,e)
+ ROUND(20,e,f,g,h,a,b,c,d)
+ ROUND(21,d,e,f,g,h,a,b,c)
+ ROUND(22,c,d,e,f,g,h,a,b)
+ ROUND(23,b,c,d,e,f,g,h,a)
+
+ ROUND(24,a,b,c,d,e,f,g,h)
+ ROUND(25,h,a,b,c,d,e,f,g)
+ ROUND(26,g,h,a,b,c,d,e,f)
+ ROUND(27,f,g,h,a,b,c,d,e)
+ ROUND(28,e,f,g,h,a,b,c,d)
+ ROUND(29,d,e,f,g,h,a,b,c)
+ ROUND(30,c,d,e,f,g,h,a,b)
+ ROUND(31,b,c,d,e,f,g,h,a)
+
+ ROUND(32,a,b,c,d,e,f,g,h)
+ ROUND(33,h,a,b,c,d,e,f,g)
+ ROUND(34,g,h,a,b,c,d,e,f)
+ ROUND(35,f,g,h,a,b,c,d,e)
+ ROUND(36,e,f,g,h,a,b,c,d)
+ ROUND(37,d,e,f,g,h,a,b,c)
+ ROUND(38,c,d,e,f,g,h,a,b)
+ ROUND(39,b,c,d,e,f,g,h,a)
+
+ ROUND(40,a,b,c,d,e,f,g,h)
+ ROUND(41,h,a,b,c,d,e,f,g)
+ ROUND(42,g,h,a,b,c,d,e,f)
+ ROUND(43,f,g,h,a,b,c,d,e)
+ ROUND(44,e,f,g,h,a,b,c,d)
+ ROUND(45,d,e,f,g,h,a,b,c)
+ ROUND(46,c,d,e,f,g,h,a,b)
+ ROUND(47,b,c,d,e,f,g,h,a)
+
+ ROUND(48,a,b,c,d,e,f,g,h)
+ ROUND(49,h,a,b,c,d,e,f,g)
+ ROUND(50,g,h,a,b,c,d,e,f)
+ ROUND(51,f,g,h,a,b,c,d,e)
+ ROUND(52,e,f,g,h,a,b,c,d)
+ ROUND(53,d,e,f,g,h,a,b,c)
+ ROUND(54,c,d,e,f,g,h,a,b)
+ ROUND(55,b,c,d,e,f,g,h,a)
+
+ ROUND(56,a,b,c,d,e,f,g,h)
+ ROUND(57,h,a,b,c,d,e,f,g)
+ ROUND(58,g,h,a,b,c,d,e,f)
+ ROUND(59,f,g,h,a,b,c,d,e)
+ ROUND(60,e,f,g,h,a,b,c,d)
+ ROUND(61,d,e,f,g,h,a,b,c)
+ ROUND(62,c,d,e,f,g,h,a,b)
+ ROUND(63,b,c,d,e,f,g,h,a)
+
+ ROUND(64,a,b,c,d,e,f,g,h)
+ ROUND(65,h,a,b,c,d,e,f,g)
+ ROUND(66,g,h,a,b,c,d,e,f)
+ ROUND(67,f,g,h,a,b,c,d,e)
+ ROUND(68,e,f,g,h,a,b,c,d)
+ ROUND(69,d,e,f,g,h,a,b,c)
+ ROUND(70,c,d,e,f,g,h,a,b)
+ ROUND(71,b,c,d,e,f,g,h,a)
+
+ ROUND(72,a,b,c,d,e,f,g,h)
+ ROUND(73,h,a,b,c,d,e,f,g)
+ ROUND(74,g,h,a,b,c,d,e,f)
+ ROUND(75,f,g,h,a,b,c,d,e)
+ ROUND(76,e,f,g,h,a,b,c,d)
+ ROUND(77,d,e,f,g,h,a,b,c)
+ ROUND(78,c,d,e,f,g,h,a,b)
+ ROUND(79,b,c,d,e,f,g,h,a)
+#endif
+
+ ADDTO(a,H[0]);
+ ADDTO(b,H[1]);
+ ADDTO(c,H[2]);
+ ADDTO(d,H[3]);
+ ADDTO(e,H[4]);
+ ADDTO(f,H[5]);
+ ADDTO(g,H[6]);
+ ADDTO(h,H[7]);
+ }
+}
+
+void
+SHA512_Update(SHA512Context *ctx, const unsigned char *input,
+ unsigned int inputLen)
+{
+ unsigned int inBuf;
+ if (!inputLen)
+ return;
+
+#if defined(HAVE_LONG_LONG)
+ inBuf = (unsigned int)ctx->sizeLo & 0x7f;
+ /* Add inputLen into the count of bytes processed, before processing */
+ ctx->sizeLo += inputLen;
+#else
+ inBuf = (unsigned int)ctx->sizeLo.lo & 0x7f;
+ ctx->sizeLo.lo += inputLen;
+ if (ctx->sizeLo.lo < inputLen) ctx->sizeLo.hi++;
+#endif
+
+ /* if data already in buffer, attemp to fill rest of buffer */
+ if (inBuf) {
+ unsigned int todo = SHA512_BLOCK_LENGTH - inBuf;
+ if (inputLen < todo)
+ todo = inputLen;
+ memcpy(B + inBuf, input, todo);
+ input += todo;
+ inputLen -= todo;
+ if (inBuf + todo == SHA512_BLOCK_LENGTH)
+ SHA512_Compress(ctx);
+ }
+
+ /* if enough data to fill one or more whole buffers, process them. */
+ while (inputLen >= SHA512_BLOCK_LENGTH) {
+ memcpy(B, input, SHA512_BLOCK_LENGTH);
+ input += SHA512_BLOCK_LENGTH;
+ inputLen -= SHA512_BLOCK_LENGTH;
+ SHA512_Compress(ctx);
+ }
+ /* if data left over, fill it into buffer */
+ if (inputLen)
+ memcpy(B, input, inputLen);
+}
+
+void
+SHA512_End(SHA512Context *ctx, unsigned char *digest,
+ unsigned int *digestLen, unsigned int maxDigestLen)
+{
+#if defined(HAVE_LONG_LONG)
+ unsigned int inBuf = (unsigned int)ctx->sizeLo & 0x7f;
+ unsigned int padLen = (inBuf < 112) ? (112 - inBuf) : (112 + 128 - inBuf);
+ PRUint64 lo, t1;
+ lo = (ctx->sizeLo << 3);
+#else
+ unsigned int inBuf = (unsigned int)ctx->sizeLo.lo & 0x7f;
+ unsigned int padLen = (inBuf < 112) ? (112 - inBuf) : (112 + 128 - inBuf);
+ PRUint64 lo = ctx->sizeLo;
+ PRUint32 t1;
+ lo.lo <<= 3;
+#endif
+
+ SHA512_Update(ctx, pad, padLen);
+
+#if defined(HAVE_LONG_LONG)
+ W[14] = 0;
+#else
+ W[14].lo = 0;
+ W[14].hi = 0;
+#endif
+
+ W[15] = lo;
+#if defined(IS_LITTLE_ENDIAN)
+ BYTESWAP8(W[15]);
+#endif
+ SHA512_Compress(ctx);
+
+ /* now output the answer */
+#if defined(IS_LITTLE_ENDIAN)
+ BYTESWAP8(H[0]);
+ BYTESWAP8(H[1]);
+ BYTESWAP8(H[2]);
+ BYTESWAP8(H[3]);
+ BYTESWAP8(H[4]);
+ BYTESWAP8(H[5]);
+ BYTESWAP8(H[6]);
+ BYTESWAP8(H[7]);
+#endif
+ padLen = PR_MIN(SHA512_LENGTH, maxDigestLen);
+ memcpy(digest, H, padLen);
+ if (digestLen)
+ *digestLen = padLen;
+}
+
+SECStatus
+SHA512_HashBuf(unsigned char *dest, const unsigned char *src,
+ unsigned int src_length)
+{
+ SHA512Context ctx;
+ unsigned int outLen;
+
+ SHA512_Begin(&ctx);
+ SHA512_Update(&ctx, src, src_length);
+ SHA512_End(&ctx, dest, &outLen, SHA512_LENGTH);
+
+ return SECSuccess;
+}
+
+
+SECStatus
+SHA512_Hash(unsigned char *dest, const char *src)
+{
+ return SHA512_HashBuf(dest, (const unsigned char *)src, PORT_Strlen(src));
+}
+
+
+void SHA512_TraceState(SHA512Context *ctx) { }
+
+unsigned int
+SHA512_FlattenSize(SHA512Context *ctx)
+{
+ return sizeof *ctx;
+}
+
+SECStatus
+SHA512_Flatten(SHA512Context *ctx,unsigned char *space)
+{
+ PORT_Memcpy(space, ctx, sizeof *ctx);
+ return SECSuccess;
+}
+
+SHA512Context *
+SHA512_Resurrect(unsigned char *space, void *arg)
+{
+ SHA512Context *ctx = SHA512_NewContext();
+ if (ctx)
+ PORT_Memcpy(ctx, space, sizeof *ctx);
+ return ctx;
+}
+
+void SHA512_Clone(SHA512Context *dest, SHA512Context *src)
+{
+ memcpy(dest, src, sizeof *dest);
+}
+
+/* ======================================================================= */
+/* SHA384 uses a SHA512Context as the real context.
+** The only differences between SHA384 an SHA512 are:
+** a) the intialization values for the context, and
+** b) the number of bytes of data produced as output.
+*/
+
+/* SHA-384 initial hash values */
+static const PRUint64 H384[8] = {
+#if PR_BYTES_PER_LONG == 8
+ 0xcbbb9d5dc1059ed8UL , 0x629a292a367cd507UL ,
+ 0x9159015a3070dd17UL , 0x152fecd8f70e5939UL ,
+ 0x67332667ffc00b31UL , 0x8eb44a8768581511UL ,
+ 0xdb0c2e0d64f98fa7UL , 0x47b5481dbefa4fa4UL
+#else
+ ULLC(cbbb9d5d,c1059ed8), ULLC(629a292a,367cd507),
+ ULLC(9159015a,3070dd17), ULLC(152fecd8,f70e5939),
+ ULLC(67332667,ffc00b31), ULLC(8eb44a87,68581511),
+ ULLC(db0c2e0d,64f98fa7), ULLC(47b5481d,befa4fa4)
+#endif
+};
+
+SHA384Context *
+SHA384_NewContext(void)
+{
+ return SHA512_NewContext();
+}
+
+void
+SHA384_DestroyContext(SHA384Context *ctx, PRBool freeit)
+{
+ SHA512_DestroyContext(ctx, freeit);
+}
+
+void
+SHA384_Begin(SHA384Context *ctx)
+{
+ memset(ctx, 0, sizeof *ctx);
+ memcpy(H, H384, sizeof H384);
+}
+
+void
+SHA384_Update(SHA384Context *ctx, const unsigned char *input,
+ unsigned int inputLen)
+{
+ SHA512_Update(ctx, input, inputLen);
+}
+
+void
+SHA384_End(SHA384Context *ctx, unsigned char *digest,
+ unsigned int *digestLen, unsigned int maxDigestLen)
+{
+#define SHA_MIN(a,b) (a < b ? a : b)
+ unsigned int maxLen = SHA_MIN(maxDigestLen, SHA384_LENGTH);
+ SHA512_End(ctx, digest, digestLen, maxLen);
+}
+
+SECStatus
+SHA384_HashBuf(unsigned char *dest, const unsigned char *src,
+ unsigned int src_length)
+{
+ SHA512Context ctx;
+ unsigned int outLen;
+
+ SHA384_Begin(&ctx);
+ SHA512_Update(&ctx, src, src_length);
+ SHA512_End(&ctx, dest, &outLen, SHA384_LENGTH);
+
+ return SECSuccess;
+}
+
+SECStatus
+SHA384_Hash(unsigned char *dest, const char *src)
+{
+ return SHA384_HashBuf(dest, (const unsigned char *)src, PORT_Strlen(src));
+}
+
+void SHA384_TraceState(SHA384Context *ctx) { }
+
+unsigned int
+SHA384_FlattenSize(SHA384Context *ctx)
+{
+ return sizeof(SHA384Context);
+}
+
+SECStatus
+SHA384_Flatten(SHA384Context *ctx,unsigned char *space)
+{
+ return SHA512_Flatten(ctx, space);
+}
+
+SHA384Context *
+SHA384_Resurrect(unsigned char *space, void *arg)
+{
+ return SHA512_Resurrect(space, arg);
+}
+
+void SHA384_Clone(SHA384Context *dest, SHA384Context *src)
+{
+ memcpy(dest, src, sizeof *dest);
+}
+#endif /* Comment out unused code. */
+
+/* ======================================================================= */
+#ifdef SELFTEST
+#include <stdio.h>
+
+static const char abc[] = { "abc" };
+static const char abcdbc[] = {
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+};
+static const char abcdef[] = {
+ "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn"
+ "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"
+};
+
+void
+dumpHash32(const unsigned char *buf, unsigned int bufLen)
+{
+ unsigned int i;
+ for (i = 0; i < bufLen; i += 4) {
+ printf(" %02x%02x%02x%02x", buf[i], buf[i+1], buf[i+2], buf[i+3]);
+ }
+ printf("\n");
+}
+
+void test256(void)
+{
+ unsigned char outBuf[SHA256_LENGTH];
+
+ printf("SHA256, input = %s\n", abc);
+ SHA256_Hash(outBuf, abc);
+ dumpHash32(outBuf, sizeof outBuf);
+
+ printf("SHA256, input = %s\n", abcdbc);
+ SHA256_Hash(outBuf, abcdbc);
+ dumpHash32(outBuf, sizeof outBuf);
+}
+
+void
+dumpHash64(const unsigned char *buf, unsigned int bufLen)
+{
+ unsigned int i;
+ for (i = 0; i < bufLen; i += 8) {
+ if (i % 32 == 0)
+ printf("\n");
+ printf(" %02x%02x%02x%02x%02x%02x%02x%02x",
+ buf[i ], buf[i+1], buf[i+2], buf[i+3],
+ buf[i+4], buf[i+5], buf[i+6], buf[i+7]);
+ }
+ printf("\n");
+}
+
+void test512(void)
+{
+ unsigned char outBuf[SHA512_LENGTH];
+
+ printf("SHA512, input = %s\n", abc);
+ SHA512_Hash(outBuf, abc);
+ dumpHash64(outBuf, sizeof outBuf);
+
+ printf("SHA512, input = %s\n", abcdef);
+ SHA512_Hash(outBuf, abcdef);
+ dumpHash64(outBuf, sizeof outBuf);
+}
+
+void time512(void)
+{
+ unsigned char outBuf[SHA512_LENGTH];
+
+ SHA512_Hash(outBuf, abc);
+ SHA512_Hash(outBuf, abcdef);
+}
+
+void test384(void)
+{
+ unsigned char outBuf[SHA384_LENGTH];
+
+ printf("SHA384, input = %s\n", abc);
+ SHA384_Hash(outBuf, abc);
+ dumpHash64(outBuf, sizeof outBuf);
+
+ printf("SHA384, input = %s\n", abcdef);
+ SHA384_Hash(outBuf, abcdef);
+ dumpHash64(outBuf, sizeof outBuf);
+}
+
+int main (int argc, char *argv[], char *envp[])
+{
+ int i = 1;
+ if (argc > 1) {
+ i = atoi(argv[1]);
+ }
+ if (i < 2) {
+ test256();
+ test512();
+ test384();
+ } else {
+ while (i-- > 0) {
+ time512();
+ }
+ printf("done\n");
+ }
+ return 0;
+}
+
+#endif
diff --git a/crypto/wincrypt_shim.h b/crypto/wincrypt_shim.h
index dcfd4adbe5..48d4b5c5fa 100644
--- a/crypto/wincrypt_shim.h
+++ b/crypto/wincrypt_shim.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CRYPTO_WINCRYPT_SHIM_H_
-#define CRYPTO_WINCRYPT_SHIM_H_
+#ifndef NET_CRYPTO_WINCRYPT_SHIM_H_
+#define NET_CRYPTO_WINCRYPT_SHIM_H_
// wincrypt.h defines macros which conflict with OpenSSL's types. This header
// includes wincrypt and undefines the OpenSSL macros which conflict. Any
@@ -22,4 +22,4 @@
#define WINCRYPT_X509_EXTENSIONS ((LPCSTR) 5)
#define WINCRYPT_X509_NAME ((LPCSTR) 7)
-#endif // CRYPTO_WINCRYPT_SHIM_H_
+#endif // NET_CRYPTO_WINCRYPT_SHIM_H_
diff --git a/dbus/BUILD.gn b/dbus/BUILD.gn
index c0bd77d8db..28efb93fe4 100644
--- a/dbus/BUILD.gn
+++ b/dbus/BUILD.gn
@@ -17,6 +17,8 @@ component("dbus") {
"dbus_statistics.h",
"exported_object.cc",
"exported_object.h",
+ "file_descriptor.cc",
+ "file_descriptor.h",
"message.cc",
"message.h",
"object_manager.cc",
diff --git a/dbus/bus.cc b/dbus/bus.cc
index b6a13d6b15..57834d348a 100644
--- a/dbus/bus.cc
+++ b/dbus/bus.cc
@@ -26,11 +26,6 @@ namespace dbus {
namespace {
-const char kDisconnectedSignal[] = "Disconnected";
-const char kDisconnectedMatchRule[] =
- "type='signal', path='/org/freedesktop/DBus/Local',"
- "interface='org.freedesktop.DBus.Local', member='Disconnected'";
-
// The NameOwnerChanged member in org.freedesktop.DBus
const char kNameOwnerChangedSignal[] = "NameOwnerChanged";
@@ -45,7 +40,7 @@ const char kServiceNameOwnerChangeMatchRule[] =
class Watch : public base::MessagePumpLibevent::Watcher {
public:
explicit Watch(DBusWatch* watch)
- : raw_watch_(watch), file_descriptor_watcher_(FROM_HERE) {
+ : raw_watch_(watch) {
dbus_watch_set_data(raw_watch_, this, NULL);
}
@@ -400,6 +395,13 @@ void Bus::RemoveObjectManagerInternalHelper(
callback.Run();
}
+void Bus::GetManagedObjects() {
+ for (ObjectManagerTable::iterator iter = object_manager_table_.begin();
+ iter != object_manager_table_.end(); ++iter) {
+ iter->second->GetManagedObjects();
+ }
+}
+
bool Bus::Connect() {
// dbus_bus_get_private() and dbus_bus_get() are blocking calls.
AssertOnDBusThread();
@@ -441,12 +443,6 @@ bool Bus::Connect() {
return false;
}
}
- // We shouldn't exit on the disconnected signal.
- dbus_connection_set_exit_on_disconnect(connection_, false);
-
- // Watch Disconnected signal.
- AddFilterFunction(Bus::OnConnectionDisconnectedFilter, this);
- AddMatch(kDisconnectedMatchRule, error.get());
return true;
}
@@ -506,8 +502,6 @@ void Bus::ShutdownAndBlock() {
if (connection_) {
// Remove Disconnected watcher.
ScopedDBusError error;
- RemoveFilterFunction(Bus::OnConnectionDisconnectedFilter, this);
- RemoveMatch(kDisconnectedMatchRule, error.get());
if (connection_type_ == PRIVATE)
ClosePrivateConnection();
@@ -1186,20 +1180,6 @@ void Bus::OnDispatchStatusChangedThunk(DBusConnection* connection,
}
// static
-DBusHandlerResult Bus::OnConnectionDisconnectedFilter(
- DBusConnection* /*connection*/,
- DBusMessage* message,
- void* /*data*/) {
- if (dbus_message_is_signal(message,
- DBUS_INTERFACE_LOCAL,
- kDisconnectedSignal)) {
- // Abort when the connection is lost.
- LOG(FATAL) << "D-Bus connection was disconnected. Aborting.";
- }
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
-
-// static
DBusHandlerResult Bus::OnServiceOwnerChangedFilter(
DBusConnection* /*connection*/,
DBusMessage* message,
diff --git a/dbus/bus.h b/dbus/bus.h
index 59a19720ec..7d3915909b 100644
--- a/dbus/bus.h
+++ b/dbus/bus.h
@@ -28,6 +28,10 @@ class SingleThreadTaskRunner;
class TaskRunner;
}
+namespace tracked_objects {
+class Location;
+}
+
namespace dbus {
class ExportedObject;
@@ -356,6 +360,12 @@ class CHROME_DBUS_EXPORT Bus : public base::RefCountedThreadSafe<Bus> {
const ObjectPath& object_path,
const base::Closure& callback);
+ // Instructs all registered object managers to retrieve their set of managed
+ // objects from their respective remote objects. There is no need to call this
+ // manually, this is called automatically by the D-Bus thread manager once
+ // implementation classes are registered.
+ virtual void GetManagedObjects();
+
// Shuts down the bus and blocks until it's done. More specifically, this
// function does the following:
//
diff --git a/dbus/dbus.gyp b/dbus/dbus.gyp
new file mode 100644
index 0000000000..264383ee4b
--- /dev/null
+++ b/dbus/dbus.gyp
@@ -0,0 +1,141 @@
+# 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.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'targets': [
+ {
+ 'target_name': 'dbus',
+ 'type': '<(component)',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../build/linux/system.gyp:dbus',
+ '../third_party/protobuf/protobuf.gyp:protobuf_lite',
+ ],
+ 'export_dependent_settings': [
+ '../base/base.gyp:base',
+ ],
+ 'defines': [
+ 'DBUS_IMPLEMENTATION',
+ ],
+ 'sources': [
+ 'bus.cc',
+ 'bus.h',
+ 'dbus_export.h',
+ 'dbus_statistics.cc',
+ 'dbus_statistics.h',
+ 'exported_object.cc',
+ 'exported_object.h',
+ 'file_descriptor.cc',
+ 'file_descriptor.h',
+ 'message.cc',
+ 'message.h',
+ 'object_manager.cc',
+ 'object_manager.h',
+ 'object_path.cc',
+ 'object_path.h',
+ 'object_proxy.cc',
+ 'object_proxy.h',
+ 'property.cc',
+ 'property.h',
+ 'scoped_dbus_error.cc',
+ 'scoped_dbus_error.h',
+ 'string_util.cc',
+ 'string_util.h',
+ 'util.cc',
+ 'util.h',
+ 'values_util.cc',
+ 'values_util.h',
+ ],
+ },
+ {
+ # Protobuf compiler / generator test protocol buffer
+ 'target_name': 'dbus_test_proto',
+ 'type': 'static_library',
+ 'sources': [ 'test_proto.proto' ],
+ 'variables': {
+ 'proto_out_dir': 'dbus',
+ },
+ 'includes': [ '../build/protoc.gypi' ],
+ },
+ {
+ # This target contains mocks that can be used to write unit tests
+ # without issuing actual D-Bus calls.
+ 'target_name': 'dbus_test_support',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../build/linux/system.gyp:dbus',
+ '../testing/gmock.gyp:gmock',
+ 'dbus',
+ ],
+ 'sources': [
+ 'mock_bus.cc',
+ 'mock_bus.h',
+ 'mock_exported_object.cc',
+ 'mock_exported_object.h',
+ 'mock_object_manager.cc',
+ 'mock_object_manager.h',
+ 'mock_object_proxy.cc',
+ 'mock_object_proxy.h',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ {
+ 'target_name': 'dbus_unittests',
+ 'type': 'executable',
+ 'dependencies': [
+ '../base/base.gyp:run_all_unittests',
+ '../base/base.gyp:test_support_base',
+ '../build/linux/system.gyp:dbus',
+ '../testing/gmock.gyp:gmock',
+ '../testing/gtest.gyp:gtest',
+ 'dbus',
+ 'dbus_test_proto',
+ 'dbus_test_support',
+ ],
+ 'sources': [
+ 'bus_unittest.cc',
+ 'dbus_statistics_unittest.cc',
+ 'end_to_end_async_unittest.cc',
+ 'end_to_end_sync_unittest.cc',
+ 'message_unittest.cc',
+ 'mock_unittest.cc',
+ 'object_manager_unittest.cc',
+ 'object_proxy_unittest.cc',
+ 'property_unittest.cc',
+ 'signal_sender_verification_unittest.cc',
+ 'string_util_unittest.cc',
+ 'test_service.cc',
+ 'test_service.h',
+ 'util_unittest.cc',
+ 'values_util_unittest.cc',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ {
+ 'target_name': 'dbus_test_server',
+ 'type': 'executable',
+ 'dependencies': [
+ '../base/base.gyp:test_support_base',
+ '../base/base.gyp:base',
+ '../build/linux/system.gyp:dbus',
+ 'dbus',
+ ],
+ 'sources': [
+ 'test_server.cc',
+ 'test_service.cc',
+ 'test_service.h',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ ],
+}
diff --git a/dbus/dbus_statistics.cc b/dbus/dbus_statistics.cc
index 2949c5032d..e1e0973d5c 100644
--- a/dbus/dbus_statistics.cc
+++ b/dbus/dbus_statistics.cc
@@ -4,11 +4,12 @@
#include "dbus/dbus_statistics.h"
-#include <map>
-#include <tuple>
+#include <memory>
+#include <set>
#include "base/logging.h"
#include "base/macros.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
@@ -17,24 +18,43 @@ namespace dbus {
namespace {
-struct StatKey {
+// Used to store dbus statistics sorted alphabetically by service, interface,
+// then method (using std::string <).
+struct Stat {
+ Stat(const std::string& service,
+ const std::string& interface,
+ const std::string& method)
+ : service(service),
+ interface(interface),
+ method(method),
+ sent_method_calls(0),
+ received_signals(0),
+ sent_blocking_method_calls(0) {
+ }
std::string service;
std::string interface;
std::string method;
-};
-
-bool operator<(const StatKey& lhs, const StatKey& rhs) {
- return std::tie(lhs.service, lhs.interface, lhs.method) <
- std::tie(rhs.service, rhs.interface, rhs.method);
-}
+ int sent_method_calls;
+ int received_signals;
+ int sent_blocking_method_calls;
+
+ bool Compare(const Stat& other) const {
+ if (service != other.service)
+ return service < other.service;
+ if (interface != other.interface)
+ return interface < other.interface;
+ return method < other.method;
+ }
-struct StatValue {
- int sent_method_calls = 0;
- int received_signals = 0;
- int sent_blocking_method_calls = 0;
+ struct PtrCompare {
+ bool operator()(Stat* lhs, Stat* rhs) const {
+ DCHECK(lhs && rhs);
+ return lhs->Compare(*rhs);
+ }
+ };
};
-using StatMap = std::map<StatKey, StatValue>;
+typedef std::set<Stat*, Stat::PtrCompare> StatSet;
//------------------------------------------------------------------------------
// DBusStatistics
@@ -49,9 +69,10 @@ class DBusStatistics {
~DBusStatistics() {
DCHECK_EQ(origin_thread_id_, base::PlatformThread::CurrentId());
+ STLDeleteContainerPointers(stats_.begin(), stats_.end());
}
- // Enum to specify which field in Stat to increment in AddStat.
+ // Enum to specify which field in Stat to increment in AddStat
enum StatType {
TYPE_SENT_METHOD_CALLS,
TYPE_RECEIVED_SIGNALS,
@@ -68,7 +89,7 @@ class DBusStatistics {
<< base::PlatformThread::CurrentId();
return;
}
- StatValue* stat = GetStats(service, interface, method, true);
+ Stat* stat = GetStat(service, interface, method, true);
DCHECK(stat);
if (type == TYPE_SENT_METHOD_CALLS)
++stat->sent_method_calls;
@@ -82,35 +103,33 @@ class DBusStatistics {
// Look up the Stat entry in |stats_|. If |add_stat| is true, add a new entry
// if one does not already exist.
- StatValue* GetStats(const std::string& service,
- const std::string& interface,
- const std::string& method,
- bool add_stat) {
+ Stat* GetStat(const std::string& service,
+ const std::string& interface,
+ const std::string& method,
+ bool add_stat) {
DCHECK_EQ(origin_thread_id_, base::PlatformThread::CurrentId());
-
- StatKey key = {service, interface, method};
- auto it = stats_.find(key);
- if (it != stats_.end())
- return &(it->second);
-
+ std::unique_ptr<Stat> stat(new Stat(service, interface, method));
+ StatSet::iterator found = stats_.find(stat.get());
+ if (found != stats_.end())
+ return *found;
if (!add_stat)
- return nullptr;
-
- return &(stats_[key]);
+ return NULL;
+ found = stats_.insert(stat.release()).first;
+ return *found;
}
- StatMap& stats() { return stats_; }
+ StatSet& stats() { return stats_; }
base::Time start_time() { return start_time_; }
private:
- StatMap stats_;
+ StatSet stats_;
base::Time start_time_;
base::PlatformThreadId origin_thread_id_;
DISALLOW_COPY_AND_ASSIGN(DBusStatistics);
};
-DBusStatistics* g_dbus_statistics = nullptr;
+DBusStatistics* g_dbus_statistics = NULL;
} // namespace
@@ -126,7 +145,7 @@ void Initialize() {
void Shutdown() {
delete g_dbus_statistics;
- g_dbus_statistics = nullptr;
+ g_dbus_statistics = NULL;
}
void AddSentMethodCall(const std::string& service,
@@ -163,7 +182,7 @@ std::string GetAsString(ShowInString show, FormatString format) {
if (!g_dbus_statistics)
return "DBusStatistics not initialized.";
- const StatMap& stats = g_dbus_statistics->stats();
+ const StatSet& stats = g_dbus_statistics->stats();
if (stats.empty())
return "No DBus calls.";
@@ -174,21 +193,19 @@ std::string GetAsString(ShowInString show, FormatString format) {
std::string result;
int sent = 0, received = 0, sent_blocking = 0;
// Stats are stored in order by service, then interface, then method.
- for (auto iter = stats.begin(); iter != stats.end();) {
- auto cur_iter = iter;
- auto next_iter = ++iter;
- const StatKey& stat_key = cur_iter->first;
- const StatValue& stat = cur_iter->second;
- sent += stat.sent_method_calls;
- received += stat.received_signals;
- sent_blocking += stat.sent_blocking_method_calls;
+ for (StatSet::const_iterator iter = stats.begin(); iter != stats.end(); ) {
+ StatSet::const_iterator cur_iter = iter;
+ StatSet::const_iterator next_iter = ++iter;
+ const Stat* stat = *cur_iter;
+ sent += stat->sent_method_calls;
+ received += stat->received_signals;
+ sent_blocking += stat->sent_blocking_method_calls;
// If this is not the last stat, and if the next stat matches the current
// stat, continue.
if (next_iter != stats.end() &&
- next_iter->first.service == stat_key.service &&
- (show < SHOW_INTERFACE ||
- next_iter->first.interface == stat_key.interface) &&
- (show < SHOW_METHOD || next_iter->first.method == stat_key.method))
+ (*next_iter)->service == stat->service &&
+ (show < SHOW_INTERFACE || (*next_iter)->interface == stat->interface) &&
+ (show < SHOW_METHOD || (*next_iter)->method == stat->method))
continue;
if (!sent && !received && !sent_blocking)
@@ -197,12 +214,12 @@ std::string GetAsString(ShowInString show, FormatString format) {
// Add a line to the result and clear the counts.
std::string line;
if (show == SHOW_SERVICE) {
- line += stat_key.service;
+ line += stat->service;
} else {
// The interface usually includes the service so don't show both.
- line += stat_key.interface;
+ line += stat->interface;
if (show >= SHOW_METHOD)
- line += "." + stat_key.method;
+ line += "." + stat->method;
}
line += base::StringPrintf(":");
if (sent_blocking) {
@@ -252,8 +269,7 @@ bool GetCalls(const std::string& service,
int* blocking) {
if (!g_dbus_statistics)
return false;
- StatValue* stat =
- g_dbus_statistics->GetStats(service, interface, method, false);
+ Stat* stat = g_dbus_statistics->GetStat(service, interface, method, false);
if (!stat)
return false;
*sent = stat->sent_method_calls;
diff --git a/dbus/exported_object.cc b/dbus/exported_object.cc
index ffc5eb391d..b156308ace 100644
--- a/dbus/exported_object.cc
+++ b/dbus/exported_object.cc
@@ -11,7 +11,7 @@
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
-#include "base/metrics/histogram_macros.h"
+#include "base/metrics/histogram.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
#include "dbus/bus.h"
@@ -186,9 +186,8 @@ bool ExportedObject::Register() {
return true;
}
-DBusHandlerResult ExportedObject::HandleMessage(
- DBusConnection* /*connection*/,
- DBusMessage* raw_message) {
+DBusHandlerResult ExportedObject::HandleMessage(DBusConnection*,
+ DBusMessage* raw_message) {
bus_->AssertOnDBusThread();
DCHECK_EQ(DBUS_MESSAGE_TYPE_METHOD_CALL, dbus_message_get_type(raw_message));
@@ -301,8 +300,7 @@ void ExportedObject::OnMethodCompleted(std::unique_ptr<MethodCall> method_call,
base::TimeTicks::Now() - start_time);
}
-void ExportedObject::OnUnregistered(DBusConnection* /*connection*/) {
-}
+void ExportedObject::OnUnregistered(DBusConnection*) {}
DBusHandlerResult ExportedObject::HandleMessageThunk(
DBusConnection* connection,
diff --git a/dbus/file_descriptor.cc b/dbus/file_descriptor.cc
new file mode 100644
index 0000000000..b690881749
--- /dev/null
+++ b/dbus/file_descriptor.cc
@@ -0,0 +1,68 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/files/file.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/threading/worker_pool.h"
+#include "dbus/file_descriptor.h"
+
+using std::swap;
+
+namespace dbus {
+
+void CHROME_DBUS_EXPORT FileDescriptor::Deleter::operator()(
+ FileDescriptor* fd) {
+ base::WorkerPool::PostTask(
+ FROM_HERE, base::Bind(&base::DeletePointer<FileDescriptor>, fd), false);
+}
+
+FileDescriptor::FileDescriptor(FileDescriptor&& other) : FileDescriptor() {
+ Swap(&other);
+}
+
+FileDescriptor::~FileDescriptor() {
+ if (owner_)
+ base::File auto_closer(value_);
+}
+
+FileDescriptor& FileDescriptor::operator=(FileDescriptor&& other) {
+ Swap(&other);
+ return *this;
+}
+
+int FileDescriptor::value() const {
+ CHECK(valid_);
+ return value_;
+}
+
+int FileDescriptor::TakeValue() {
+ CHECK(valid_); // NB: check first so owner_ is unchanged if this triggers
+ owner_ = false;
+ return value_;
+}
+
+void FileDescriptor::CheckValidity() {
+ base::File file(value_);
+ if (!file.IsValid()) {
+ valid_ = false;
+ return;
+ }
+
+ base::File::Info info;
+ bool ok = file.GetInfo(&info);
+ file.TakePlatformFile(); // Prevent |value_| from being closed by |file|.
+ valid_ = (ok && !info.is_directory);
+}
+
+void FileDescriptor::Swap(FileDescriptor* other) {
+ swap(value_, other->value_);
+ swap(owner_, other->owner_);
+ swap(valid_, other->valid_);
+}
+
+} // namespace dbus
diff --git a/dbus/file_descriptor.h b/dbus/file_descriptor.h
new file mode 100644
index 0000000000..f8e86777ea
--- /dev/null
+++ b/dbus/file_descriptor.h
@@ -0,0 +1,92 @@
+// 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 DBUS_FILE_DESCRIPTOR_H_
+#define DBUS_FILE_DESCRIPTOR_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "dbus/dbus_export.h"
+
+namespace dbus {
+
+// FileDescriptor is a type used to encapsulate D-Bus file descriptors
+// and to follow the RAII idiom appropiate for use with message operations
+// where the descriptor might be easily leaked. To guard against this the
+// descriptor is closed when an instance is destroyed if it is owned.
+// Ownership is asserted only when PutValue is used and TakeValue can be
+// used to take ownership.
+//
+// For example, in the following
+// FileDescriptor fd;
+// if (!reader->PopString(&name) ||
+// !reader->PopFileDescriptor(&fd) ||
+// !reader->PopUint32(&flags)) {
+// the descriptor in fd will be closed if the PopUint32 fails. But
+// writer.AppendFileDescriptor(dbus::FileDescriptor(1));
+// will not automatically close "1" because it is not owned.
+//
+// Descriptors must be validated before marshalling in a D-Bus message
+// or using them after unmarshalling. We disallow descriptors to a
+// directory to reduce the security risks. Splitting out validation
+// also allows the caller to do this work on the File thread to conform
+// with i/o restrictions.
+class CHROME_DBUS_EXPORT FileDescriptor {
+ public:
+ // This provides a simple way to pass around file descriptors since they must
+ // be closed on a thread that is allowed to perform I/O.
+ struct Deleter {
+ void CHROME_DBUS_EXPORT operator()(FileDescriptor* fd);
+ };
+
+ // Permits initialization without a value for passing to
+ // dbus::MessageReader::PopFileDescriptor to fill in and from int values.
+ FileDescriptor() : value_(-1), owner_(false), valid_(false) {}
+ explicit FileDescriptor(int value) : value_(value), owner_(false),
+ valid_(false) {}
+
+ FileDescriptor(FileDescriptor&& other);
+
+ virtual ~FileDescriptor();
+
+ FileDescriptor& operator=(FileDescriptor&& other);
+
+ // Retrieves value as an int without affecting ownership.
+ int value() const;
+
+ // Retrieves whether or not the descriptor is ok to send/receive.
+ int is_valid() const { return valid_; }
+
+ // Sets the value and assign ownership.
+ void PutValue(int value) {
+ value_ = value;
+ owner_ = true;
+ valid_ = false;
+ }
+
+ // Takes the value and ownership.
+ int TakeValue();
+
+ // Checks (and records) validity of the file descriptor.
+ // We disallow directories to avoid potential sandbox escapes.
+ // Note this call must be made on a thread where file i/o is allowed.
+ void CheckValidity();
+
+ private:
+ void Swap(FileDescriptor* other);
+
+ int value_;
+ bool owner_;
+ bool valid_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileDescriptor);
+};
+
+using ScopedFileDescriptor =
+ std::unique_ptr<FileDescriptor, FileDescriptor::Deleter>;
+
+} // namespace dbus
+
+#endif // DBUS_FILE_DESCRIPTOR_H_
diff --git a/dbus/message.cc b/dbus/message.cc
index c8663f72ad..4a84756c41 100644
--- a/dbus/message.cc
+++ b/dbus/message.cc
@@ -222,11 +222,11 @@ std::string Message::ToStringInternal(const std::string& indent,
case UNIX_FD: {
CHECK(IsDBusTypeUnixFdSupported());
- base::ScopedFD file_descriptor;
+ FileDescriptor file_descriptor;
if (!reader->PopFileDescriptor(&file_descriptor))
return kBrokenMessage;
output += indent + "fd#" +
- base::IntToString(file_descriptor.get()) + "\n";
+ base::IntToString(file_descriptor.value()) + "\n";
break;
}
default:
@@ -714,9 +714,15 @@ void MessageWriter::AppendVariantOfBasic(int dbus_type, const void* value) {
CloseContainer(&variant_writer);
}
-void MessageWriter::AppendFileDescriptor(int value) {
+void MessageWriter::AppendFileDescriptor(const FileDescriptor& value) {
CHECK(IsDBusTypeUnixFdSupported());
- AppendBasic(DBUS_TYPE_UNIX_FD, &value); // This duplicates the FD.
+
+ if (!value.is_valid()) {
+ // NB: sending a directory potentially enables sandbox escape
+ LOG(FATAL) << "Attempt to pass invalid file descriptor";
+ }
+ int fd = value.value();
+ AppendBasic(DBUS_TYPE_UNIX_FD, &fd);
}
//
@@ -1010,7 +1016,7 @@ bool MessageReader::PopVariantOfBasic(int dbus_type, void* value) {
return variant_reader.PopBasic(dbus_type, value);
}
-bool MessageReader::PopFileDescriptor(base::ScopedFD* value) {
+bool MessageReader::PopFileDescriptor(FileDescriptor* value) {
CHECK(IsDBusTypeUnixFdSupported());
int fd = -1;
@@ -1018,7 +1024,8 @@ bool MessageReader::PopFileDescriptor(base::ScopedFD* value) {
if (!success)
return false;
- *value = base::ScopedFD(fd);
+ value->PutValue(fd);
+ // NB: the caller must check validity before using the value
return true;
}
diff --git a/dbus/message.h b/dbus/message.h
index 256a8428c5..0aa010ccde 100644
--- a/dbus/message.h
+++ b/dbus/message.h
@@ -13,9 +13,9 @@
#include <string>
#include <vector>
-#include "base/files/scoped_file.h"
#include "base/macros.h"
#include "dbus/dbus_export.h"
+#include "dbus/file_descriptor.h"
#include "dbus/object_path.h"
namespace google {
@@ -285,10 +285,7 @@ class CHROME_DBUS_EXPORT MessageWriter {
void AppendDouble(double value);
void AppendString(const std::string& value);
void AppendObjectPath(const ObjectPath& value);
-
- // Appends a file descriptor to the message.
- // The FD will be duplicated so you still have to close the original FD.
- void AppendFileDescriptor(int value);
+ void AppendFileDescriptor(const FileDescriptor& value);
// Opens an array. The array contents can be added to the array with
// |sub_writer|. The client code must close the array with
@@ -401,7 +398,7 @@ class CHROME_DBUS_EXPORT MessageReader {
bool PopDouble(double* value);
bool PopString(std::string* value);
bool PopObjectPath(ObjectPath* value);
- bool PopFileDescriptor(base::ScopedFD* value);
+ bool PopFileDescriptor(FileDescriptor* value);
// Sets up the given message reader to read an array at the current
// iterator position.
diff --git a/dbus/mock_bus.h b/dbus/mock_bus.h
index 40b090b156..b50f230569 100644
--- a/dbus/mock_bus.h
+++ b/dbus/mock_bus.h
@@ -26,6 +26,15 @@ class MockBus : public Bus {
ObjectProxy*(const std::string& service_name,
const ObjectPath& object_path,
int options));
+ MOCK_METHOD3(RemoveObjectProxy, bool(
+ const std::string& service_name,
+ const ObjectPath& object_path,
+ const base::Closure& callback));
+ MOCK_METHOD4(RemoveObjectProxyWithOptions, bool(
+ const std::string& service_name,
+ const ObjectPath& object_path,
+ int options,
+ const base::Closure& callback));
MOCK_METHOD1(GetExportedObject, ExportedObject*(
const ObjectPath& object_path));
MOCK_METHOD2(GetObjectManager, ObjectManager*(const std::string&,
diff --git a/dbus/mock_object_manager.h b/dbus/mock_object_manager.h
index 2318e497ea..e4c76ba711 100644
--- a/dbus/mock_object_manager.h
+++ b/dbus/mock_object_manager.h
@@ -31,6 +31,7 @@ class MockObjectManager : public ObjectManager {
MOCK_METHOD1(GetObjectProxy, ObjectProxy*(const ObjectPath&));
MOCK_METHOD2(GetProperties, PropertySet*(const ObjectPath&,
const std::string&));
+ MOCK_METHOD0(GetManagedObjects, void());
protected:
virtual ~MockObjectManager();
diff --git a/dbus/mock_object_proxy.h b/dbus/mock_object_proxy.h
index 17d2a9f0f4..f27f6f6acc 100644
--- a/dbus/mock_object_proxy.h
+++ b/dbus/mock_object_proxy.h
@@ -56,10 +56,6 @@ class MockObjectProxy : public ObjectProxy {
const std::string& signal_name,
SignalCallback signal_callback,
OnConnectedCallback on_connected_callback));
- MOCK_METHOD1(SetNameOwnerChangedCallback,
- void(NameOwnerChangedCallback callback));
- MOCK_METHOD1(WaitForServiceToBeAvailable,
- void(WaitForServiceToBeAvailableCallback callback));
MOCK_METHOD0(Detach, void());
protected:
diff --git a/dbus/object_manager.cc b/dbus/object_manager.cc
index 08e6e048e2..178bb5ff12 100644
--- a/dbus/object_manager.cc
+++ b/dbus/object_manager.cc
@@ -167,6 +167,35 @@ void ObjectManager::CleanUp() {
match_rule_.clear();
}
+void ObjectManager::InitializeObjects() {
+ DCHECK(bus_);
+ DCHECK(object_proxy_);
+ DCHECK(setup_success_);
+
+ // |object_proxy_| is no longer valid if the Bus was shut down before this
+ // call. Don't initiate any other action from the origin thread.
+ if (cleanup_called_)
+ return;
+
+ object_proxy_->ConnectToSignal(
+ kObjectManagerInterface,
+ kObjectManagerInterfacesAdded,
+ base::Bind(&ObjectManager::InterfacesAddedReceived,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::Bind(&ObjectManager::InterfacesAddedConnected,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ object_proxy_->ConnectToSignal(
+ kObjectManagerInterface,
+ kObjectManagerInterfacesRemoved,
+ base::Bind(&ObjectManager::InterfacesRemovedReceived,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::Bind(&ObjectManager::InterfacesRemovedConnected,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ GetManagedObjects();
+}
+
bool ObjectManager::SetupMatchRuleAndFilter() {
DCHECK(bus_);
DCHECK(!setup_success_);
@@ -206,39 +235,10 @@ bool ObjectManager::SetupMatchRuleAndFilter() {
}
void ObjectManager::OnSetupMatchRuleAndFilterComplete(bool success) {
- if (!success) {
- LOG(WARNING) << service_name_ << " " << object_path_.value()
- << ": Failed to set up match rule.";
- return;
- }
-
- DCHECK(bus_);
- DCHECK(object_proxy_);
- DCHECK(setup_success_);
-
- // |object_proxy_| is no longer valid if the Bus was shut down before this
- // call. Don't initiate any other action from the origin thread.
- if (cleanup_called_)
- return;
-
- object_proxy_->ConnectToSignal(
- kObjectManagerInterface,
- kObjectManagerInterfacesAdded,
- base::Bind(&ObjectManager::InterfacesAddedReceived,
- weak_ptr_factory_.GetWeakPtr()),
- base::Bind(&ObjectManager::InterfacesAddedConnected,
- weak_ptr_factory_.GetWeakPtr()));
-
- object_proxy_->ConnectToSignal(
- kObjectManagerInterface,
- kObjectManagerInterfacesRemoved,
- base::Bind(&ObjectManager::InterfacesRemovedReceived,
- weak_ptr_factory_.GetWeakPtr()),
- base::Bind(&ObjectManager::InterfacesRemovedConnected,
- weak_ptr_factory_.GetWeakPtr()));
-
- if (!service_name_owner_.empty())
- GetManagedObjects();
+ LOG_IF(WARNING, !success) << service_name_ << " " << object_path_.value()
+ << ": Failed to set up match rule.";
+ if (success)
+ InitializeObjects();
}
// static
@@ -249,7 +249,7 @@ DBusHandlerResult ObjectManager::HandleMessageThunk(DBusConnection* connection,
return self->HandleMessage(connection, raw_message);
}
-DBusHandlerResult ObjectManager::HandleMessage(DBusConnection* /*connection*/,
+DBusHandlerResult ObjectManager::HandleMessage(DBusConnection*,
DBusMessage* raw_message) {
DCHECK(bus_);
bus_->AssertOnDBusThread();
@@ -385,9 +385,10 @@ void ObjectManager::InterfacesAddedReceived(Signal* signal) {
UpdateObject(object_path, &reader);
}
-void ObjectManager::InterfacesAddedConnected(const std::string& /*interface_name*/,
- const std::string& /*signal_name*/,
- bool success) {
+void ObjectManager::InterfacesAddedConnected(
+ const std::string& /*interface_name*/,
+ const std::string& /*signal_name*/,
+ bool success) {
LOG_IF(WARNING, !success) << service_name_ << " " << object_path_.value()
<< ": Failed to connect to InterfacesAdded signal.";
}
diff --git a/dbus/object_manager.h b/dbus/object_manager.h
index 90cf919639..a97495e1f9 100644
--- a/dbus/object_manager.h
+++ b/dbus/object_manager.h
@@ -71,9 +71,8 @@
// object_manager_->UnregisterInterface(kInterface);
// }
//
-// This class calls GetManagedObjects() asynchronously after the remote service
-// becomes available and additionally refreshes managed objects after the
-// service stops or restarts.
+// The D-Bus thread manager takes care of issuing the necessary call to
+// GetManagedObjects() after the implementation classes have been set up.
//
// The object manager interface class has one abstract method that must be
// implemented by the class to create Properties structures on demand. As well
@@ -168,7 +167,7 @@ public:
// |interface_name| as appropriate. An implementation class will only
// receive multiple calls if it has registered for multiple interfaces.
virtual void ObjectAdded(const ObjectPath& /*object_path*/,
- const std::string& /*interface_name*/) { }
+ const std::string& /*interface_name*/) {}
// Called by ObjectManager to inform the implementation class than an
// object with the path |object_path| has been removed. Ths D-Bus interface
@@ -180,7 +179,7 @@ public:
// ObjectProxy object for the given interface are cleaned up, it is safe
// to retrieve them during removal to vary processing.
virtual void ObjectRemoved(const ObjectPath& /*object_path*/,
- const std::string& /*interface_name*/) { }
+ const std::string& /*interface_name*/) {}
};
// Client code should use Bus::GetObjectManager() instead of this constructor.
@@ -239,14 +238,17 @@ public:
private:
friend class base::RefCountedThreadSafe<ObjectManager>;
+ // Connects the InterfacesAdded and InterfacesRemoved signals and calls
+ // GetManagedObjects. Called from OnSetupMatchRuleAndFilterComplete.
+ void InitializeObjects();
+
// Called from the constructor to add a match rule for PropertiesChanged
- // signals on the D-Bus thread and set up a corresponding filter function.
+ // signals on the DBus thread and set up a corresponding filter function.
bool SetupMatchRuleAndFilter();
// Called on the origin thread once the match rule and filter have been set
- // up. Connects the InterfacesAdded and InterfacesRemoved signals and
- // refreshes objects if the service is available. |success| is false if an
- // error occurred during setup and true otherwise.
+ // up. |success| is false, if an error occurred during set up; it's true
+ // otherwise.
void OnSetupMatchRuleAndFilterComplete(bool success);
// Called by dbus:: when a message is received. This is used to filter
diff --git a/dbus/object_proxy.cc b/dbus/object_proxy.cc
index 50e62a3a99..ce0255154a 100644
--- a/dbus/object_proxy.cc
+++ b/dbus/object_proxy.cc
@@ -10,7 +10,7 @@
#include "base/bind.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
-#include "base/metrics/histogram_macros.h"
+#include "base/metrics/histogram.h"
#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
#include "base/task_runner_util.h"
@@ -459,9 +459,8 @@ void ObjectProxy::WaitForServiceToBeAvailableInternal() {
}
}
-DBusHandlerResult ObjectProxy::HandleMessage(
- DBusConnection* /*connection*/,
- DBusMessage* raw_message) {
+DBusHandlerResult ObjectProxy::HandleMessage(DBusConnection*,
+ DBusMessage* raw_message) {
bus_->AssertOnDBusThread();
if (dbus_message_get_type(raw_message) != DBUS_MESSAGE_TYPE_SIGNAL)
diff --git a/dbus/object_proxy.h b/dbus/object_proxy.h
index 5de390461e..033e88608a 100644
--- a/dbus/object_proxy.h
+++ b/dbus/object_proxy.h
@@ -137,10 +137,10 @@ class CHROME_DBUS_EXPORT ObjectProxy
// from the method (i.e. calling a method that does not return a value),
// EmptyResponseCallback() can be passed to the |callback| parameter.
//
- // If the method call is successful, |callback| will be invoked with a
- // Response object. If unsuccessful, |error_callback| will be invoked with an
- // ErrorResponse object (if the remote object returned an error) or nullptr
- // (if a response was not received at all).
+ // If the method call is successful, a pointer to Response object will
+ // be passed to the callback. If unsuccessful, the error callback will be
+ // called and a pointer to ErrorResponse object will be passed to the error
+ // callback if available, otherwise NULL will be passed.
//
// Must be called in the origin thread.
virtual void CallMethodWithErrorCallback(MethodCall* method_call,
@@ -174,11 +174,7 @@ class CHROME_DBUS_EXPORT ObjectProxy
// represented by |service_name_|.
virtual void SetNameOwnerChangedCallback(NameOwnerChangedCallback callback);
- // Registers |callback| to run when the service becomes available. If the
- // service is already available, or if connecting to the name-owner-changed
- // signal fails, |callback| will be run once asynchronously. Otherwise,
- // |callback| will be run once in the future after the service becomes
- // available.
+ // Runs the callback as soon as the service becomes available.
virtual void WaitForServiceToBeAvailable(
WaitForServiceToBeAvailableCallback callback);
diff --git a/dbus/property.cc b/dbus/property.cc
index 93f9ed693c..aa58436f51 100644
--- a/dbus/property.cc
+++ b/dbus/property.cc
@@ -6,8 +6,6 @@
#include <stddef.h>
-#include <memory>
-
#include "base/bind.h"
#include "base/logging.h"
@@ -661,134 +659,6 @@ void Property<std::vector<std::pair<std::vector<uint8_t>, uint16_t>>>::
writer->CloseContainer(&variant_writer);
}
-//
-// Property<std::unordered_map<std::string, std::vector<uint8_t>>>
-// specialization.
-//
-
-template <>
-bool Property<std::unordered_map<std::string, std::vector<uint8_t>>>::
- PopValueFromReader(MessageReader* reader) {
- MessageReader variant_reader(nullptr);
- MessageReader dict_reader(nullptr);
- if (!reader->PopVariant(&variant_reader) ||
- !variant_reader.PopArray(&dict_reader))
- return false;
-
- value_.clear();
- while (dict_reader.HasMoreData()) {
- MessageReader entry_reader(nullptr);
- if (!dict_reader.PopDictEntry(&entry_reader))
- return false;
-
- std::string key;
- MessageReader value_varient_reader(nullptr);
- if (!entry_reader.PopString(&key) ||
- !entry_reader.PopVariant(&value_varient_reader))
- return false;
-
- const uint8_t* bytes = nullptr;
- size_t length = 0;
- if (!value_varient_reader.PopArrayOfBytes(&bytes, &length))
- return false;
-
- value_[key].assign(bytes, bytes + length);
- }
- return true;
-}
-
-template <>
-void Property<std::unordered_map<std::string, std::vector<uint8_t>>>::
- AppendSetValueToWriter(MessageWriter* writer) {
- MessageWriter variant_writer(nullptr);
- MessageWriter dict_writer(nullptr);
-
- writer->OpenVariant("a{sv}", &variant_writer);
- variant_writer.OpenArray("{sv}", &dict_writer);
-
- for (const auto& pair : set_value_) {
- MessageWriter entry_writer(nullptr);
- dict_writer.OpenDictEntry(&entry_writer);
-
- entry_writer.AppendString(pair.first);
-
- MessageWriter value_varient_writer(nullptr);
- entry_writer.OpenVariant("ay", &value_varient_writer);
- value_varient_writer.AppendArrayOfBytes(pair.second.data(),
- pair.second.size());
- entry_writer.CloseContainer(&value_varient_writer);
-
- dict_writer.CloseContainer(&entry_writer);
- }
-
- variant_writer.CloseContainer(&dict_writer);
- writer->CloseContainer(&variant_writer);
-}
-
-//
-// Property<std::unordered_map<uint16_t, std::vector<uint8_t>>>
-// specialization.
-//
-
-template <>
-bool Property<std::unordered_map<uint16_t, std::vector<uint8_t>>>::
- PopValueFromReader(MessageReader* reader) {
- MessageReader variant_reader(nullptr);
- MessageReader dict_reader(nullptr);
- if (!reader->PopVariant(&variant_reader) ||
- !variant_reader.PopArray(&dict_reader))
- return false;
-
- value_.clear();
- while (dict_reader.HasMoreData()) {
- MessageReader entry_reader(nullptr);
- if (!dict_reader.PopDictEntry(&entry_reader))
- return false;
-
- uint16_t key;
- MessageReader value_varient_reader(nullptr);
- if (!entry_reader.PopUint16(&key) ||
- !entry_reader.PopVariant(&value_varient_reader))
- return false;
-
- const uint8_t* bytes = nullptr;
- size_t length = 0;
- if (!value_varient_reader.PopArrayOfBytes(&bytes, &length))
- return false;
-
- value_[key].assign(bytes, bytes + length);
- }
- return true;
-}
-
-template <>
-void Property<std::unordered_map<uint16_t, std::vector<uint8_t>>>::
- AppendSetValueToWriter(MessageWriter* writer) {
- MessageWriter variant_writer(nullptr);
- MessageWriter dict_writer(nullptr);
-
- writer->OpenVariant("a{qv}", &variant_writer);
- variant_writer.OpenArray("{qv}", &dict_writer);
-
- for (const auto& pair : set_value_) {
- MessageWriter entry_writer(nullptr);
- dict_writer.OpenDictEntry(&entry_writer);
-
- entry_writer.AppendUint16(pair.first);
-
- MessageWriter value_varient_writer(nullptr);
- entry_writer.OpenVariant("ay", &value_varient_writer);
- value_varient_writer.AppendArrayOfBytes(pair.second.data(),
- pair.second.size());
- entry_writer.CloseContainer(&value_varient_writer);
-
- dict_writer.CloseContainer(&entry_writer);
- }
-
- variant_writer.CloseContainer(&dict_writer);
- writer->CloseContainer(&variant_writer);
-}
-
template class Property<uint8_t>;
template class Property<bool>;
template class Property<int16_t>;
@@ -805,7 +675,5 @@ template class Property<std::vector<ObjectPath> >;
template class Property<std::vector<uint8_t>>;
template class Property<std::map<std::string, std::string>>;
template class Property<std::vector<std::pair<std::vector<uint8_t>, uint16_t>>>;
-template class Property<std::unordered_map<std::string, std::vector<uint8_t>>>;
-template class Property<std::unordered_map<uint16_t, std::vector<uint8_t>>>;
} // namespace dbus
diff --git a/dbus/property.h b/dbus/property.h
index 0559ea0554..efbad226a6 100644
--- a/dbus/property.h
+++ b/dbus/property.h
@@ -9,7 +9,6 @@
#include <map>
#include <string>
-#include <unordered_map>
#include <utility>
#include <vector>
@@ -611,28 +610,6 @@ Property<std::vector<std::pair<std::vector<uint8_t>, uint16_t>>>::
extern template class CHROME_DBUS_EXPORT
Property<std::vector<std::pair<std::vector<uint8_t>, uint16_t>>>;
-template <>
-CHROME_DBUS_EXPORT bool
-Property<std::unordered_map<std::string, std::vector<uint8_t>>>::
- PopValueFromReader(MessageReader* reader);
-template <>
-CHROME_DBUS_EXPORT void
-Property<std::unordered_map<std::string, std::vector<uint8_t>>>::
- AppendSetValueToWriter(MessageWriter* writer);
-extern template class CHROME_DBUS_EXPORT
- Property<std::unordered_map<std::string, std::vector<uint8_t>>>;
-
-template <>
-CHROME_DBUS_EXPORT bool
-Property<std::unordered_map<uint16_t, std::vector<uint8_t>>>::
- PopValueFromReader(MessageReader* reader);
-template <>
-CHROME_DBUS_EXPORT void
-Property<std::unordered_map<uint16_t, std::vector<uint8_t>>>::
- AppendSetValueToWriter(MessageWriter* writer);
-extern template class CHROME_DBUS_EXPORT
- Property<std::unordered_map<uint16_t, std::vector<uint8_t>>>;
-
#pragma GCC diagnostic pop
} // namespace dbus
diff --git a/dbus/values_util.cc b/dbus/values_util.cc
index 1e035c9cfe..bea7bea746 100644
--- a/dbus/values_util.cc
+++ b/dbus/values_util.cc
@@ -67,19 +67,19 @@ bool PopDictionaryEntries(MessageReader* reader,
// Gets the D-Bus type signature for the value.
std::string GetTypeSignature(const base::Value& value) {
switch (value.GetType()) {
- case base::Value::Type::BOOLEAN:
+ case base::Value::TYPE_BOOLEAN:
return "b";
- case base::Value::Type::INTEGER:
+ case base::Value::TYPE_INTEGER:
return "i";
- case base::Value::Type::DOUBLE:
+ case base::Value::TYPE_DOUBLE:
return "d";
- case base::Value::Type::STRING:
+ case base::Value::TYPE_STRING:
return "s";
- case base::Value::Type::BINARY:
+ case base::Value::TYPE_BINARY:
return "ay";
- case base::Value::Type::DICTIONARY:
+ case base::Value::TYPE_DICTIONARY:
return "a{sv}";
- case base::Value::Type::LIST:
+ case base::Value::TYPE_LIST:
return "av";
default:
DLOG(ERROR) << "Unexpected type " << value.GetType();
@@ -98,37 +98,38 @@ std::unique_ptr<base::Value> PopDataAsValue(MessageReader* reader) {
case Message::BYTE: {
uint8_t value = 0;
if (reader->PopByte(&value))
- result = base::MakeUnique<base::Value>(value);
+ result = base::MakeUnique<base::FundamentalValue>(value);
break;
}
case Message::BOOL: {
bool value = false;
if (reader->PopBool(&value))
- result = base::MakeUnique<base::Value>(value);
+ result = base::MakeUnique<base::FundamentalValue>(value);
break;
}
case Message::INT16: {
int16_t value = 0;
if (reader->PopInt16(&value))
- result = base::MakeUnique<base::Value>(value);
+ result = base::MakeUnique<base::FundamentalValue>(value);
break;
}
case Message::UINT16: {
uint16_t value = 0;
if (reader->PopUint16(&value))
- result = base::MakeUnique<base::Value>(value);
+ result = base::MakeUnique<base::FundamentalValue>(value);
break;
}
case Message::INT32: {
int32_t value = 0;
if (reader->PopInt32(&value))
- result = base::MakeUnique<base::Value>(value);
+ result = base::MakeUnique<base::FundamentalValue>(value);
break;
}
case Message::UINT32: {
uint32_t value = 0;
if (reader->PopUint32(&value)) {
- result = base::MakeUnique<base::Value>(static_cast<double>(value));
+ result = base::MakeUnique<base::FundamentalValue>(
+ static_cast<double>(value));
}
break;
}
@@ -137,7 +138,8 @@ std::unique_ptr<base::Value> PopDataAsValue(MessageReader* reader) {
if (reader->PopInt64(&value)) {
DLOG_IF(WARNING, !IsExactlyRepresentableByDouble(value)) <<
value << " is not exactly representable by double";
- result = base::MakeUnique<base::Value>(static_cast<double>(value));
+ result = base::MakeUnique<base::FundamentalValue>(
+ static_cast<double>(value));
}
break;
}
@@ -146,26 +148,27 @@ std::unique_ptr<base::Value> PopDataAsValue(MessageReader* reader) {
if (reader->PopUint64(&value)) {
DLOG_IF(WARNING, !IsExactlyRepresentableByDouble(value)) <<
value << " is not exactly representable by double";
- result = base::MakeUnique<base::Value>(static_cast<double>(value));
+ result = base::MakeUnique<base::FundamentalValue>(
+ static_cast<double>(value));
}
break;
}
case Message::DOUBLE: {
double value = 0;
if (reader->PopDouble(&value))
- result = base::MakeUnique<base::Value>(value);
+ result = base::MakeUnique<base::FundamentalValue>(value);
break;
}
case Message::STRING: {
std::string value;
if (reader->PopString(&value))
- result = base::MakeUnique<base::Value>(value);
+ result = base::MakeUnique<base::StringValue>(value);
break;
}
case Message::OBJECT_PATH: {
ObjectPath value;
if (reader->PopObjectPath(&value))
- result = base::MakeUnique<base::Value>(value.value());
+ result = base::MakeUnique<base::StringValue>(value.value());
break;
}
case Message::UNIX_FD: {
@@ -216,28 +219,28 @@ std::unique_ptr<base::Value> PopDataAsValue(MessageReader* reader) {
void AppendBasicTypeValueData(MessageWriter* writer, const base::Value& value) {
switch (value.GetType()) {
- case base::Value::Type::BOOLEAN: {
+ case base::Value::TYPE_BOOLEAN: {
bool bool_value = false;
bool success = value.GetAsBoolean(&bool_value);
DCHECK(success);
writer->AppendBool(bool_value);
break;
}
- case base::Value::Type::INTEGER: {
+ case base::Value::TYPE_INTEGER: {
int int_value = 0;
bool success = value.GetAsInteger(&int_value);
DCHECK(success);
writer->AppendInt32(int_value);
break;
}
- case base::Value::Type::DOUBLE: {
+ case base::Value::TYPE_DOUBLE: {
double double_value = 0;
bool success = value.GetAsDouble(&double_value);
DCHECK(success);
writer->AppendDouble(double_value);
break;
}
- case base::Value::Type::STRING: {
+ case base::Value::TYPE_STRING: {
std::string string_value;
bool success = value.GetAsString(&string_value);
DCHECK(success);
@@ -260,7 +263,7 @@ void AppendBasicTypeValueDataAsVariant(MessageWriter* writer,
void AppendValueData(MessageWriter* writer, const base::Value& value) {
switch (value.GetType()) {
- case base::Value::Type::DICTIONARY: {
+ case base::Value::TYPE_DICTIONARY: {
const base::DictionaryValue* dictionary = NULL;
value.GetAsDictionary(&dictionary);
dbus::MessageWriter array_writer(NULL);
@@ -276,7 +279,7 @@ void AppendValueData(MessageWriter* writer, const base::Value& value) {
writer->CloseContainer(&array_writer);
break;
}
- case base::Value::Type::LIST: {
+ case base::Value::TYPE_LIST: {
const base::ListValue* list = NULL;
value.GetAsList(&list);
dbus::MessageWriter array_writer(NULL);
@@ -287,10 +290,10 @@ void AppendValueData(MessageWriter* writer, const base::Value& value) {
writer->CloseContainer(&array_writer);
break;
}
- case base::Value::Type::BOOLEAN:
- case base::Value::Type::INTEGER:
- case base::Value::Type::DOUBLE:
- case base::Value::Type::STRING:
+ case base::Value::TYPE_BOOLEAN:
+ case base::Value::TYPE_INTEGER:
+ case base::Value::TYPE_DOUBLE:
+ case base::Value::TYPE_STRING:
AppendBasicTypeValueData(writer, value);
break;
default:
diff --git a/sandbox/BUILD.gn b/sandbox/BUILD.gn
index 8c0405ed8e..8ca3574e18 100644
--- a/sandbox/BUILD.gn
+++ b/sandbox/BUILD.gn
@@ -2,9 +2,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/buildflag_header.gni")
-import("//sandbox/features.gni")
-
# Meta-target that forwards to the proper platform one.
group("sandbox") {
if (is_win) {
@@ -22,8 +19,3 @@ group("sandbox") {
]
}
}
-
-buildflag_header("sandbox_features") {
- header = "sandbox_features.h"
- flags = [ "USE_SECCOMP_BPF=$use_seccomp_bpf" ]
-}
diff --git a/sandbox/linux/BUILD.gn b/sandbox/linux/BUILD.gn
index 3e98defa5c..a5c041fad0 100644
--- a/sandbox/linux/BUILD.gn
+++ b/sandbox/linux/BUILD.gn
@@ -4,7 +4,6 @@
import("//build/config/features.gni")
import("//build/config/nacl/config.gni")
-import("//sandbox/features.gni")
import("//testing/test.gni")
if (is_android) {
@@ -42,7 +41,10 @@ group("sandbox") {
public_deps += [ ":suid_sandbox_client" ]
}
if (use_seccomp_bpf || is_nacl_nonsfi) {
- public_deps += [ ":seccomp_bpf" ]
+ public_deps += [
+ ":seccomp_bpf",
+ ":seccomp_bpf_helpers",
+ ]
}
}
@@ -190,6 +192,7 @@ action("bpf_dsl_golden") {
rebase_path(outputs, root_build_dir) + rebase_path(inputs, root_build_dir)
}
+
test("sandbox_linux_unittests") {
deps = [
":sandbox_linux_unittests_sources",
@@ -219,14 +222,6 @@ component("seccomp_bpf") {
"bpf_dsl/syscall_set.cc",
"bpf_dsl/syscall_set.h",
"bpf_dsl/trap_registry.h",
- "seccomp-bpf-helpers/baseline_policy.cc",
- "seccomp-bpf-helpers/baseline_policy.h",
- "seccomp-bpf-helpers/sigsys_handlers.cc",
- "seccomp-bpf-helpers/sigsys_handlers.h",
- "seccomp-bpf-helpers/syscall_parameters_restrictions.cc",
- "seccomp-bpf-helpers/syscall_parameters_restrictions.h",
- "seccomp-bpf-helpers/syscall_sets.cc",
- "seccomp-bpf-helpers/syscall_sets.h",
"seccomp-bpf/die.cc",
"seccomp-bpf/die.h",
"seccomp-bpf/sandbox_bpf.cc",
@@ -256,6 +251,31 @@ component("seccomp_bpf") {
"bpf_dsl/linux_syscall_ranges.h",
"bpf_dsl/seccomp_macros.h",
"bpf_dsl/trap_registry.h",
+ ]
+ }
+}
+
+component("seccomp_bpf_helpers") {
+ sources = [
+ "seccomp-bpf-helpers/baseline_policy.cc",
+ "seccomp-bpf-helpers/baseline_policy.h",
+ "seccomp-bpf-helpers/sigsys_handlers.cc",
+ "seccomp-bpf-helpers/sigsys_handlers.h",
+ "seccomp-bpf-helpers/syscall_parameters_restrictions.cc",
+ "seccomp-bpf-helpers/syscall_parameters_restrictions.h",
+ "seccomp-bpf-helpers/syscall_sets.cc",
+ "seccomp-bpf-helpers/syscall_sets.h",
+ ]
+ defines = [ "SANDBOX_IMPLEMENTATION" ]
+
+ deps = [
+ ":sandbox_services",
+ ":seccomp_bpf",
+ "//base",
+ ]
+
+ if (is_nacl_nonsfi) {
+ sources -= [
"seccomp-bpf-helpers/baseline_policy.cc",
"seccomp-bpf-helpers/baseline_policy.h",
"seccomp-bpf-helpers/syscall_sets.cc",
diff --git a/sandbox/linux/bpf_dsl/bpf_dsl.h b/sandbox/linux/bpf_dsl/bpf_dsl.h
index 6f0dd4eb39..7f81344237 100644
--- a/sandbox/linux/bpf_dsl/bpf_dsl.h
+++ b/sandbox/linux/bpf_dsl/bpf_dsl.h
@@ -76,11 +76,6 @@
namespace sandbox {
namespace bpf_dsl {
-template <typename T>
-class Caser;
-
-class Elser;
-
// ResultExpr is an opaque reference to an immutable result expression tree.
using ResultExpr = std::shared_ptr<const internal::ResultExprImpl>;
diff --git a/sandbox/linux/bpf_dsl/bpf_dsl_forward.h b/sandbox/linux/bpf_dsl/bpf_dsl_forward.h
index af1b48b407..10477c9b31 100644
--- a/sandbox/linux/bpf_dsl/bpf_dsl_forward.h
+++ b/sandbox/linux/bpf_dsl/bpf_dsl_forward.h
@@ -24,6 +24,14 @@ class BoolExprImpl;
using ResultExpr = std::shared_ptr<const internal::ResultExprImpl>;
using BoolExpr = std::shared_ptr<const internal::BoolExprImpl>;
+template <typename T>
+class Arg;
+
+class Elser;
+
+template <typename T>
+class Caser;
+
} // namespace bpf_dsl
} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/bpf_dsl_impl.h b/sandbox/linux/bpf_dsl/bpf_dsl_impl.h
index f397321edd..35ff64f498 100644
--- a/sandbox/linux/bpf_dsl/bpf_dsl_impl.h
+++ b/sandbox/linux/bpf_dsl/bpf_dsl_impl.h
@@ -13,6 +13,7 @@
namespace sandbox {
namespace bpf_dsl {
+class ErrorCode;
class PolicyCompiler;
namespace internal {
diff --git a/sandbox/linux/sandbox_linux.gypi b/sandbox/linux/sandbox_linux.gypi
new file mode 100644
index 0000000000..e96ae9eefd
--- /dev/null
+++ b/sandbox/linux/sandbox_linux.gypi
@@ -0,0 +1,434 @@
+# 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.
+
+{
+ 'variables': {
+ 'conditions': [
+ ['OS=="linux"', {
+ 'compile_suid_client': 1,
+ 'compile_credentials': 1,
+ 'use_base_test_suite': 1,
+ }, {
+ 'compile_suid_client': 0,
+ 'compile_credentials': 0,
+ 'use_base_test_suite': 0,
+ }],
+ ['OS=="linux" and (target_arch=="ia32" or target_arch=="x64" or '
+ 'target_arch=="mipsel")', {
+ 'compile_seccomp_bpf_demo': 1,
+ }, {
+ 'compile_seccomp_bpf_demo': 0,
+ }],
+ ],
+ },
+ 'target_defaults': {
+ 'target_conditions': [
+ # All linux/ files will automatically be excluded on Android
+ # so make sure we re-include them explicitly.
+ ['OS == "android"', {
+ 'sources/': [
+ ['include', '^linux/'],
+ ],
+ }],
+ ],
+ },
+ 'targets': [
+ # We have two principal targets: sandbox and sandbox_linux_unittests
+ # All other targets are listed as dependencies.
+ # There is one notable exception: for historical reasons, chrome_sandbox is
+ # the setuid sandbox and is its own target.
+ {
+ 'target_name': 'sandbox',
+ 'type': 'none',
+ 'dependencies': [
+ 'sandbox_services',
+ ],
+ 'conditions': [
+ [ 'compile_suid_client==1', {
+ 'dependencies': [
+ 'suid_sandbox_client',
+ ],
+ }],
+ # Compile seccomp BPF when we support it.
+ [ 'use_seccomp_bpf==1', {
+ 'dependencies': [
+ 'seccomp_bpf',
+ 'seccomp_bpf_helpers',
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'sandbox_linux_test_utils',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ 'sources': [
+ 'tests/sandbox_test_runner.cc',
+ 'tests/sandbox_test_runner.h',
+ 'tests/sandbox_test_runner_function_pointer.cc',
+ 'tests/sandbox_test_runner_function_pointer.h',
+ 'tests/test_utils.cc',
+ 'tests/test_utils.h',
+ 'tests/unit_tests.cc',
+ 'tests/unit_tests.h',
+ ],
+ 'conditions': [
+ [ 'use_seccomp_bpf==1', {
+ 'sources': [
+ 'seccomp-bpf/bpf_tester_compatibility_delegate.h',
+ 'seccomp-bpf/bpf_tests.h',
+ 'seccomp-bpf/sandbox_bpf_test_runner.cc',
+ 'seccomp-bpf/sandbox_bpf_test_runner.h',
+ ],
+ 'dependencies': [
+ 'seccomp_bpf',
+ ]
+ }],
+ [ 'use_base_test_suite==1', {
+ 'dependencies': [
+ '../base/base.gyp:test_support_base',
+ ],
+ 'defines': [
+ 'SANDBOX_USES_BASE_TEST_SUITE',
+ ],
+ }],
+ ],
+ },
+ {
+ # The main sandboxing test target.
+ 'target_name': 'sandbox_linux_unittests',
+ 'includes': [
+ 'sandbox_linux_test_sources.gypi',
+ ],
+ 'type': 'executable',
+ 'conditions': [
+ [ 'OS == "android"', {
+ 'variables': {
+ 'test_type': 'gtest',
+ 'test_suite_name': '<(_target_name)',
+ },
+ 'includes': [
+ '../../build/android/test_runner.gypi',
+ ],
+ }]
+ ]
+ },
+ {
+ 'target_name': 'seccomp_bpf',
+ 'type': '<(component)',
+ 'sources': [
+ 'bpf_dsl/bpf_dsl.cc',
+ 'bpf_dsl/bpf_dsl.h',
+ 'bpf_dsl/bpf_dsl_forward.h',
+ 'bpf_dsl/bpf_dsl_impl.h',
+ 'bpf_dsl/codegen.cc',
+ 'bpf_dsl/codegen.h',
+ 'bpf_dsl/cons.h',
+ 'bpf_dsl/errorcode.h',
+ 'bpf_dsl/linux_syscall_ranges.h',
+ 'bpf_dsl/policy.cc',
+ 'bpf_dsl/policy.h',
+ 'bpf_dsl/policy_compiler.cc',
+ 'bpf_dsl/policy_compiler.h',
+ 'bpf_dsl/seccomp_macros.h',
+ 'bpf_dsl/seccomp_macros.h',
+ 'bpf_dsl/syscall_set.cc',
+ 'bpf_dsl/syscall_set.h',
+ 'bpf_dsl/trap_registry.h',
+ 'seccomp-bpf/die.cc',
+ 'seccomp-bpf/die.h',
+ 'seccomp-bpf/sandbox_bpf.cc',
+ 'seccomp-bpf/sandbox_bpf.h',
+ 'seccomp-bpf/syscall.cc',
+ 'seccomp-bpf/syscall.h',
+ 'seccomp-bpf/trap.cc',
+ 'seccomp-bpf/trap.h',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ 'sandbox_services',
+ 'sandbox_services_headers',
+ ],
+ 'defines': [
+ 'SANDBOX_IMPLEMENTATION',
+ ],
+ 'includes': [
+ # Disable LTO due to compiler bug
+ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57703
+ '../../build/android/disable_gcc_lto.gypi',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ },
+ {
+ 'target_name': 'seccomp_bpf_helpers',
+ 'type': '<(component)',
+ 'sources': [
+ 'seccomp-bpf-helpers/baseline_policy.cc',
+ 'seccomp-bpf-helpers/baseline_policy.h',
+ 'seccomp-bpf-helpers/sigsys_handlers.cc',
+ 'seccomp-bpf-helpers/sigsys_handlers.h',
+ 'seccomp-bpf-helpers/syscall_parameters_restrictions.cc',
+ 'seccomp-bpf-helpers/syscall_parameters_restrictions.h',
+ 'seccomp-bpf-helpers/syscall_sets.cc',
+ 'seccomp-bpf-helpers/syscall_sets.h',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ 'sandbox_services',
+ 'seccomp_bpf',
+ ],
+ 'defines': [
+ 'SANDBOX_IMPLEMENTATION',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ },
+ {
+ # The setuid sandbox, for Linux
+ 'target_name': 'chrome_sandbox',
+ 'type': 'executable',
+ 'sources': [
+ 'suid/common/sandbox.h',
+ 'suid/common/suid_unsafe_environment_variables.h',
+ 'suid/process_util.h',
+ 'suid/process_util_linux.c',
+ 'suid/sandbox.c',
+ ],
+ 'cflags': [
+ # For ULLONG_MAX
+ '-std=gnu99',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ # Do not use any sanitizer tools with this binary. http://crbug.com/382766
+ 'cflags/': [
+ ['exclude', '-fsanitize'],
+ ],
+ 'ldflags/': [
+ ['exclude', '-fsanitize'],
+ ],
+ },
+ { 'target_name': 'sandbox_services',
+ 'type': '<(component)',
+ 'sources': [
+ 'services/init_process_reaper.cc',
+ 'services/init_process_reaper.h',
+ 'services/proc_util.cc',
+ 'services/proc_util.h',
+ 'services/resource_limits.cc',
+ 'services/resource_limits.h',
+ 'services/scoped_process.cc',
+ 'services/scoped_process.h',
+ 'services/syscall_wrappers.cc',
+ 'services/syscall_wrappers.h',
+ 'services/thread_helpers.cc',
+ 'services/thread_helpers.h',
+ 'services/yama.cc',
+ 'services/yama.h',
+ 'syscall_broker/broker_channel.cc',
+ 'syscall_broker/broker_channel.h',
+ 'syscall_broker/broker_client.cc',
+ 'syscall_broker/broker_client.h',
+ 'syscall_broker/broker_common.h',
+ 'syscall_broker/broker_file_permission.cc',
+ 'syscall_broker/broker_file_permission.h',
+ 'syscall_broker/broker_host.cc',
+ 'syscall_broker/broker_host.h',
+ 'syscall_broker/broker_policy.cc',
+ 'syscall_broker/broker_policy.h',
+ 'syscall_broker/broker_process.cc',
+ 'syscall_broker/broker_process.h',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ ],
+ 'defines': [
+ 'SANDBOX_IMPLEMENTATION',
+ ],
+ 'conditions': [
+ ['compile_credentials==1', {
+ 'sources': [
+ 'services/credentials.cc',
+ 'services/credentials.h',
+ 'services/namespace_sandbox.cc',
+ 'services/namespace_sandbox.h',
+ 'services/namespace_utils.cc',
+ 'services/namespace_utils.h',
+ ],
+ 'dependencies': [
+ # for capability.h.
+ 'sandbox_services_headers',
+ ],
+ }],
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ { 'target_name': 'sandbox_services_headers',
+ 'type': 'none',
+ 'sources': [
+ 'system_headers/arm64_linux_syscalls.h',
+ 'system_headers/arm64_linux_ucontext.h',
+ 'system_headers/arm_linux_syscalls.h',
+ 'system_headers/arm_linux_ucontext.h',
+ 'system_headers/capability.h',
+ 'system_headers/i386_linux_ucontext.h',
+ 'system_headers/linux_futex.h',
+ 'system_headers/linux_seccomp.h',
+ 'system_headers/linux_syscalls.h',
+ 'system_headers/linux_time.h',
+ 'system_headers/linux_ucontext.h',
+ 'system_headers/mips_linux_syscalls.h',
+ 'system_headers/mips_linux_ucontext.h',
+ 'system_headers/x86_32_linux_syscalls.h',
+ 'system_headers/x86_64_linux_syscalls.h',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ {
+ 'target_name': 'suid_sandbox_client',
+ 'type': '<(component)',
+ 'sources': [
+ 'suid/common/sandbox.h',
+ 'suid/common/suid_unsafe_environment_variables.h',
+ 'suid/client/setuid_sandbox_client.cc',
+ 'suid/client/setuid_sandbox_client.h',
+ 'suid/client/setuid_sandbox_host.cc',
+ 'suid/client/setuid_sandbox_host.h',
+ ],
+ 'defines': [
+ 'SANDBOX_IMPLEMENTATION',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ 'sandbox_services',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ {
+ 'target_name': 'bpf_dsl_golden',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'action_name': 'generate',
+ 'inputs': [
+ 'bpf_dsl/golden/generate.py',
+ 'bpf_dsl/golden/i386/ArgSizePolicy.txt',
+ 'bpf_dsl/golden/i386/BasicPolicy.txt',
+ 'bpf_dsl/golden/i386/ElseIfPolicy.txt',
+ 'bpf_dsl/golden/i386/MaskingPolicy.txt',
+ 'bpf_dsl/golden/i386/MoreBooleanLogicPolicy.txt',
+ 'bpf_dsl/golden/i386/NegativeConstantsPolicy.txt',
+ 'bpf_dsl/golden/i386/SwitchPolicy.txt',
+ 'bpf_dsl/golden/x86-64/ArgSizePolicy.txt',
+ 'bpf_dsl/golden/x86-64/BasicPolicy.txt',
+ 'bpf_dsl/golden/x86-64/BooleanLogicPolicy.txt',
+ 'bpf_dsl/golden/x86-64/ElseIfPolicy.txt',
+ 'bpf_dsl/golden/x86-64/MaskingPolicy.txt',
+ 'bpf_dsl/golden/x86-64/MoreBooleanLogicPolicy.txt',
+ 'bpf_dsl/golden/x86-64/NegativeConstantsPolicy.txt',
+ 'bpf_dsl/golden/x86-64/SwitchPolicy.txt',
+ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/sandbox/linux/bpf_dsl/golden/golden_files.h',
+ ],
+ 'action': [
+ 'python',
+ 'linux/bpf_dsl/golden/generate.py',
+ '<(SHARED_INTERMEDIATE_DIR)/sandbox/linux/bpf_dsl/golden/golden_files.h',
+ 'linux/bpf_dsl/golden/i386/ArgSizePolicy.txt',
+ 'linux/bpf_dsl/golden/i386/BasicPolicy.txt',
+ 'linux/bpf_dsl/golden/i386/ElseIfPolicy.txt',
+ 'linux/bpf_dsl/golden/i386/MaskingPolicy.txt',
+ 'linux/bpf_dsl/golden/i386/MoreBooleanLogicPolicy.txt',
+ 'linux/bpf_dsl/golden/i386/NegativeConstantsPolicy.txt',
+ 'linux/bpf_dsl/golden/i386/SwitchPolicy.txt',
+ 'linux/bpf_dsl/golden/x86-64/ArgSizePolicy.txt',
+ 'linux/bpf_dsl/golden/x86-64/BasicPolicy.txt',
+ 'linux/bpf_dsl/golden/x86-64/BooleanLogicPolicy.txt',
+ 'linux/bpf_dsl/golden/x86-64/ElseIfPolicy.txt',
+ 'linux/bpf_dsl/golden/x86-64/MaskingPolicy.txt',
+ 'linux/bpf_dsl/golden/x86-64/MoreBooleanLogicPolicy.txt',
+ 'linux/bpf_dsl/golden/x86-64/NegativeConstantsPolicy.txt',
+ 'linux/bpf_dsl/golden/x86-64/SwitchPolicy.txt',
+ ],
+ 'message': 'Generating header from golden files ...',
+ },
+ ],
+ },
+ ],
+ 'conditions': [
+ [ 'OS=="android"', {
+ 'targets': [
+ {
+ 'target_name': 'sandbox_linux_unittests_deps',
+ 'type': 'none',
+ 'dependencies': [
+ 'sandbox_linux_unittests',
+ ],
+ 'variables': {
+ 'output_dir': '<(PRODUCT_DIR)/sandbox_linux_unittests__dist/',
+ 'native_binary': '<(PRODUCT_DIR)/sandbox_linux_unittests',
+ 'include_main_binary': 1,
+ },
+ 'includes': [
+ '../../build/android/native_app_dependencies.gypi'
+ ],
+ }],
+ }],
+ [ 'OS=="android"', {
+ 'conditions': [
+ ['test_isolation_mode != "noop"', {
+ 'targets': [
+ {
+ 'target_name': 'sandbox_linux_unittests_android_run',
+ 'type': 'none',
+ 'dependencies': [
+ 'sandbox_linux_unittests',
+ ],
+ 'includes': [
+ '../../build/isolate.gypi',
+ ],
+ 'sources': [
+ '../sandbox_linux_unittests_android.isolate',
+ ],
+ },
+ ],
+ },
+ ],
+ ],
+ }],
+ ['test_isolation_mode != "noop"', {
+ 'targets': [
+ {
+ 'target_name': 'sandbox_linux_unittests_run',
+ 'type': 'none',
+ 'dependencies': [
+ 'sandbox_linux_unittests',
+ ],
+ 'includes': [
+ '../../build/isolate.gypi',
+ ],
+ 'sources': [
+ '../sandbox_linux_unittests.isolate',
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/sandbox/linux/sandbox_linux_nacl_nonsfi.gyp b/sandbox/linux/sandbox_linux_nacl_nonsfi.gyp
new file mode 100644
index 0000000000..50e637c360
--- /dev/null
+++ b/sandbox/linux/sandbox_linux_nacl_nonsfi.gyp
@@ -0,0 +1,87 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'includes': [
+ '../../build/common_untrusted.gypi',
+ ],
+ 'conditions': [
+ ['disable_nacl==0 and disable_nacl_untrusted==0', {
+ 'targets': [
+ {
+ 'target_name': 'sandbox_linux_nacl_nonsfi',
+ 'type': 'none',
+ 'variables': {
+ 'nacl_untrusted_build': 1,
+ 'nlib_target': 'libsandbox_linux_nacl_nonsfi.a',
+ 'build_glibc': 0,
+ 'build_newlib': 0,
+ 'build_irt': 0,
+ 'build_pnacl_newlib': 0,
+ 'build_nonsfi_helper': 1,
+ 'compile_flags': [
+ '-fgnu-inline-asm',
+ ],
+ 'sources': [
+ # This is the subset of linux build target, needed for
+ # nacl_helper_nonsfi's sandbox implementation.
+ 'bpf_dsl/bpf_dsl.cc',
+ 'bpf_dsl/codegen.cc',
+ 'bpf_dsl/policy.cc',
+ 'bpf_dsl/policy_compiler.cc',
+ 'bpf_dsl/syscall_set.cc',
+ 'seccomp-bpf-helpers/sigsys_handlers.cc',
+ 'seccomp-bpf-helpers/syscall_parameters_restrictions.cc',
+ 'seccomp-bpf/die.cc',
+ 'seccomp-bpf/sandbox_bpf.cc',
+ 'seccomp-bpf/syscall.cc',
+ 'seccomp-bpf/trap.cc',
+ 'services/credentials.cc',
+ 'services/namespace_sandbox.cc',
+ 'services/namespace_utils.cc',
+ 'services/proc_util.cc',
+ 'services/resource_limits.cc',
+ 'services/syscall_wrappers.cc',
+ 'services/thread_helpers.cc',
+ 'suid/client/setuid_sandbox_client.cc',
+ ],
+ },
+ 'dependencies': [
+ '../../base/base_nacl.gyp:base_nacl_nonsfi',
+ ],
+ },
+ ],
+ }],
+
+ ['disable_nacl==0 and disable_nacl_untrusted==0 and enable_nacl_nonsfi_test==1', {
+ 'targets': [
+ {
+ 'target_name': 'sandbox_linux_test_utils_nacl_nonsfi',
+ 'type': 'none',
+ 'variables': {
+ 'nacl_untrusted_build': 1,
+ 'nlib_target': 'libsandbox_linux_test_utils_nacl_nonsfi.a',
+ 'build_glibc': 0,
+ 'build_newlib': 0,
+ 'build_irt': 0,
+ 'build_pnacl_newlib': 0,
+ 'build_nonsfi_helper': 1,
+
+ 'sources': [
+ 'seccomp-bpf/sandbox_bpf_test_runner.cc',
+ 'tests/sandbox_test_runner.cc',
+ 'tests/unit_tests.cc',
+ ],
+ },
+ 'dependencies': [
+ '../../testing/gtest_nacl.gyp:gtest_nacl',
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/sandbox/linux/sandbox_linux_test_sources.gypi b/sandbox/linux/sandbox_linux_test_sources.gypi
new file mode 100644
index 0000000000..612814e1d4
--- /dev/null
+++ b/sandbox/linux/sandbox_linux_test_sources.gypi
@@ -0,0 +1,93 @@
+# 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.
+
+# Tests need to be compiled in the same link unit, so we have to list them
+# in a separate .gypi file.
+{
+ 'dependencies': [
+ 'sandbox',
+ 'sandbox_linux_test_utils',
+ 'sandbox_services',
+ '../base/base.gyp:base',
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ 'sources': [
+ 'services/proc_util_unittest.cc',
+ 'services/scoped_process_unittest.cc',
+ 'services/resource_limits_unittests.cc',
+ 'services/syscall_wrappers_unittest.cc',
+ 'services/thread_helpers_unittests.cc',
+ 'services/yama_unittests.cc',
+ 'syscall_broker/broker_file_permission_unittest.cc',
+ 'syscall_broker/broker_process_unittest.cc',
+ 'tests/main.cc',
+ 'tests/scoped_temporary_file.cc',
+ 'tests/scoped_temporary_file.h',
+ 'tests/scoped_temporary_file_unittest.cc',
+ 'tests/test_utils_unittest.cc',
+ 'tests/unit_tests_unittest.cc',
+ ],
+ 'conditions': [
+ [ 'compile_suid_client==1', {
+ 'sources': [
+ 'suid/client/setuid_sandbox_client_unittest.cc',
+ 'suid/client/setuid_sandbox_host_unittest.cc',
+ ],
+ }],
+ [ 'use_seccomp_bpf==1', {
+ 'sources': [
+ 'bpf_dsl/bpf_dsl_unittest.cc',
+ 'bpf_dsl/codegen_unittest.cc',
+ 'bpf_dsl/cons_unittest.cc',
+ 'bpf_dsl/dump_bpf.cc',
+ 'bpf_dsl/dump_bpf.h',
+ 'bpf_dsl/syscall_set_unittest.cc',
+ 'bpf_dsl/test_trap_registry.cc',
+ 'bpf_dsl/test_trap_registry.h',
+ 'bpf_dsl/test_trap_registry_unittest.cc',
+ 'bpf_dsl/verifier.cc',
+ 'bpf_dsl/verifier.h',
+ 'integration_tests/bpf_dsl_seccomp_unittest.cc',
+ 'integration_tests/seccomp_broker_process_unittest.cc',
+ 'seccomp-bpf-helpers/baseline_policy_unittest.cc',
+ 'seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc',
+ 'seccomp-bpf/bpf_tests_unittest.cc',
+ 'seccomp-bpf/sandbox_bpf_unittest.cc',
+ 'seccomp-bpf/syscall_unittest.cc',
+ 'seccomp-bpf/trap_unittest.cc',
+ ],
+ 'dependencies': [
+ 'bpf_dsl_golden',
+ ],
+ }],
+ [ 'compile_credentials==1', {
+ 'sources': [
+ 'integration_tests/namespace_unix_domain_socket_unittest.cc',
+ 'services/credentials_unittest.cc',
+ 'services/namespace_utils_unittest.cc',
+ ],
+ 'dependencies': [
+ '../build/linux/system.gyp:libcap'
+ ],
+ 'conditions': [
+ [ 'use_base_test_suite==1', {
+ 'sources': [
+ 'services/namespace_sandbox_unittest.cc',
+ ]
+ }]
+ ],
+ }],
+ [ 'use_base_test_suite==1', {
+ 'dependencies': [
+ '../base/base.gyp:test_support_base',
+ ],
+ 'defines': [
+ 'SANDBOX_USES_BASE_TEST_SUITE',
+ ],
+ }],
+ ],
+}
diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
index 88a932607c..2bf572c0b3 100644
--- a/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
@@ -169,20 +169,10 @@ ResultExpr EvaluateSyscallImpl(int fs_denied_errno,
if (sysno == __NR_getpriority || sysno ==__NR_setpriority)
return RestrictGetSetpriority(current_pid);
- if (sysno == __NR_getrandom) {
- return RestrictGetRandom();
- }
-
if (sysno == __NR_madvise) {
- // Only allow MADV_DONTNEED and MADV_FREE.
+ // Only allow MADV_DONTNEED (aka MADV_FREE).
const Arg<int> advice(2);
- return If(AnyOf(advice == MADV_DONTNEED
-#if defined(MADV_FREE)
- // MADV_FREE was introduced in Linux 4.5 and started being
- // defined in glibc 2.24.
- , advice == MADV_FREE
-#endif
- ), Allow()).Else(Error(EPERM));
+ return If(advice == MADV_DONTNEED, Allow()).Else(Error(EPERM));
}
#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \
diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
index ca812d8a1e..f0392b1a00 100644
--- a/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
@@ -168,21 +168,6 @@ BPF_TEST_C(BaselinePolicy, Socketpair, BaselinePolicy) {
TestPipeOrSocketPair(base::ScopedFD(sv[0]), base::ScopedFD(sv[1]));
}
-#if !defined(GRND_NONBLOCK)
-#define GRND_NONBLOCK 1
-#endif
-
-BPF_TEST_C(BaselinePolicy, GetRandom, BaselinePolicy) {
- char buf[1];
-
- // Many systems do not yet support getrandom(2) so ENOSYS is a valid result
- // here.
- int ret = HANDLE_EINTR(syscall(__NR_getrandom, buf, sizeof(buf), 0));
- BPF_ASSERT((ret == -1 && errno == ENOSYS) || ret == 1);
- ret = HANDLE_EINTR(syscall(__NR_getrandom, buf, sizeof(buf), GRND_NONBLOCK));
- BPF_ASSERT((ret == -1 && (errno == ENOSYS || errno == EAGAIN)) || ret == 1);
-}
-
// Not all architectures can restrict the domain for socketpair().
#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
BPF_DEATH_TEST_C(BaselinePolicy,
@@ -364,17 +349,6 @@ BPF_DEATH_TEST_C(BaselinePolicy,
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
}
-#if !defined(GRND_RANDOM)
-#define GRND_RANDOM 2
-#endif
-
-BPF_DEATH_TEST_C(BaselinePolicy,
- GetRandomOfDevRandomCrashes,
- DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
- BaselinePolicy) {
- syscall(__NR_getrandom, NULL, 0, GRND_RANDOM);
-}
-
#if !defined(__i386__)
BPF_DEATH_TEST_C(BaselinePolicy,
GetSockOptWrongLevelSigsys,
diff --git a/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc b/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc
index e6c64defad..077bc61f38 100644
--- a/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc
@@ -11,7 +11,6 @@
#include <sys/syscall.h>
#include <unistd.h>
-#include "base/debug/crash_logging.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "build/build_config.h"
@@ -50,8 +49,7 @@ void WriteToStdErr(const char* error_message, size_t size) {
while (size > 0) {
// TODO(jln): query the current policy to check if send() is available and
// use it to perform a non-blocking write.
- const int ret = HANDLE_EINTR(
- sandbox::sys_write(STDERR_FILENO, error_message, size));
+ const int ret = HANDLE_EINTR(write(STDERR_FILENO, error_message, size));
// We can't handle any type of error here.
if (ret <= 0 || static_cast<size_t>(ret) > size) break;
size -= ret;
@@ -94,7 +92,6 @@ void PrintSyscallError(uint32_t sysno) {
rem /= 10;
sysno_base10[i] = '0' + mod;
}
-
#if defined(__mips__) && (_MIPS_SIM == _MIPS_SIM_ABI32)
static const char kSeccompErrorPrefix[] = __FILE__
":**CRASHING**:" SECCOMP_MESSAGE_COMMON_CONTENT " in syscall 4000 + ";
@@ -108,88 +105,7 @@ void PrintSyscallError(uint32_t sysno) {
WriteToStdErr(kSeccompErrorPostfix, sizeof(kSeccompErrorPostfix) - 1);
}
-// Helper to convert a number of type T to a hexadecimal string using
-// stack-allocated storage.
-template <typename T>
-class NumberToHex {
- public:
- explicit NumberToHex(T value) {
- static const char kHexChars[] = "0123456789abcdef";
-
- memset(str_, '0', sizeof(str_));
- str_[1] = 'x';
- str_[sizeof(str_) - 1] = '\0';
-
- T rem = value;
- T mod = 0;
- for (size_t i = sizeof(str_) - 2; i >= 2; --i) {
- mod = rem % 16;
- rem /= 16;
- str_[i] = kHexChars[mod];
- }
- }
-
- const char* str() const { return str_; }
-
- static size_t length() { return sizeof(str_) - 1; }
-
- private:
- // HEX uses two characters per byte, with a leading '0x', and a trailing NUL.
- char str_[sizeof(T) * 2 + 3];
-};
-
-// Records the syscall number and first four arguments in a crash key, to help
-// debug the failure.
-void SetSeccompCrashKey(const struct sandbox::arch_seccomp_data& args) {
-#if !defined(OS_NACL_NONSFI)
- NumberToHex<int> nr(args.nr);
- NumberToHex<uint64_t> arg1(args.args[0]);
- NumberToHex<uint64_t> arg2(args.args[1]);
- NumberToHex<uint64_t> arg3(args.args[2]);
- NumberToHex<uint64_t> arg4(args.args[3]);
-
- // In order to avoid calling into libc sprintf functions from an unsafe signal
- // context, manually construct the crash key string.
- const char* const prefixes[] = {
- "nr=",
- " arg1=",
- " arg2=",
- " arg3=",
- " arg4=",
- };
- const char* const values[] = {
- nr.str(),
- arg1.str(),
- arg2.str(),
- arg3.str(),
- arg4.str(),
- };
-
- size_t crash_key_length = nr.length() + arg1.length() + arg2.length() +
- arg3.length() + arg4.length();
- for (auto* prefix : prefixes) {
- crash_key_length += strlen(prefix);
- }
- ++crash_key_length; // For the trailing NUL byte.
-
- char crash_key[crash_key_length];
- memset(crash_key, '\0', crash_key_length);
-
- size_t offset = 0;
- for (size_t i = 0; i < arraysize(values); ++i) {
- const char* strings[2] = { prefixes[i], values[i] };
- for (auto* string : strings) {
- size_t string_len = strlen(string);
- memmove(&crash_key[offset], string, string_len);
- offset += string_len;
- }
- }
-
- base::debug::SetCrashKeyValue("seccomp-sigsys", crash_key);
-#endif
-}
-
-} // namespace
+} // namespace.
namespace sandbox {
@@ -197,7 +113,6 @@ intptr_t CrashSIGSYS_Handler(const struct arch_seccomp_data& args, void* aux) {
uint32_t syscall = SyscallNumberToOffsetFromBase(args.nr);
PrintSyscallError(syscall);
- SetSeccompCrashKey(args);
// Encode 8-bits of the 1st two arguments too, so we can discern which socket
// type, which fcntl, ... etc., without being likely to hit a mapped
@@ -225,7 +140,6 @@ intptr_t SIGSYSCloneFailure(const struct arch_seccomp_data& args, void* aux) {
static const char kSeccompCloneError[] =
__FILE__":**CRASHING**:" SECCOMP_MESSAGE_CLONE_CONTENT "\n";
WriteToStdErr(kSeccompCloneError, sizeof(kSeccompCloneError) - 1);
- SetSeccompCrashKey(args);
// "flags" is the first argument in the kernel's clone().
// Mark as volatile to be able to find the value on the stack in a minidump.
volatile uint64_t clone_flags = args.args[0];
@@ -246,7 +160,6 @@ intptr_t SIGSYSPrctlFailure(const struct arch_seccomp_data& args,
static const char kSeccompPrctlError[] =
__FILE__":**CRASHING**:" SECCOMP_MESSAGE_PRCTL_CONTENT "\n";
WriteToStdErr(kSeccompPrctlError, sizeof(kSeccompPrctlError) - 1);
- SetSeccompCrashKey(args);
// Mark as volatile to be able to find the value on the stack in a minidump.
volatile uint64_t option = args.args[0];
volatile char* addr =
@@ -261,7 +174,6 @@ intptr_t SIGSYSIoctlFailure(const struct arch_seccomp_data& args,
static const char kSeccompIoctlError[] =
__FILE__":**CRASHING**:" SECCOMP_MESSAGE_IOCTL_CONTENT "\n";
WriteToStdErr(kSeccompIoctlError, sizeof(kSeccompIoctlError) - 1);
- SetSeccompCrashKey(args);
// Make "request" volatile so that we can see it on the stack in a minidump.
volatile uint64_t request = args.args[1];
volatile char* addr = reinterpret_cast<volatile char*>(request & 0xFFFF);
@@ -278,7 +190,6 @@ intptr_t SIGSYSKillFailure(const struct arch_seccomp_data& args,
static const char kSeccompKillError[] =
__FILE__":**CRASHING**:" SECCOMP_MESSAGE_KILL_CONTENT "\n";
WriteToStdErr(kSeccompKillError, sizeof(kSeccompKillError) - 1);
- SetSeccompCrashKey(args);
// Make "pid" volatile so that we can see it on the stack in a minidump.
volatile uint64_t my_pid = sys_getpid();
volatile char* addr = reinterpret_cast<volatile char*>(my_pid & 0xFFF);
@@ -292,7 +203,6 @@ intptr_t SIGSYSFutexFailure(const struct arch_seccomp_data& args,
static const char kSeccompFutexError[] =
__FILE__ ":**CRASHING**:" SECCOMP_MESSAGE_FUTEX_CONTENT "\n";
WriteToStdErr(kSeccompFutexError, sizeof(kSeccompFutexError) - 1);
- SetSeccompCrashKey(args);
volatile int futex_op = args.args[1];
volatile char* addr = reinterpret_cast<volatile char*>(futex_op & 0xFFF);
*addr = '\0';
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
index 061bfb4803..56c4cb387d 100644
--- a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
@@ -351,22 +351,4 @@ ResultExpr RestrictClockID() {
.Default(CrashSIGSYS());
}
-#if !defined(GRND_NONBLOCK)
-#define GRND_NONBLOCK 1
-#endif
-
-ResultExpr RestrictGetRandom() {
- const Arg<unsigned int> flags(2);
- const unsigned int kGoodFlags = GRND_NONBLOCK;
- return If((flags & ~kGoodFlags) == 0, Allow()).Else(CrashSIGSYS());
-}
-
-ResultExpr RestrictPrlimitToGetrlimit(pid_t target_pid) {
- const Arg<pid_t> pid(0);
- const Arg<uintptr_t> new_limit(2);
- // Only allow 'get' operations, and only for the current process.
- return If(AllOf(new_limit == 0, AnyOf(pid == 0, pid == target_pid)), Allow())
- .Else(Error(EPERM));
-}
-
} // namespace sandbox.
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h
index c4577dc97d..b96fe20e35 100644
--- a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h
@@ -94,15 +94,6 @@ SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictGetrusage();
// about the state of the host OS.
SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictClockID();
-// Restrict the flags argument to getrandom() to allow only no flags, or
-// GRND_NONBLOCK.
-SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictGetRandom();
-
-// Restrict |new_limit| to NULL, and |pid| to the calling process (or 0) for
-// prlimit64(). This allows only getting rlimits on the current process.
-// Otherwise, fail gracefully; see crbug.com/160157.
-SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictPrlimitToGetrlimit(pid_t target_pid);
-
} // namespace sandbox.
#endif // SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_PARAMETERS_RESTRICTIONS_H_
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc
index c068cd2d04..804a8fea1e 100644
--- a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc
@@ -13,7 +13,6 @@
#include <unistd.h>
#include "base/bind.h"
-#include "base/single_thread_task_runner.h"
#include "base/synchronization/waitable_event.h"
#include "base/sys_info.h"
#include "base/threading/thread.h"
@@ -165,7 +164,7 @@ BPF_TEST_C(ParameterRestrictions,
// different.
base::Thread getparam_thread("sched_getparam_thread");
BPF_ASSERT(getparam_thread.Start());
- getparam_thread.task_runner()->PostTask(
+ getparam_thread.message_loop()->PostTask(
FROM_HERE, base::Bind(&SchedGetParamThread, &thread_run));
BPF_ASSERT(thread_run.TimedWait(base::TimeDelta::FromMilliseconds(5000)));
getparam_thread.Stop();
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc
index 1d9f95cd64..c217d47e2d 100644
--- a/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc
@@ -120,7 +120,9 @@ bool SyscallSets::IsFileSystem(int sysno) {
#if defined(__i386__) || defined(__arm__) || defined(__mips__)
case __NR_lstat64:
#endif
+#if defined(__i386__) || defined(__arm__) || defined(__x86_64__)
case __NR_memfd_create:
+#endif
case __NR_mkdirat:
case __NR_mknodat:
#if defined(__i386__)
@@ -412,7 +414,6 @@ bool SyscallSets::IsAllowedEpoll(int sysno) {
case __NR_epoll_create:
case __NR_epoll_wait:
#endif
- case __NR_epoll_pwait:
case __NR_epoll_create1:
case __NR_epoll_ctl:
return true;
@@ -420,6 +421,7 @@ bool SyscallSets::IsAllowedEpoll(int sysno) {
#if defined(__x86_64__)
case __NR_epoll_ctl_old:
#endif
+ case __NR_epoll_pwait:
#if defined(__x86_64__)
case __NR_epoll_wait_old:
#endif
@@ -548,7 +550,7 @@ bool SyscallSets::IsAllowedGeneralIo(int sysno) {
#if defined(__i386__) || defined(__arm__) || defined(__mips__)
case __NR__newselect:
#endif
-#if defined(__arm__) || defined(__mips__)
+#if defined(__arm__)
case __NR_send:
#endif
#if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \
diff --git a/sandbox/linux/services/credentials.cc b/sandbox/linux/services/credentials.cc
index 50a109e2f4..0c617d4b2f 100644
--- a/sandbox/linux/services/credentials.cc
+++ b/sandbox/linux/services/credentials.cc
@@ -150,18 +150,6 @@ int CapabilityToKernelValue(Credentials::Capability cap) {
return 0;
}
-void SetGidAndUidMaps(gid_t gid, uid_t uid) {
- if (NamespaceUtils::KernelSupportsDenySetgroups()) {
- PCHECK(NamespaceUtils::DenySetgroups());
- }
- DCHECK(GetRESIds(NULL, NULL));
- const char kGidMapFile[] = "/proc/self/gid_map";
- const char kUidMapFile[] = "/proc/self/uid_map";
- PCHECK(NamespaceUtils::WriteToIdMapFile(kGidMapFile, gid));
- PCHECK(NamespaceUtils::WriteToIdMapFile(kUidMapFile, uid));
- DCHECK(GetRESIds(NULL, NULL));
-}
-
} // namespace.
// static
@@ -265,14 +253,8 @@ bool Credentials::CanCreateProcessInNewUserNS() {
return false;
#endif
- uid_t uid;
- gid_t gid;
- if (!GetRESIds(&uid, &gid)) {
- return false;
- }
-
- const pid_t pid =
- base::ForkWithFlags(CLONE_NEWUSER | SIGCHLD, nullptr, nullptr);
+ // This is roughly a fork().
+ const pid_t pid = sys_clone(CLONE_NEWUSER | SIGCHLD, 0, 0, 0, 0);
if (pid == -1) {
CheckCloneNewUserErrno(errno);
@@ -280,28 +262,20 @@ bool Credentials::CanCreateProcessInNewUserNS() {
}
// The parent process could have had threads. In the child, these threads
- // have disappeared.
+ // have disappeared. Make sure to not do anything in the child, as this is a
+ // fragile execution environment.
if (pid == 0) {
- // unshare() requires the effective uid and gid to have a mapping in the
- // parent namespace.
- SetGidAndUidMaps(gid, uid);
-
- // Make sure we drop CAP_SYS_ADMIN.
- CHECK(sandbox::Credentials::DropAllCapabilities());
-
- // Ensure we have unprivileged use of CLONE_NEWUSER. Debian
- // Jessie explicitly forbids this case. See:
- // add-sysctl-to-disallow-unprivileged-CLONE_NEWUSER-by-default.patch
- _exit(!!sys_unshare(CLONE_NEWUSER));
+ _exit(kExitSuccess);
}
// Always reap the child.
int status = -1;
PCHECK(HANDLE_EINTR(waitpid(pid, &status, 0)) == pid);
+ CHECK(WIFEXITED(status));
+ CHECK_EQ(kExitSuccess, WEXITSTATUS(status));
- // clone(2) succeeded. Now return true only if the system grants
- // unprivileged use of CLONE_NEWUSER as well.
- return WIFEXITED(status) && WEXITSTATUS(status) == kExitSuccess;
+ // clone(2) succeeded, we can use CLONE_NEWUSER.
+ return true;
}
bool Credentials::MoveToNewUserNS() {
@@ -322,9 +296,18 @@ bool Credentials::MoveToNewUserNS() {
return false;
}
+ if (NamespaceUtils::KernelSupportsDenySetgroups()) {
+ PCHECK(NamespaceUtils::DenySetgroups());
+ }
+
// The current {r,e,s}{u,g}id is now an overflow id (c.f.
// /proc/sys/kernel/overflowuid). Setup the uid and gid maps.
- SetGidAndUidMaps(gid, uid);
+ DCHECK(GetRESIds(NULL, NULL));
+ const char kGidMapFile[] = "/proc/self/gid_map";
+ const char kUidMapFile[] = "/proc/self/uid_map";
+ PCHECK(NamespaceUtils::WriteToIdMapFile(kGidMapFile, gid));
+ PCHECK(NamespaceUtils::WriteToIdMapFile(kUidMapFile, uid));
+ DCHECK(GetRESIds(NULL, NULL));
return true;
}
@@ -332,16 +315,12 @@ bool Credentials::DropFileSystemAccess(int proc_fd) {
CHECK_LE(0, proc_fd);
CHECK(ChrootToSafeEmptyDir());
- CHECK(!HasFileSystemAccess());
+ CHECK(!base::DirectoryExists(base::FilePath("/proc")));
CHECK(!ProcUtil::HasOpenDirectory(proc_fd));
// We never let this function fail.
return true;
}
-bool Credentials::HasFileSystemAccess() {
- return base::DirectoryExists(base::FilePath("/proc"));
-}
-
pid_t Credentials::ForkAndDropCapabilitiesInChild() {
pid_t pid = fork();
if (pid != 0) {
diff --git a/sandbox/linux/services/credentials.h b/sandbox/linux/services/credentials.h
index 157c8e75e8..b89a6aa7cf 100644
--- a/sandbox/linux/services/credentials.h
+++ b/sandbox/linux/services/credentials.h
@@ -94,9 +94,6 @@ class SANDBOX_EXPORT Credentials {
// - DropAllCapabilities() must be called to prevent escapes.
static bool DropFileSystemAccess(int proc_fd) WARN_UNUSED_RESULT;
- // This function returns true if the process can still access the filesystem.
- static bool HasFileSystemAccess();
-
// Forks and drops capabilities in the child.
static pid_t ForkAndDropCapabilitiesInChild();
diff --git a/sandbox/linux/services/credentials_unittest.cc b/sandbox/linux/services/credentials_unittest.cc
index 41c04bbcc2..b95ba0bab2 100644
--- a/sandbox/linux/services/credentials_unittest.cc
+++ b/sandbox/linux/services/credentials_unittest.cc
@@ -145,12 +145,11 @@ SANDBOX_TEST(Credentials, CanDetectRoot) {
// Disabled on ASAN because of crbug.com/451603.
SANDBOX_TEST(Credentials, DISABLE_ON_ASAN(DropFileSystemAccessIsSafe)) {
- CHECK(Credentials::HasFileSystemAccess());
CHECK(Credentials::DropAllCapabilities());
// Probably missing kernel support.
if (!Credentials::MoveToNewUserNS()) return;
CHECK(Credentials::DropFileSystemAccess(ProcUtil::OpenProc().get()));
- CHECK(!Credentials::HasFileSystemAccess());
+ CHECK(!base::DirectoryExists(base::FilePath("/proc")));
CHECK(WorkingDirectoryIsRoot());
CHECK(base::IsDirectoryEmpty(base::FilePath("/")));
// We want the chroot to never have a subdirectory. A subdirectory
@@ -246,19 +245,18 @@ void SignalHandler(int sig) {
signal_handler_called = 1;
}
-// glibc (and some other libcs) caches the PID and TID in TLS. This test
-// verifies that these values are correct after DropFilesystemAccess.
// Disabled on ASAN because of crbug.com/451603.
SANDBOX_TEST(Credentials, DISABLE_ON_ASAN(DropFileSystemAccessPreservesTLS)) {
// Probably missing kernel support.
if (!Credentials::MoveToNewUserNS()) return;
CHECK(Credentials::DropFileSystemAccess(ProcUtil::OpenProc().get()));
- // The libc getpid implementation may return a cached PID. Ensure that
- // it matches the PID returned from the getpid system call.
- CHECK_EQ(sys_getpid(), getpid());
+ // In glibc, pthread_getattr_np makes an assertion about the cached PID/TID in
+ // TLS.
+ pthread_attr_t attr;
+ EXPECT_EQ(0, pthread_getattr_np(pthread_self(), &attr));
- // raise uses the cached TID in glibc.
+ // raise also uses the cached TID in glibc.
struct sigaction action = {};
action.sa_handler = &SignalHandler;
PCHECK(sigaction(SIGUSR1, &action, nullptr) == 0);
diff --git a/sandbox/linux/services/syscall_wrappers.cc b/sandbox/linux/services/syscall_wrappers.cc
index 9c7727cee5..7132d2ade9 100644
--- a/sandbox/linux/services/syscall_wrappers.cc
+++ b/sandbox/linux/services/syscall_wrappers.cc
@@ -32,10 +32,6 @@ pid_t sys_gettid(void) {
return syscall(__NR_gettid);
}
-ssize_t sys_write(int fd, const char* buffer, size_t buffer_size) {
- return syscall(__NR_write, fd, buffer, buffer_size);
-}
-
long sys_clone(unsigned long flags,
std::nullptr_t child_stack,
pid_t* ptid,
diff --git a/sandbox/linux/services/syscall_wrappers.h b/sandbox/linux/services/syscall_wrappers.h
index 1975bfbd88..057e4c87f4 100644
--- a/sandbox/linux/services/syscall_wrappers.h
+++ b/sandbox/linux/services/syscall_wrappers.h
@@ -28,10 +28,6 @@ SANDBOX_EXPORT pid_t sys_getpid(void);
SANDBOX_EXPORT pid_t sys_gettid(void);
-SANDBOX_EXPORT ssize_t sys_write(int fd,
- const char* buffer,
- size_t buffer_size);
-
SANDBOX_EXPORT long sys_clone(unsigned long flags);
// |regs| is not supported and must be passed as nullptr. |child_stack| must be
diff --git a/sandbox/linux/suid/process_util_linux.c b/sandbox/linux/suid/process_util_linux.c
index d28d5766c3..40949bd6ac 100644
--- a/sandbox/linux/suid/process_util_linux.c
+++ b/sandbox/linux/suid/process_util_linux.c
@@ -59,7 +59,6 @@ bool AdjustOOMScore(pid_t process, int score) {
fd = openat(dirfd, "oom_adj", O_WRONLY);
if (fd < 0) {
// Nope, that doesn't work either.
- close(dirfd);
return false;
} else {
// If we're using the old oom_adj file, the allowed range is now
diff --git a/sandbox/linux/syscall_broker/broker_file_permission.h b/sandbox/linux/syscall_broker/broker_file_permission.h
index ddc62d5629..03300d1d74 100644
--- a/sandbox/linux/syscall_broker/broker_file_permission.h
+++ b/sandbox/linux/syscall_broker/broker_file_permission.h
@@ -61,7 +61,7 @@ class SANDBOX_EXPORT BrokerFilePermission {
// or a pointer the matched path in the whitelist if an absolute
// match.
// If not NULL |unlink_after_open| is set to point to true if the
- // caller should unlink the path after opening.
+ // caller should unlink the path after openning.
// Async signal safe if |file_to_open| is NULL.
bool CheckOpen(const char* requested_filename,
int flags,
diff --git a/sandbox/linux/syscall_broker/broker_file_permission_unittest.cc b/sandbox/linux/syscall_broker/broker_file_permission_unittest.cc
index 83840779f9..b58a901cde 100644
--- a/sandbox/linux/syscall_broker/broker_file_permission_unittest.cc
+++ b/sandbox/linux/syscall_broker/broker_file_permission_unittest.cc
@@ -46,19 +46,10 @@ SANDBOX_TEST(BrokerFilePermission, CreateGoodRecursive) {
BrokerFilePermission perm = BrokerFilePermission::ReadOnlyRecursive(kPath);
}
-// In official builds, CHECK(x) causes a SIGTRAP on the architectures where the
-// sanbox is enabled (that are x86, x86_64, arm64 and 32-bit arm processes
-// running on a arm64 kernel).
-#if defined(OFFICIAL_BUILD)
-#define DEATH_BY_CHECK(msg) DEATH_BY_SIGNAL(SIGTRAP)
-#else
-#define DEATH_BY_CHECK(msg) DEATH_MESSAGE(msg)
-#endif
-
SANDBOX_DEATH_TEST(
BrokerFilePermission,
CreateBad,
- DEATH_BY_CHECK(BrokerFilePermissionTester::GetErrorMessage())) {
+ DEATH_MESSAGE(BrokerFilePermissionTester::GetErrorMessage())) {
const char kPath[] = "/tmp/bad/";
BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
}
@@ -66,7 +57,7 @@ SANDBOX_DEATH_TEST(
SANDBOX_DEATH_TEST(
BrokerFilePermission,
CreateBadRecursive,
- DEATH_BY_CHECK(BrokerFilePermissionTester::GetErrorMessage())) {
+ DEATH_MESSAGE(BrokerFilePermissionTester::GetErrorMessage())) {
const char kPath[] = "/tmp/bad";
BrokerFilePermission perm = BrokerFilePermission::ReadOnlyRecursive(kPath);
}
@@ -74,7 +65,7 @@ SANDBOX_DEATH_TEST(
SANDBOX_DEATH_TEST(
BrokerFilePermission,
CreateBadNotAbs,
- DEATH_BY_CHECK(BrokerFilePermissionTester::GetErrorMessage())) {
+ DEATH_MESSAGE(BrokerFilePermissionTester::GetErrorMessage())) {
const char kPath[] = "tmp/bad";
BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
}
@@ -82,7 +73,7 @@ SANDBOX_DEATH_TEST(
SANDBOX_DEATH_TEST(
BrokerFilePermission,
CreateBadEmpty,
- DEATH_BY_CHECK(BrokerFilePermissionTester::GetErrorMessage())) {
+ DEATH_MESSAGE(BrokerFilePermissionTester::GetErrorMessage())) {
const char kPath[] = "";
BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
}
diff --git a/sandbox/linux/system_headers/arm64_linux_syscalls.h b/sandbox/linux/system_headers/arm64_linux_syscalls.h
index 59d0eab8ec..8acb2d1000 100644
--- a/sandbox/linux/system_headers/arm64_linux_syscalls.h
+++ b/sandbox/linux/system_headers/arm64_linux_syscalls.h
@@ -1059,8 +1059,4 @@
#define __NR_getrandom 278
#endif
-#if !defined(__NR_memfd_create)
-#define __NR_memfd_create 279
-#endif
-
#endif // SANDBOX_LINUX_SYSTEM_HEADERS_ARM64_LINUX_SYSCALLS_H_
diff --git a/sandbox/linux/system_headers/mips64_linux_syscalls.h b/sandbox/linux/system_headers/mips64_linux_syscalls.h
index 90f3d1bea8..d003124284 100644
--- a/sandbox/linux/system_headers/mips64_linux_syscalls.h
+++ b/sandbox/linux/system_headers/mips64_linux_syscalls.h
@@ -1263,12 +1263,4 @@
#define __NR_seccomp (__NR_Linux + 312)
#endif
-#if !defined(__NR_getrandom)
-#define __NR_getrandom (__NR_Linux + 313)
-#endif
-
-#if !defined(__NR_memfd_create)
-#define __NR_memfd_create (__NR_Linux + 314)
-#endif
-
#endif // SANDBOX_LINUX_SYSTEM_HEADERS_MIPS64_LINUX_SYSCALLS_H_
diff --git a/sandbox/linux/system_headers/mips_linux_syscalls.h b/sandbox/linux/system_headers/mips_linux_syscalls.h
index 784d6b8ae0..eb1717aad9 100644
--- a/sandbox/linux/system_headers/mips_linux_syscalls.h
+++ b/sandbox/linux/system_headers/mips_linux_syscalls.h
@@ -1425,12 +1425,4 @@
#define __NR_seccomp (__NR_Linux + 352)
#endif
-#if !defined(__NR_getrandom)
-#define __NR_getrandom (__NR_Linux + 353)
-#endif
-
-#if !defined(__NR_memfd_create)
-#define __NR_memfd_create (__NR_Linux + 354)
-#endif
-
#endif // SANDBOX_LINUX_SYSTEM_HEADERS_MIPS_LINUX_SYSCALLS_H_
diff --git a/sandbox/mac/BUILD.gn b/sandbox/mac/BUILD.gn
index 5174b54f81..fd53131dbb 100644
--- a/sandbox/mac/BUILD.gn
+++ b/sandbox/mac/BUILD.gn
@@ -35,8 +35,6 @@ component("sandbox") {
component("seatbelt") {
sources = [
- "sandbox_compiler.cc",
- "sandbox_compiler.h",
"seatbelt.cc",
"seatbelt.h",
"seatbelt_export.h",
@@ -49,8 +47,6 @@ test("sandbox_mac_unittests") {
sources = [
"bootstrap_sandbox_unittest.mm",
"policy_unittest.cc",
- "sandbox_mac_compiler_unittest.mm",
- "sandbox_mac_compiler_v2_unittest.mm",
"xpc_message_server_unittest.cc",
]
@@ -61,7 +57,6 @@ test("sandbox_mac_unittests") {
deps = [
":sandbox",
- ":seatbelt",
"//base",
"//base/test:run_all_unittests",
"//testing/gtest",
diff --git a/sandbox/mac/sandbox_mac.gypi b/sandbox/mac/sandbox_mac.gypi
new file mode 100644
index 0000000000..79740e5a84
--- /dev/null
+++ b/sandbox/mac/sandbox_mac.gypi
@@ -0,0 +1,104 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'seatbelt',
+ 'type' : '<(component)',
+ 'sources': [
+ 'seatbelt.cc',
+ 'seatbelt.h',
+ 'seatbelt_export.h',
+ ],
+ 'defines': [
+ 'SEATBELT_IMPLEMENTATION',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/usr/lib/libsandbox.dylib',
+ ],
+ }
+ },
+ {
+ 'target_name': 'sandbox',
+ 'type': '<(component)',
+ 'sources': [
+ 'bootstrap_sandbox.cc',
+ 'bootstrap_sandbox.h',
+ 'launchd_interception_server.cc',
+ 'launchd_interception_server.h',
+ 'mach_message_server.cc',
+ 'mach_message_server.h',
+ 'message_server.h',
+ 'os_compatibility.cc',
+ 'os_compatibility.h',
+ 'policy.cc',
+ 'policy.h',
+ 'pre_exec_delegate.cc',
+ 'pre_exec_delegate.h',
+ 'xpc.h',
+ 'xpc_message_server.cc',
+ 'xpc_message_server.h',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ ],
+ 'include_dirs': [
+ '..',
+ '<(SHARED_INTERMEDIATE_DIR)',
+ ],
+ 'defines': [
+ 'SANDBOX_IMPLEMENTATION',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/usr/lib/libbsm.dylib',
+ ],
+ },
+ },
+ {
+ 'target_name': 'sandbox_mac_unittests',
+ 'type': 'executable',
+ 'sources': [
+ 'bootstrap_sandbox_unittest.mm',
+ 'policy_unittest.cc',
+ 'xpc_message_server_unittest.cc',
+ ],
+ 'dependencies': [
+ 'sandbox',
+ '../base/base.gyp:base',
+ '../base/base.gyp:run_all_unittests',
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/CoreFoundation.framework',
+ '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+ ],
+ },
+ },
+ ],
+ 'conditions': [
+ ['test_isolation_mode != "noop"', {
+ 'targets': [
+ {
+ 'target_name': 'sandbox_mac_unittests_run',
+ 'type': 'none',
+ 'dependencies': [
+ 'sandbox_mac_unittests',
+ ],
+ 'includes': [ '../../build/isolate.gypi' ],
+ 'sources': [ '../sandbox_mac_unittests.isolate' ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/sandbox/sandbox.gyp b/sandbox/sandbox.gyp
new file mode 100644
index 0000000000..f93fa1862a
--- /dev/null
+++ b/sandbox/sandbox.gyp
@@ -0,0 +1,35 @@
+# 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.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'conditions': [
+ [ 'OS=="win"', {
+ 'includes': [
+ 'win/sandbox_win.gypi',
+ ],
+ }],
+ [ 'OS=="linux" or OS=="android"', {
+ 'includes': [
+ 'linux/sandbox_linux.gypi',
+ ],
+ }],
+ [ 'OS=="mac" and OS!="ios"', {
+ 'includes': [
+ 'mac/sandbox_mac.gypi',
+ ],
+ }],
+ [ 'OS!="win" and OS!="mac" and OS!="linux" and OS!="android"', {
+ # A 'default' to accomodate the "sandbox" target.
+ 'targets': [
+ {
+ 'target_name': 'sandbox',
+ 'type': 'none',
+ }
+ ]
+ }],
+ ],
+}
diff --git a/sandbox/sandbox_linux_unittests.isolate b/sandbox/sandbox_linux_unittests.isolate
new file mode 100644
index 0000000000..2b7c2a73af
--- /dev/null
+++ b/sandbox/sandbox_linux_unittests.isolate
@@ -0,0 +1,23 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Because of a limitation in isolate_driver.py, this file needs to be in
+# the same directory as the main .gyp file.
+
+{
+ 'conditions': [
+ ['OS=="android" or OS=="linux"', {
+ 'variables': {
+ 'command': [
+ '<(PRODUCT_DIR)/sandbox_linux_unittests',
+ ],
+ },
+ }],
+ ],
+ 'includes': [
+ # This is needed because of base/ dependencies on
+ # icudtl.dat.
+ '../base/base.isolate',
+ ],
+}
diff --git a/sandbox/win/BUILD.gn b/sandbox/win/BUILD.gn
index 1d51220c5a..60bb499af3 100644
--- a/sandbox/win/BUILD.gn
+++ b/sandbox/win/BUILD.gn
@@ -154,18 +154,31 @@ static_library("sandbox") {
]
}
- # Disable sanitizer coverage in the sandbox code. The sandbox code runs before
- # sanitizer coverage can initialize. http://crbug.com/484711
- configs -= [ "//build/config/sanitizers:default_sanitizer_flags" ]
- configs +=
- [ "//build/config/sanitizers:default_sanitizer_flags_but_coverage" ]
-
configs += [ "//build/config:precompiled_headers" ]
deps = [
"//base",
"//base:base_static",
]
+ if (current_cpu == "x86") {
+ deps += [ ":copy_wow_helper" ]
+ }
+}
+
+if (current_cpu == "x86") {
+ # Make a target that copies the wow_helper files to the out dir.
+ #
+ # TODO(brettw) we can probably just build this now that we have proper
+ # toolchain support.
+ copy("copy_wow_helper") {
+ sources = [
+ "wow_helper/wow_helper.exe",
+ "wow_helper/wow_helper.pdb",
+ ]
+ outputs = [
+ "$root_out_dir/{{source_file_part}}",
+ ]
+ }
}
test("sbox_integration_tests") {
@@ -191,7 +204,6 @@ test("sbox_integration_tests") {
"tests/common/controller.h",
"tests/common/test_utils.cc",
"tests/common/test_utils.h",
- "tests/integration_tests/cfi_unittest.cc",
"tests/integration_tests/integration_tests.cc",
"tests/integration_tests/integration_tests_common.h",
"tests/integration_tests/integration_tests_test.cc",
@@ -199,30 +211,15 @@ test("sbox_integration_tests") {
deps = [
":sandbox",
- "//base/test:test_support",
- "//testing/gtest",
- ]
-
- data_deps = [
- ":cfi_unittest_exe",
":sbox_integration_test_hook_dll",
":sbox_integration_test_win_proc",
+ "//base/test:test_support",
+ "//testing/gtest",
]
libs = [ "dxva2.lib" ]
}
-executable("cfi_unittest_exe") {
- sources = [
- "tests/integration_tests/cfi_unittest_exe.cc",
- ]
- deps = [
- "//base",
- "//build/config/sanitizers:deps",
- "//build/win:default_exe_manifest",
- ]
-}
-
loadable_module("sbox_integration_test_hook_dll") {
sources = [
"tests/integration_tests/hooking_dll.cc",
diff --git a/sandbox/win/sandbox_win.gypi b/sandbox/win/sandbox_win.gypi
new file mode 100644
index 0000000000..e9673aa9a1
--- /dev/null
+++ b/sandbox/win/sandbox_win.gypi
@@ -0,0 +1,432 @@
+# 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.
+
+{
+ 'target_defaults': {
+ 'variables': {
+ 'sandbox_windows_target': 0,
+ 'target_arch%': 'ia32',
+ },
+ 'target_conditions': [
+ ['sandbox_windows_target==1', {
+ # Files that are shared between the 32-bit and the 64-bit versions
+ # of the Windows sandbox library.
+ 'sources': [
+ 'src/acl.cc',
+ 'src/acl.h',
+ 'src/broker_services.cc',
+ 'src/broker_services.h',
+ 'src/crosscall_client.h',
+ 'src/crosscall_params.h',
+ 'src/crosscall_server.cc',
+ 'src/crosscall_server.h',
+ 'src/eat_resolver.cc',
+ 'src/eat_resolver.h',
+ 'src/filesystem_dispatcher.cc',
+ 'src/filesystem_dispatcher.h',
+ 'src/filesystem_interception.cc',
+ 'src/filesystem_interception.h',
+ 'src/filesystem_policy.cc',
+ 'src/filesystem_policy.h',
+ 'src/handle_closer.cc',
+ 'src/handle_closer.h',
+ 'src/handle_closer_agent.cc',
+ 'src/handle_closer_agent.h',
+ 'src/interception.cc',
+ 'src/interception.h',
+ 'src/interception_agent.cc',
+ 'src/interception_agent.h',
+ 'src/interception_internal.h',
+ 'src/interceptors.h',
+ 'src/internal_types.h',
+ 'src/ipc_tags.h',
+ 'src/job.cc',
+ 'src/job.h',
+ 'src/named_pipe_dispatcher.cc',
+ 'src/named_pipe_dispatcher.h',
+ 'src/named_pipe_interception.cc',
+ 'src/named_pipe_interception.h',
+ 'src/named_pipe_policy.cc',
+ 'src/named_pipe_policy.h',
+ 'src/nt_internals.h',
+ 'src/policy_broker.cc',
+ 'src/policy_broker.h',
+ 'src/policy_engine_opcodes.cc',
+ 'src/policy_engine_opcodes.h',
+ 'src/policy_engine_params.h',
+ 'src/policy_engine_processor.cc',
+ 'src/policy_engine_processor.h',
+ 'src/policy_low_level.cc',
+ 'src/policy_low_level.h',
+ 'src/policy_params.h',
+ 'src/policy_target.cc',
+ 'src/policy_target.h',
+ 'src/process_mitigations.cc',
+ 'src/process_mitigations.h',
+ 'src/process_mitigations_win32k_dispatcher.cc',
+ 'src/process_mitigations_win32k_dispatcher.h',
+ 'src/process_mitigations_win32k_interception.cc',
+ 'src/process_mitigations_win32k_interception.h',
+ 'src/process_mitigations_win32k_policy.cc',
+ 'src/process_mitigations_win32k_policy.h',
+ 'src/process_thread_dispatcher.cc',
+ 'src/process_thread_dispatcher.h',
+ 'src/process_thread_interception.cc',
+ 'src/process_thread_interception.h',
+ 'src/process_thread_policy.cc',
+ 'src/process_thread_policy.h',
+ 'src/registry_dispatcher.cc',
+ 'src/registry_dispatcher.h',
+ 'src/registry_interception.cc',
+ 'src/registry_interception.h',
+ 'src/registry_policy.cc',
+ 'src/registry_policy.h',
+ 'src/resolver.cc',
+ 'src/resolver.h',
+ 'src/restricted_token_utils.cc',
+ 'src/restricted_token_utils.h',
+ 'src/restricted_token.cc',
+ 'src/restricted_token.h',
+ 'src/sandbox_factory.h',
+ 'src/sandbox_globals.cc',
+ 'src/sandbox_nt_types.h',
+ 'src/sandbox_nt_util.cc',
+ 'src/sandbox_nt_util.h',
+ 'src/sandbox_policy_base.cc',
+ 'src/sandbox_policy_base.h',
+ 'src/sandbox_policy.h',
+ 'src/sandbox_rand.cc',
+ 'src/sandbox_rand.h',
+ 'src/sandbox_types.h',
+ 'src/sandbox_utils.cc',
+ 'src/sandbox_utils.h',
+ 'src/sandbox.cc',
+ 'src/sandbox.h',
+ 'src/security_level.h',
+ 'src/service_resolver.cc',
+ 'src/service_resolver.h',
+ 'src/sharedmem_ipc_client.cc',
+ 'src/sharedmem_ipc_client.h',
+ 'src/sharedmem_ipc_server.cc',
+ 'src/sharedmem_ipc_server.h',
+ 'src/sid.cc',
+ 'src/sid.h',
+ 'src/sync_dispatcher.cc',
+ 'src/sync_dispatcher.h',
+ 'src/sync_interception.cc',
+ 'src/sync_interception.h',
+ 'src/sync_policy.cc',
+ 'src/sync_policy.h',
+ 'src/target_interceptions.cc',
+ 'src/target_interceptions.h',
+ 'src/target_process.cc',
+ 'src/target_process.h',
+ 'src/target_services.cc',
+ 'src/target_services.h',
+ 'src/top_level_dispatcher.cc',
+ 'src/top_level_dispatcher.h',
+ 'src/win_utils.cc',
+ 'src/win_utils.h',
+ 'src/win2k_threadpool.cc',
+ 'src/win2k_threadpool.h',
+ 'src/window.cc',
+ 'src/window.h',
+ ],
+ 'target_conditions': [
+ ['target_arch=="x64"', {
+ 'sources': [
+ 'src/interceptors_64.cc',
+ 'src/interceptors_64.h',
+ 'src/resolver_64.cc',
+ 'src/service_resolver_64.cc',
+ ],
+ }],
+ ['target_arch=="ia32"', {
+ 'sources': [
+ 'src/resolver_32.cc',
+ 'src/service_resolver_32.cc',
+ 'src/sidestep_resolver.cc',
+ 'src/sidestep_resolver.h',
+ 'src/sidestep\ia32_modrm_map.cpp',
+ 'src/sidestep\ia32_opcode_map.cpp',
+ 'src/sidestep\mini_disassembler_types.h',
+ 'src/sidestep\mini_disassembler.cpp',
+ 'src/sidestep\mini_disassembler.h',
+ 'src/sidestep\preamble_patcher_with_stub.cpp',
+ 'src/sidestep\preamble_patcher.h',
+ ],
+ }],
+ ],
+ }],
+ ],
+ },
+ 'targets': [
+ {
+ 'target_name': 'sandbox',
+ 'type': 'static_library',
+ 'variables': {
+ 'sandbox_windows_target': 1,
+ },
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/base.gyp:base_static',
+ ],
+ 'export_dependent_settings': [
+ '../base/base.gyp:base',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ 'target_conditions': [
+ ['target_arch=="ia32"', {
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)',
+ 'files': [
+ 'wow_helper/wow_helper.exe',
+ 'wow_helper/wow_helper.pdb',
+ ],
+ },
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'sbox_integration_tests',
+ 'type': 'executable',
+ 'dependencies': [
+ 'sandbox',
+ 'sbox_integration_test_hook_dll',
+ 'sbox_integration_test_win_proc',
+ '../base/base.gyp:test_support_base',
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'sources': [
+ 'src/address_sanitizer_test.cc',
+ 'src/app_container_test.cc',
+ 'src/file_policy_test.cc',
+ 'src/handle_inheritance_test.cc',
+ 'tests/integration_tests/integration_tests_test.cc',
+ 'src/handle_closer_test.cc',
+ 'src/integrity_level_test.cc',
+ 'src/ipc_ping_test.cc',
+ 'src/lpc_policy_test.cc',
+ 'src/named_pipe_policy_test.cc',
+ 'src/policy_target_test.cc',
+ 'src/process_mitigations_test.cc',
+ 'src/process_policy_test.cc',
+ 'src/registry_policy_test.cc',
+ 'src/restricted_token_test.cc',
+ 'src/sync_policy_test.cc',
+ 'src/sync_policy_test.h',
+ 'src/unload_dll_test.cc',
+ 'tests/common/controller.cc',
+ 'tests/common/controller.h',
+ 'tests/common/test_utils.cc',
+ 'tests/common/test_utils.h',
+ 'tests/integration_tests/integration_tests.cc',
+ 'tests/integration_tests/integration_tests_common.h',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '-ldxva2.lib',
+ ],
+ },
+ },
+ {
+ 'target_name': 'sbox_integration_test_hook_dll',
+ 'type': 'shared_library',
+ 'dependencies': [
+ ],
+ 'sources': [
+ 'tests/integration_tests/hooking_dll.cc',
+ 'tests/integration_tests/integration_tests_common.h',
+ ],
+ },
+ {
+ 'target_name': 'sbox_integration_test_win_proc',
+ 'type': 'executable',
+ 'dependencies': [
+ ],
+ 'sources': [
+ 'tests/integration_tests/hooking_win_proc.cc',
+ 'tests/integration_tests/integration_tests_common.h',
+ ],
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'SubSystem': '2', # Set /SUBSYSTEM:WINDOWS
+ },
+ },
+ },
+ {
+ 'target_name': 'sbox_validation_tests',
+ 'type': 'executable',
+ 'dependencies': [
+ 'sandbox',
+ '../base/base.gyp:test_support_base',
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'sources': [
+ 'tests/common/controller.cc',
+ 'tests/common/controller.h',
+ 'tests/validation_tests/unit_tests.cc',
+ 'tests/validation_tests/commands.cc',
+ 'tests/validation_tests/commands.h',
+ 'tests/validation_tests/suite.cc',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '-lshlwapi.lib',
+ ],
+ },
+ },
+ {
+ 'target_name': 'sbox_unittests',
+ 'type': 'executable',
+ 'dependencies': [
+ 'sandbox',
+ '../base/base.gyp:test_support_base',
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'sources': [
+ 'src/interception_unittest.cc',
+ 'src/service_resolver_unittest.cc',
+ 'src/restricted_token_unittest.cc',
+ 'src/job_unittest.cc',
+ 'src/sid_unittest.cc',
+ 'src/policy_engine_unittest.cc',
+ 'src/policy_low_level_unittest.cc',
+ 'src/policy_opcodes_unittest.cc',
+ 'src/ipc_unittest.cc',
+ 'src/sandbox_nt_util_unittest.cc',
+ 'src/threadpool_unittest.cc',
+ 'src/win_utils_unittest.cc',
+ 'tests/common/test_utils.cc',
+ 'tests/common/test_utils.h',
+ 'tests/unit_tests/unit_tests.cc',
+ ],
+ },
+ {
+ 'target_name': 'sandbox_poc',
+ 'type': 'executable',
+ 'dependencies': [
+ 'sandbox',
+ 'pocdll',
+ ],
+ 'sources': [
+ 'sandbox_poc/main_ui_window.cc',
+ 'sandbox_poc/main_ui_window.h',
+ 'sandbox_poc/resource.h',
+ 'sandbox_poc/sandbox.cc',
+ 'sandbox_poc/sandbox.h',
+ 'sandbox_poc/sandbox.ico',
+ 'sandbox_poc/sandbox.rc',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '-lcomctl32.lib',
+ ],
+ },
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'SubSystem': '2', # Set /SUBSYSTEM:WINDOWS
+ },
+ },
+ },
+ {
+ 'target_name': 'pocdll',
+ 'type': 'shared_library',
+ 'sources': [
+ 'sandbox_poc/pocdll/exports.h',
+ 'sandbox_poc/pocdll/fs.cc',
+ 'sandbox_poc/pocdll/handles.cc',
+ 'sandbox_poc/pocdll/invasive.cc',
+ 'sandbox_poc/pocdll/network.cc',
+ 'sandbox_poc/pocdll/pocdll.cc',
+ 'sandbox_poc/pocdll/processes_and_threads.cc',
+ 'sandbox_poc/pocdll/registry.cc',
+ 'sandbox_poc/pocdll/spyware.cc',
+ 'sandbox_poc/pocdll/utils.h',
+ ],
+ 'defines': [
+ 'POCDLL_EXPORTS',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ },
+ ],
+ 'conditions': [
+ ['OS=="win" and target_arch=="ia32"', {
+ 'targets': [
+ {
+ 'target_name': 'sandbox_win64',
+ 'type': 'static_library',
+ 'variables': {
+ 'sandbox_windows_target': 1,
+ 'target_arch': 'x64',
+ },
+ 'dependencies': [
+ '../base/base.gyp:base_win64',
+ '../base/base.gyp:base_static_win64',
+ ],
+ 'configurations': {
+ 'Common_Base': {
+ 'msvs_target_platform': 'x64',
+ },
+ },
+ 'include_dirs': [
+ '../..',
+ ],
+ 'defines': [
+ '<@(nacl_win64_defines)',
+ ]
+ },
+ ],
+ }],
+ ['test_isolation_mode != "noop"', {
+ 'targets': [
+ {
+ 'target_name': 'sbox_integration_tests_run',
+ 'type': 'none',
+ 'dependencies': [
+ 'sbox_integration_tests',
+ ],
+ 'includes': [
+ '../../build/isolate.gypi',
+ ],
+ 'sources': [
+ '../sbox_integration_tests.isolate',
+ ],
+ },
+ {
+ 'target_name': 'sbox_unittests_run',
+ 'type': 'none',
+ 'dependencies': [
+ 'sbox_unittests',
+ ],
+ 'includes': [
+ '../../build/isolate.gypi',
+ ],
+ 'sources': [
+ '../sbox_unittests.isolate',
+ ],
+ },
+ {
+ 'target_name': 'sbox_validation_tests_run',
+ 'type': 'none',
+ 'dependencies': [
+ 'sbox_validation_tests',
+ ],
+ 'includes': [
+ '../../build/isolate.gypi',
+ ],
+ 'sources': [
+ '../sbox_validation_tests.isolate',
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/sandbox/win/src/internal_types.h b/sandbox/win/src/internal_types.h
index 7ea4b7d62e..e1028189d8 100644
--- a/sandbox/win/src/internal_types.h
+++ b/sandbox/win/src/internal_types.h
@@ -13,7 +13,7 @@ const wchar_t kNtdllName[] = L"ntdll.dll";
const wchar_t kKerneldllName[] = L"kernel32.dll";
const wchar_t kKernelBasedllName[] = L"kernelbase.dll";
-// Defines the supported C++ types encoding to numeric id. Like a simplified
+// Defines the supported C++ types encoding to numeric id. Like a poor's man
// RTTI. Note that true C++ RTTI will not work because the types are not
// polymorphic anyway.
enum ArgType {
diff --git a/sandbox/win/src/sandbox.vcproj b/sandbox/win/src/sandbox.vcproj
index 229441cbd5..f206e01a1f 100644
--- a/sandbox/win/src/sandbox.vcproj
+++ b/sandbox/win/src/sandbox.vcproj
@@ -64,6 +64,11 @@
<Tool
Name="VCFxCopTool"
/>
+ <Tool
+ Name="VCPostBuildEventTool"
+ Description="Copy wow_helper to output directory"
+ CommandLine="copy $(ProjectDir)\..\wow_helper\wow_helper.exe $(OutDir) &amp;&amp; copy $(ProjectDir)\..\wow_helper\wow_helper.pdb $(OutDir)"
+ />
</Configuration>
<Configuration
Name="Release|Win32"
@@ -113,6 +118,11 @@
<Tool
Name="VCFxCopTool"
/>
+ <Tool
+ Name="VCPostBuildEventTool"
+ Description="Copy wow_helper to output directory"
+ CommandLine="copy $(ProjectDir)\..\wow_helper\wow_helper.exe $(OutDir) &amp;&amp; copy $(ProjectDir)\..\wow_helper\wow_helper.pdb $(OutDir)"
+ />
</Configuration>
</Configurations>
<References>
diff --git a/sandbox/win/src/sandbox_types.h b/sandbox/win/src/sandbox_types.h
index ae36ef5c95..919086a828 100644
--- a/sandbox/win/src/sandbox_types.h
+++ b/sandbox/win/src/sandbox_types.h
@@ -5,7 +5,6 @@
#ifndef SANDBOX_WIN_SRC_SANDBOX_TYPES_H_
#define SANDBOX_WIN_SRC_SANDBOX_TYPES_H_
-#include "base/process/kill.h"
#include "base/process/launch.h"
namespace sandbox {
@@ -104,8 +103,6 @@ enum ResultCode : int {
SBOX_ERROR_CANNOT_RESOLVE_INTERCEPTION_THUNK = 41,
// Cannot write interception thunk to child process.
SBOX_ERROR_CANNOT_WRITE_INTERCEPTION_THUNK = 42,
- // Cannot find the base address of the new process.
- SBOX_ERROR_CANNOT_FIND_BASE_ADDRESS = 43,
// Placeholder for last item of the enum.
SBOX_ERROR_LAST
};
@@ -124,10 +121,6 @@ enum TerminationCodes {
SBOX_FATAL_LAST
};
-static_assert(SBOX_FATAL_MEMORY_EXCEEDED ==
- base::win::kSandboxFatalMemoryExceeded,
- "Value for SBOX_FATAL_MEMORY_EXCEEDED must match base.");
-
class BrokerServices;
class TargetServices;
diff --git a/sandbox/win/src/security_level.h b/sandbox/win/src/security_level.h
index ecca64d8fc..d8524c1fac 100644
--- a/sandbox/win/src/security_level.h
+++ b/sandbox/win/src/security_level.h
@@ -154,13 +154,11 @@ const MitigationFlags MITIGATION_DEP_NO_ATL_THUNK = 0x00000002;
// PROCESS_CREATION_MITIGATION_POLICY_SEHOP_ENABLE.
const MitigationFlags MITIGATION_SEHOP = 0x00000004;
-// Forces ASLR on all images in the child process. In debug builds, must be
-// enabled after startup. Corresponds to
+// Forces ASLR on all images in the child process. Corresponds to
// PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_ALWAYS_ON .
const MitigationFlags MITIGATION_RELOCATE_IMAGE = 0x00000008;
-// Refuses to load DLLs that cannot support ASLR. In debug builds, must be
-// enabled after startup. Corresponds to
+// Refuses to load DLLs that cannot support ASLR. Corresponds to
// PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_ALWAYS_ON_REQ_RELOCS.
const MitigationFlags MITIGATION_RELOCATE_IMAGE_REQUIRED = 0x00000010;
@@ -187,11 +185,6 @@ const MitigationFlags MITIGATION_STRICT_HANDLE_CHECKS = 0x00000100;
// Prevents the process from making Win32k calls. Corresponds to
// PROCESS_CREATION_MITIGATION_POLICY_WIN32K_SYSTEM_CALL_DISABLE_ALWAYS_ON.
-//
-// Applications linked to user32.dll or gdi32.dll make Win32k calls during
-// setup, even if Win32k is not otherwise used. So they also need to add a rule
-// with SUBSYS_WIN32K_LOCKDOWN and semantics FAKE_USER_GDI_INIT to allow the
-// initialization to succeed.
const MitigationFlags MITIGATION_WIN32K_DISABLE = 0x00000200;
// Prevents certain built-in third party extension points from being used.
diff --git a/sandbox/win/wow_helper.sln b/sandbox/win/wow_helper.sln
new file mode 100644
index 0000000000..26d0da2526
--- /dev/null
+++ b/sandbox/win/wow_helper.sln
@@ -0,0 +1,19 @@
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wow_helper", "wow_helper\wow_helper.vcproj", "{BCF3A457-39F1-4DAA-9A65-93CFCD559036}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {BCF3A457-39F1-4DAA-9A65-93CFCD559036}.Debug|x64.ActiveCfg = Debug|x64
+ {BCF3A457-39F1-4DAA-9A65-93CFCD559036}.Debug|x64.Build.0 = Debug|x64
+ {BCF3A457-39F1-4DAA-9A65-93CFCD559036}.Release|x64.ActiveCfg = Release|x64
+ {BCF3A457-39F1-4DAA-9A65-93CFCD559036}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/sandbox/win/wow_helper/wow_helper.exe b/sandbox/win/wow_helper/wow_helper.exe
new file mode 100755
index 0000000000..f9bfb4bbdd
--- /dev/null
+++ b/sandbox/win/wow_helper/wow_helper.exe
Binary files differ
diff --git a/sandbox/win/wow_helper/wow_helper.pdb b/sandbox/win/wow_helper/wow_helper.pdb
new file mode 100644
index 0000000000..9cb67d001d
--- /dev/null
+++ b/sandbox/win/wow_helper/wow_helper.pdb
Binary files differ
diff --git a/sandbox/win/wow_helper/wow_helper.vcproj b/sandbox/win/wow_helper/wow_helper.vcproj
new file mode 100644
index 0000000000..c8e7c9ebff
--- /dev/null
+++ b/sandbox/win/wow_helper/wow_helper.vcproj
@@ -0,0 +1,215 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="wow_helper"
+ ProjectGUID="{BCF3A457-39F1-4DAA-9A65-93CFCD559036}"
+ RootNamespace="wow_helper"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(ProjectDir)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)..;$(SolutionDir)..\third_party\platformsdk_win2008_6_1\files\Include;$(VSInstallDir)\VC\atlmfc\include"
+ PreprocessorDefinitions="_WIN32_WINNT=0x0501;WINVER=0x0501;WIN32;_DEBUG"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="0"
+ RuntimeLibrary="1"
+ BufferSecurityCheck="false"
+ RuntimeTypeInfo="false"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(ProjectDir)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="$(SolutionDir)..;$(SolutionDir)..\third_party\platformsdk_win2008_6_1\files\Include;$(VSInstallDir)\VC\atlmfc\include"
+ PreprocessorDefinitions="_WIN32_WINNT=0x0501;WINVER=0x0501;WIN32;NDEBUG"
+ RuntimeLibrary="0"
+ BufferSecurityCheck="false"
+ RuntimeTypeInfo="false"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="sandbox"
+ >
+ <File
+ RelativePath="..\src\nt_internals.h"
+ >
+ </File>
+ <File
+ RelativePath="..\src\resolver.h"
+ >
+ </File>
+ </Filter>
+ <File
+ RelativePath=".\service64_resolver.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\service64_resolver.h"
+ >
+ </File>
+ <File
+ RelativePath=".\target_code.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\target_code.h"
+ >
+ </File>
+ <File
+ RelativePath=".\wow_helper.cc"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>