diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-01-23 17:21:03 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-01-23 16:25:15 +0000 |
commit | c551f43206405019121bd2b2c93714319a0a3300 (patch) | |
tree | 1f48c30631c421fd4bbb3c36da20183c8a2ed7d7 /chromium/base | |
parent | 7961cea6d1041e3e454dae6a1da660b453efd238 (diff) | |
download | qtwebengine-chromium-c551f43206405019121bd2b2c93714319a0a3300.tar.gz |
BASELINE: Update Chromium to 79.0.3945.139
Change-Id: I336b7182fab9bca80b709682489c07db112eaca5
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/base')
302 files changed, 7142 insertions, 4148 deletions
diff --git a/chromium/base/BUILD.gn b/chromium/base/BUILD.gn index c093596dba0..200b8daecfb 100644 --- a/chromium/base/BUILD.gn +++ b/chromium/base/BUILD.gn @@ -34,6 +34,7 @@ import("//build/nocompile.gni") import("//build/timestamp.gni") import("//testing/libfuzzer/fuzzer_test.gni") import("//testing/test.gni") +import("//third_party/icu/config.gni") if (is_mac) { # Used to generate fuzzer corpus :base_mach_port_rendezvous_convert_corpus. @@ -317,6 +318,7 @@ jumbo_component("base") { "ios/scoped_critical_action.mm", "ios/weak_nsobject.h", "ios/weak_nsobject.mm", + "json/json_common.h", "json/json_file_value_serializer.cc", "json/json_file_value_serializer.h", "json/json_parser.cc", @@ -618,6 +620,10 @@ jumbo_component("base") { "profiler/stack_buffer.h", "profiler/stack_copier.cc", "profiler/stack_copier.h", + "profiler/stack_copier_signal.cc", + "profiler/stack_copier_signal.h", + "profiler/stack_copier_suspend.cc", + "profiler/stack_copier_suspend.h", "profiler/stack_sampler.cc", "profiler/stack_sampler.h", "profiler/stack_sampler_android.cc", @@ -627,13 +633,14 @@ jumbo_component("base") { "profiler/stack_sampler_win.cc", "profiler/stack_sampling_profiler.cc", "profiler/stack_sampling_profiler.h", + "profiler/suspendable_thread_delegate.h", + "profiler/suspendable_thread_delegate_mac.cc", + "profiler/suspendable_thread_delegate_mac.h", + "profiler/suspendable_thread_delegate_win.cc", + "profiler/suspendable_thread_delegate_win.h", "profiler/thread_delegate.h", "profiler/thread_delegate_android.cc", "profiler/thread_delegate_android.h", - "profiler/thread_delegate_mac.cc", - "profiler/thread_delegate_mac.h", - "profiler/thread_delegate_win.cc", - "profiler/thread_delegate_win.h", "profiler/unwinder.h", "rand_util.cc", "rand_util.h", @@ -810,6 +817,8 @@ jumbo_component("base") { "task/sequence_manager/work_queue.h", "task/sequence_manager/work_queue_sets.cc", "task/sequence_manager/work_queue_sets.h", + "task/simple_task_executor.cc", + "task/simple_task_executor.h", "task/single_thread_task_executor.cc", "task/single_thread_task_executor.h", "task/single_thread_task_runner_thread_mode.h", @@ -1065,8 +1074,6 @@ jumbo_component("base") { "win/i18n.h", "win/iat_patch_function.cc", "win/iat_patch_function.h", - "win/iunknown_impl.cc", - "win/iunknown_impl.h", "win/map.h", "win/message_window.cc", "win/message_window.h", @@ -1244,6 +1251,7 @@ jumbo_component("base") { "//base/allocator:buildflags", "//base/third_party/double_conversion", "//base/third_party/dynamic_annotations", + "//build:branding_buildflags", "//third_party/modp_b64", ] @@ -1366,6 +1374,8 @@ jumbo_component("base") { "android/java_exception_reporter.h", "android/java_handler_thread.cc", "android/java_handler_thread.h", + "android/java_heap_dump_generator.cc", + "android/java_heap_dump_generator.h", "android/java_runtime.cc", "android/java_runtime.h", "android/jni_android.cc", @@ -2158,6 +2168,11 @@ static_library("base_static") { ] if (is_win) { + sources += [ + "win/static_constants.cc", + "win/static_constants.h", + ] + public_deps = [ "//base/win:pe_image", ] @@ -2475,7 +2490,7 @@ test("base_unittests") { "android/unguessable_token_android_unittest.cc", "at_exit_unittest.cc", "atomicops_unittest.cc", - "auto_reset_unittests.cc", + "auto_reset_unittest.cc", "barrier_closure_unittest.cc", "base64_unittest.cc", "base64url_unittest.cc", @@ -2541,6 +2556,7 @@ test("base_unittests") { "i18n/character_encoding_unittest.cc", "i18n/file_util_icu_unittest.cc", "i18n/icu_string_conversions_unittest.cc", + "i18n/icu_util_unittest.cc", "i18n/message_formatter_unittest.cc", "i18n/number_formatting_unittest.cc", "i18n/rtl_unittest.cc", @@ -2637,6 +2653,7 @@ test("base_unittests") { "process/process_util_unittest.cc", "profiler/metadata_recorder_unittest.cc", "profiler/sample_metadata_unittest.cc", + "profiler/stack_copier_suspend_unittest.cc", "profiler/stack_copier_unittest.cc", "profiler/stack_sampler_impl_unittest.cc", "profiler/stack_sampling_profiler_unittest.cc", @@ -2685,6 +2702,7 @@ test("base_unittests") { "task/common/operations_controller_unittest.cc", "task/common/task_annotator_unittest.cc", "task/lazy_task_runner_unittest.cc", + "task/post_job_unittest.cc", "task/post_task_unittest.cc", "task/promise/abstract_promise_unittest.cc", "task/promise/dependent_list_unittest.cc", @@ -2704,6 +2722,7 @@ test("base_unittests") { "task/sequence_manager/work_deduplicator_unittest.cc", "task/sequence_manager/work_queue_sets_unittest.cc", "task/sequence_manager/work_queue_unittest.cc", + "task/single_thread_task_executor_unittest.cc", "task/task_traits_extension_unittest.cc", "task/task_traits_unittest.cc", "task/thread_pool/can_run_policy_test.h", @@ -2810,7 +2829,6 @@ test("base_unittests") { "win/hstring_compare_unittest.cc", "win/hstring_reference_unittest.cc", "win/i18n_unittest.cc", - "win/iunknown_impl_unittest.cc", "win/map_unittest.cc", "win/message_window_unittest.cc", "win/object_watcher_unittest.cc", @@ -2919,6 +2937,15 @@ test("base_unittests") { ] } + if (icu_use_data_file) { + if (is_android) { + deps += [ "//third_party/icu:icu_extra_assets" ] + } else { + deps += [ "//third_party/icu:extra_icudata" ] + data += [ "$root_out_dir/icudtl_extra.dat" ] + } + } + if (is_ios) { # ios does not use test_launcher to run gtests. sources -= [ @@ -2985,7 +3012,10 @@ test("base_unittests") { } if (is_desktop_linux) { - sources += [ "nix/xdg_util_unittest.cc" ] + sources += [ + "linux_util_unittest.cc", + "nix/xdg_util_unittest.cc", + ] } if (!use_glib) { @@ -3143,6 +3173,7 @@ if (is_android) { "android/java/src/org/chromium/base/UnguessableToken.java", "android/java/src/org/chromium/base/library_loader/LibraryLoader.java", "android/java/src/org/chromium/base/library_loader/LibraryPrefetcher.java", + "android/java/src/org/chromium/base/memory/JavaHeapDumpGenerator.java", "android/java/src/org/chromium/base/metrics/RecordHistogram.java", "android/java/src/org/chromium/base/metrics/RecordUserAction.java", "android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java", @@ -3182,9 +3213,9 @@ if (is_android) { deps = [ ":jni_java", "//third_party/android_deps:android_support_v4_java", + "//third_party/android_deps:androidx_annotation_annotation_java", "//third_party/android_deps:com_android_support_collections_java", "//third_party/android_deps:com_android_support_multidex_java", - "//third_party/android_deps:com_android_support_support_annotations_java", "//third_party/jsr-305:jsr_305_javalib", ] @@ -3223,6 +3254,7 @@ if (is_android) { "android/java/src/org/chromium/base/ObservableSupplier.java", "android/java/src/org/chromium/base/ObservableSupplierImpl.java", "android/java/src/org/chromium/base/PackageUtils.java", + "android/java/src/org/chromium/base/PackageManagerUtils.java", "android/java/src/org/chromium/base/PathService.java", "android/java/src/org/chromium/base/PathUtils.java", "android/java/src/org/chromium/base/PiiElider.java", @@ -3253,6 +3285,15 @@ if (is_android) { "android/java/src/org/chromium/base/annotations/NativeClassQualifiedName.java", "android/java/src/org/chromium/base/annotations/RemovableInRelease.java", "android/java/src/org/chromium/base/annotations/UsedByReflection.java", + "android/java/src/org/chromium/base/annotations/VerifiesOnLollipop.java", + "android/java/src/org/chromium/base/annotations/VerifiesOnLollipopMR1.java", + "android/java/src/org/chromium/base/annotations/VerifiesOnM.java", + "android/java/src/org/chromium/base/annotations/VerifiesOnN.java", + "android/java/src/org/chromium/base/annotations/VerifiesOnNMR1.java", + "android/java/src/org/chromium/base/annotations/VerifiesOnO.java", + "android/java/src/org/chromium/base/annotations/VerifiesOnOMR1.java", + "android/java/src/org/chromium/base/annotations/VerifiesOnP.java", + "android/java/src/org/chromium/base/annotations/VerifiesOnQ.java", "android/java/src/org/chromium/base/compat/ApiHelperForM.java", "android/java/src/org/chromium/base/compat/ApiHelperForN.java", "android/java/src/org/chromium/base/compat/ApiHelperForO.java", @@ -3282,6 +3323,7 @@ if (is_android) { "android/java/src/org/chromium/base/process_launcher/ChildProcessService.java", "android/java/src/org/chromium/base/process_launcher/ChildProcessServiceDelegate.java", "android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.java", + "android/java/src/org/chromium/base/memory/JavaHeapDumpGenerator.java", "android/java/src/org/chromium/base/memory/MemoryPressureMonitor.java", "android/java/src/org/chromium/base/memory/MemoryPressureCallback.java", "android/java/src/org/chromium/base/memory/MemoryPressureUma.java", @@ -3363,7 +3405,7 @@ if (is_android) { ":base_java", ":jni_java", "//testing/android/reporter:reporter_java", - "//third_party/android_deps:com_android_support_support_annotations_java", + "//third_party/android_deps:androidx_annotation_annotation_java", "//third_party/android_deps:com_android_support_support_compat_java", "//third_party/android_sdk:android_support_chromium_java", "//third_party/android_sdk:android_test_base_java", diff --git a/chromium/base/allocator/allocator.gni b/chromium/base/allocator/allocator.gni index 9c82dd29ba9..62e03b364d8 100644 --- a/chromium/base/allocator/allocator.gni +++ b/chromium/base/allocator/allocator.gni @@ -2,11 +2,13 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/chromecast_build.gni") import("//build/config/sanitizers/sanitizers.gni") # Temporarily disable tcmalloc on arm64 linux to get rid of compilation errors. if (is_android || is_mac || is_ios || is_asan || is_lsan || is_tsan || - is_msan || is_win || is_fuchsia || (is_linux && target_cpu == "arm64")) { + is_msan || is_win || is_fuchsia || (is_linux && target_cpu == "arm64") || + (is_cast_audio_only && target_cpu == "arm")) { _default_allocator = "none" } else { _default_allocator = "tcmalloc" diff --git a/chromium/base/allocator/debugallocation_shim.cc b/chromium/base/allocator/debugallocation_shim.cc index 479cfcad72d..24addf9d099 100644 --- a/chromium/base/allocator/debugallocation_shim.cc +++ b/chromium/base/allocator/debugallocation_shim.cc @@ -7,9 +7,10 @@ // AFDO can mess with them. Better not to use AFDO there. This is a // temporary hack. We will add a mechanism in the build system to // avoid using -fauto-profile for tcmalloc files. -#if !defined(__clang__) && (defined(OS_CHROMEOS) || __GNUC__ > 5) +#if !defined(__clang__) && \ + (defined(OS_CHROMEOS) || (__GNUC__ > 5 && __GNUC__ < 7)) // Note that this option only seems to be available in the chromeos GCC 4.9 -// toolchain, and stock GCC 5 and up. +// toolchain, and stock GCC 5 upto 7. #pragma GCC optimize ("no-auto-profile") #endif diff --git a/chromium/base/allocator/partition_allocator/partition_alloc.cc b/chromium/base/allocator/partition_allocator/partition_alloc.cc index 4b6d55fdf35..297a6bbe76e 100644 --- a/chromium/base/allocator/partition_allocator/partition_alloc.cc +++ b/chromium/base/allocator/partition_allocator/partition_alloc.cc @@ -493,14 +493,14 @@ static size_t PartitionPurgePage(internal::PartitionPage* page, bool discard) { size_t slot_index = (reinterpret_cast<char*>(entry) - ptr) / slot_size; DCHECK(slot_index < num_slots); slot_usage[slot_index] = 0; - entry = internal::PartitionFreelistEntry::Transform(entry->next); + entry = internal::EncodedPartitionFreelistEntry::Decode(entry->next); #if !defined(OS_WIN) // If we have a slot where the masked freelist entry is 0, we can actually // discard that freelist entry because touching a discarded page is // guaranteed to return original content or 0. (Note that this optimization // won't fire on big-endian machines because the masking function is // negation.) - if (!internal::PartitionFreelistEntry::Transform(entry)) + if (!internal::PartitionFreelistEntry::Encode(entry)) last_slot = slot_index; #endif } @@ -534,25 +534,33 @@ static size_t PartitionPurgePage(internal::PartitionPage* page, bool discard) { DCHECK(truncated_slots > 0); size_t num_new_entries = 0; page->num_unprovisioned_slots += static_cast<uint16_t>(truncated_slots); + // Rewrite the freelist. - internal::PartitionFreelistEntry** entry_ptr = &page->freelist_head; + internal::PartitionFreelistEntry* head = nullptr; + internal::PartitionFreelistEntry* back = head; for (size_t slot_index = 0; slot_index < num_slots; ++slot_index) { if (slot_usage[slot_index]) continue; + auto* entry = reinterpret_cast<internal::PartitionFreelistEntry*>( ptr + (slot_size * slot_index)); - *entry_ptr = internal::PartitionFreelistEntry::Transform(entry); - entry_ptr = reinterpret_cast<internal::PartitionFreelistEntry**>(entry); + if (!head) { + head = entry; + back = entry; + } else { + back->next = internal::PartitionFreelistEntry::Encode(entry); + back = entry; + } num_new_entries++; #if !defined(OS_WIN) last_slot = slot_index; #endif } - // Terminate the freelist chain. - *entry_ptr = nullptr; - // The freelist head is stored unmasked. - page->freelist_head = - internal::PartitionFreelistEntry::Transform(page->freelist_head); + + page->freelist_head = head; + if (back) + back->next = internal::PartitionFreelistEntry::Encode(nullptr); + DCHECK(num_new_entries == num_slots - page->num_allocated_slots); // Discard the memory. DiscardSystemPages(begin_ptr, unprovisioned_bytes); diff --git a/chromium/base/allocator/partition_allocator/partition_alloc_perftest.cc b/chromium/base/allocator/partition_allocator/partition_alloc_perftest.cc index 85b782a5eef..bdd85f0ae1d 100644 --- a/chromium/base/allocator/partition_allocator/partition_alloc_perftest.cc +++ b/chromium/base/allocator/partition_allocator/partition_alloc_perftest.cc @@ -2,13 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <atomic> #include <vector> + #include "base/allocator/partition_allocator/partition_alloc.h" +#include "base/threading/platform_thread.h" #include "base/time/time.h" #include "base/timer/lap_timer.h" - #include "build/build_config.h" - #include "testing/gtest/include/gtest/gtest.h" #include "testing/perf/perf_test.h" @@ -28,19 +29,47 @@ constexpr int kMultiBucketIncrement = 13; // Final size is 24 + (13 * 22) = 310 bytes. constexpr int kMultiBucketRounds = 22; -class MemoryAllocationPerfTest : public testing::Test { +class AllocatingThread : public PlatformThread::Delegate { public: - MemoryAllocationPerfTest() - : timer_(kWarmupRuns, kTimeLimit, kTimeCheckInterval) {} - void SetUp() override { alloc_.init(); } - void TearDown() override { - alloc_.root()->PurgeMemory(PartitionPurgeDecommitEmptyPages | - PartitionPurgeDiscardUnusedSystemPages); + explicit AllocatingThread(PartitionAllocatorGeneric* allocator) + : allocator_(allocator), should_stop_(false) { + PlatformThread::Create(0, this, &thread_handle_); } - LapTimer timer_; - PartitionAllocatorGeneric alloc_; + + ~AllocatingThread() override { + should_stop_ = true; + PlatformThread::Join(thread_handle_); + } + + // Allocates and frees memory in a loop until |should_stop_| becomes true. + void ThreadMain() override { + uint64_t count = 0; + while (true) { + // Only check |should_stop_| every 2^15 iterations, as it is a + // sequentially consistent access, hence expensive. + if (count % (1 << 15) == 0 && should_stop_) + break; + void* data = allocator_->root()->Alloc(10, ""); + allocator_->root()->Free(data); + count++; + } + } + + PartitionAllocatorGeneric* allocator_; + std::atomic<bool> should_stop_; + PlatformThreadHandle thread_handle_; }; +void DisplayResults(const std::string& measurement, + const std::string& modifier, + size_t iterations_per_second) { + perf_test::PrintResult(measurement, modifier, "", iterations_per_second, + "runs/s", true); + perf_test::PrintResult(measurement, modifier, "", + static_cast<size_t>(1e9 / iterations_per_second), + "ns/run", true); +} + class MemoryAllocationPerfNode { public: MemoryAllocationPerfNode* GetNext() const { return next_; } @@ -59,110 +88,169 @@ class MemoryAllocationPerfNode { MemoryAllocationPerfNode* next_ = nullptr; }; -TEST_F(MemoryAllocationPerfTest, SingleBucket) { - timer_.Reset(); - MemoryAllocationPerfNode* first = reinterpret_cast<MemoryAllocationPerfNode*>( - alloc_.root()->Alloc(40, "<testing>")); - MemoryAllocationPerfNode* cur = first; - do { - MemoryAllocationPerfNode* next = +class MemoryAllocationPerfTest : public testing::Test { + public: + MemoryAllocationPerfTest() + : timer_(kWarmupRuns, kTimeLimit, kTimeCheckInterval) {} + void SetUp() override { alloc_.init(); } + void TearDown() override { + alloc_.root()->PurgeMemory(PartitionPurgeDecommitEmptyPages | + PartitionPurgeDiscardUnusedSystemPages); + } + + protected: + void TestSingleBucket() { + MemoryAllocationPerfNode* first = reinterpret_cast<MemoryAllocationPerfNode*>( alloc_.root()->Alloc(40, "<testing>")); - CHECK_NE(next, nullptr); - cur->SetNext(next); - cur = next; - timer_.NextLap(); - } while (!timer_.HasTimeLimitExpired()); - // next_ = nullptr only works if the class constructor is called (it's not - // called in this case because then we can allocate arbitrary-length - // payloads.) - cur->SetNext(nullptr); - - MemoryAllocationPerfNode::FreeAll(first, alloc_); - - perf_test::PrintResult("MemoryAllocationPerfTest", - " single bucket allocation (40 bytes)", "", - timer_.LapsPerSecond(), "runs/s", true); -} - -TEST_F(MemoryAllocationPerfTest, SingleBucketWithFree) { - timer_.Reset(); - // Allocate an initial element to make sure the bucket stays set up. - void* elem = alloc_.root()->Alloc(40, "<testing>"); - do { - void* cur = alloc_.root()->Alloc(40, "<testing>"); - CHECK_NE(cur, nullptr); - alloc_.root()->Free(cur); - timer_.NextLap(); - } while (!timer_.HasTimeLimitExpired()); - - alloc_.root()->Free(elem); - perf_test::PrintResult("MemoryAllocationPerfTest", - " single bucket allocation + free (40 bytes)", "", - timer_.LapsPerSecond(), "runs/s", true); -} -// Failing on Nexus5x: crbug.com/949838 -#if defined(OS_ANDROID) -#define MAYBE_MultiBucket DISABLED_MultiBucket -#else -#define MAYBE_MultiBucket MultiBucket -#endif -TEST_F(MemoryAllocationPerfTest, MAYBE_MultiBucket) { - timer_.Reset(); - MemoryAllocationPerfNode* first = reinterpret_cast<MemoryAllocationPerfNode*>( - alloc_.root()->Alloc(40, "<testing>")); - MemoryAllocationPerfNode* cur = first; - do { - for (int i = 0; i < kMultiBucketRounds; i++) { + timer_.Reset(); + MemoryAllocationPerfNode* cur = first; + do { MemoryAllocationPerfNode* next = - reinterpret_cast<MemoryAllocationPerfNode*>(alloc_.root()->Alloc( - kMultiBucketMinimumSize + (i * kMultiBucketIncrement), - "<testing>")); + reinterpret_cast<MemoryAllocationPerfNode*>( + alloc_.root()->Alloc(40, "<testing>")); CHECK_NE(next, nullptr); cur->SetNext(next); cur = next; - } - timer_.NextLap(); - } while (!timer_.HasTimeLimitExpired()); - cur->SetNext(nullptr); + timer_.NextLap(); + } while (!timer_.HasTimeLimitExpired()); + // next_ = nullptr only works if the class constructor is called (it's not + // called in this case because then we can allocate arbitrary-length + // payloads.) + cur->SetNext(nullptr); - MemoryAllocationPerfNode::FreeAll(first, alloc_); + MemoryAllocationPerfNode::FreeAll(first, alloc_); - perf_test::PrintResult("MemoryAllocationPerfTest", " multi-bucket allocation", - "", timer_.LapsPerSecond() * kMultiBucketRounds, - "runs/s", true); -} + DisplayResults("MemoryAllocationPerfTest", + " single bucket allocation (40 bytes)", + timer_.LapsPerSecond()); + } -TEST_F(MemoryAllocationPerfTest, MultiBucketWithFree) { - timer_.Reset(); - std::vector<void*> elems; - // Do an initial round of allocation to make sure that the buckets stay in use - // (and aren't accidentally released back to the OS). - for (int i = 0; i < kMultiBucketRounds; i++) { - void* cur = alloc_.root()->Alloc( - kMultiBucketMinimumSize + (i * kMultiBucketIncrement), "<testing>"); - CHECK_NE(cur, nullptr); - elems.push_back(cur); + void TestSingleBucketWithFree() { + // Allocate an initial element to make sure the bucket stays set up. + void* elem = alloc_.root()->Alloc(40, "<testing>"); + + timer_.Reset(); + do { + void* cur = alloc_.root()->Alloc(40, "<testing>"); + CHECK_NE(cur, nullptr); + alloc_.root()->Free(cur); + timer_.NextLap(); + } while (!timer_.HasTimeLimitExpired()); + + alloc_.root()->Free(elem); + DisplayResults("MemoryAllocationPerfTest", + " single bucket allocation + free (40 bytes)", + timer_.LapsPerSecond()); + } + + void TestMultiBucket() { + MemoryAllocationPerfNode* first = + reinterpret_cast<MemoryAllocationPerfNode*>( + alloc_.root()->Alloc(40, "<testing>")); + MemoryAllocationPerfNode* cur = first; + + timer_.Reset(); + do { + for (int i = 0; i < kMultiBucketRounds; i++) { + MemoryAllocationPerfNode* next = + reinterpret_cast<MemoryAllocationPerfNode*>(alloc_.root()->Alloc( + kMultiBucketMinimumSize + (i * kMultiBucketIncrement), + "<testing>")); + CHECK_NE(next, nullptr); + cur->SetNext(next); + cur = next; + } + timer_.NextLap(); + } while (!timer_.HasTimeLimitExpired()); + cur->SetNext(nullptr); + + MemoryAllocationPerfNode::FreeAll(first, alloc_); + + DisplayResults("MemoryAllocationPerfTest", " multi-bucket allocation", + timer_.LapsPerSecond() * kMultiBucketRounds); } - do { + void TestMultiBucketWithFree() { + std::vector<void*> elems; + elems.reserve(kMultiBucketRounds); + // Do an initial round of allocation to make sure that the buckets stay in + // use (and aren't accidentally released back to the OS). for (int i = 0; i < kMultiBucketRounds; i++) { void* cur = alloc_.root()->Alloc( kMultiBucketMinimumSize + (i * kMultiBucketIncrement), "<testing>"); CHECK_NE(cur, nullptr); - alloc_.root()->Free(cur); + elems.push_back(cur); + } + + timer_.Reset(); + do { + for (int i = 0; i < kMultiBucketRounds; i++) { + void* cur = alloc_.root()->Alloc( + kMultiBucketMinimumSize + (i * kMultiBucketIncrement), "<testing>"); + CHECK_NE(cur, nullptr); + alloc_.root()->Free(cur); + } + timer_.NextLap(); + } while (!timer_.HasTimeLimitExpired()); + + for (void* ptr : elems) { + alloc_.root()->Free(ptr); } - timer_.NextLap(); - } while (!timer_.HasTimeLimitExpired()); - for (void* ptr : elems) { - alloc_.root()->Free(ptr); + DisplayResults("MemoryAllocationPerfTest", + " multi-bucket allocation + free", + timer_.LapsPerSecond() * kMultiBucketRounds); } - perf_test::PrintResult( - "MemoryAllocationPerfTest", " multi-bucket allocation + free", "", - timer_.LapsPerSecond() * kMultiBucketRounds, "runs/s", true); + LapTimer timer_; + PartitionAllocatorGeneric alloc_; +}; + +TEST_F(MemoryAllocationPerfTest, SingleBucket) { + TestSingleBucket(); +} + +TEST_F(MemoryAllocationPerfTest, SingleBucketWithCompetingThread) { + AllocatingThread t(&alloc_); + TestSingleBucket(); +} + +TEST_F(MemoryAllocationPerfTest, SingleBucketWithFree) { + TestSingleBucketWithFree(); +} + +TEST_F(MemoryAllocationPerfTest, SingleBucketWithFreeWithCompetingThread) { + AllocatingThread t(&alloc_); + TestSingleBucketWithFree(); +} + +// Failing on Nexus5x: crbug.com/949838 +#if defined(OS_ANDROID) +#define MAYBE_MultiBucket DISABLED_MultiBucket +#define MAYBE_MultiBucketWithCompetingThread \ + DISABLED_MultiBucketWithCompetingThread +#else +#define MAYBE_MultiBucket MultiBucket +#define MAYBE_MultiBucketWithCompetingThread MultiBucketWithCompetingThread +#endif +TEST_F(MemoryAllocationPerfTest, MAYBE_MultiBucket) { + TestMultiBucket(); +} + +TEST_F(MemoryAllocationPerfTest, MAYBE_MultiBucketWithCompetingThread) { + AllocatingThread t(&alloc_); + TestMultiBucket(); +} + +TEST_F(MemoryAllocationPerfTest, MultiBucketWithFree) { + TestMultiBucketWithFree(); +} + +TEST_F(MemoryAllocationPerfTest, MultiBucketWithFreeWithCompetingThread) { + AllocatingThread t(&alloc_); + TestMultiBucketWithFree(); } } // anonymous namespace diff --git a/chromium/base/allocator/partition_allocator/partition_bucket.cc b/chromium/base/allocator/partition_allocator/partition_bucket.cc index 8e54f552b22..2e239a80fa7 100644 --- a/chromium/base/allocator/partition_allocator/partition_bucket.cc +++ b/chromium/base/allocator/partition_allocator/partition_bucket.cc @@ -79,7 +79,7 @@ ALWAYS_INLINE PartitionPage* PartitionDirectMap(PartitionRootBase* root, page->freelist_head = reinterpret_cast<PartitionFreelistEntry*>(slot); PartitionFreelistEntry* next_entry = reinterpret_cast<PartitionFreelistEntry*>(slot); - next_entry->next = PartitionFreelistEntry::Transform(nullptr); + next_entry->next = PartitionFreelistEntry::Encode(nullptr); DCHECK(!bucket->active_pages_head); DCHECK(!bucket->empty_pages_head); @@ -394,10 +394,10 @@ ALWAYS_INLINE char* PartitionBucket::AllocAndFillFreelist(PartitionPage* page) { freelist_pointer += size; PartitionFreelistEntry* next_entry = reinterpret_cast<PartitionFreelistEntry*>(freelist_pointer); - entry->next = PartitionFreelistEntry::Transform(next_entry); + entry->next = PartitionFreelistEntry::Encode(next_entry); entry = next_entry; } - entry->next = PartitionFreelistEntry::Transform(nullptr); + entry->next = PartitionFreelistEntry::Encode(nullptr); } else { page->freelist_head = nullptr; } @@ -555,7 +555,7 @@ void* PartitionBucket::SlowPathAlloc(PartitionRootBase* root, if (LIKELY(new_page->freelist_head != nullptr)) { PartitionFreelistEntry* entry = new_page->freelist_head; PartitionFreelistEntry* new_head = - PartitionFreelistEntry::Transform(entry->next); + EncodedPartitionFreelistEntry::Decode(entry->next); new_page->freelist_head = new_head; new_page->num_allocated_slots++; return entry; diff --git a/chromium/base/allocator/partition_allocator/partition_freelist_entry.h b/chromium/base/allocator/partition_allocator/partition_freelist_entry.h index 7e3282ef412..ce3763b88d7 100644 --- a/chromium/base/allocator/partition_allocator/partition_freelist_entry.h +++ b/chromium/base/allocator/partition_allocator/partition_freelist_entry.h @@ -15,33 +15,56 @@ namespace base { namespace internal { -// TODO(ajwong): Introduce an EncodedFreelistEntry type and then replace -// Transform() with Encode()/Decode() such that the API provides some static -// type safety. -// -// https://crbug.com/787153 +struct EncodedPartitionFreelistEntry; + struct PartitionFreelistEntry { - PartitionFreelistEntry* next; + EncodedPartitionFreelistEntry* next; + + PartitionFreelistEntry() = delete; + ~PartitionFreelistEntry() = delete; - static ALWAYS_INLINE PartitionFreelistEntry* Transform( + ALWAYS_INLINE static EncodedPartitionFreelistEntry* Encode( PartitionFreelistEntry* ptr) { -// We use bswap on little endian as a fast mask for two reasons: -// 1) If an object is freed and its vtable used where the attacker doesn't -// get the chance to run allocations between the free and use, the vtable -// dereference is likely to fault. -// 2) If the attacker has a linear buffer overflow and elects to try and -// corrupt a freelist pointer, partial pointer overwrite attacks are -// thwarted. -// For big endian, similar guarantees are arrived at with a negation. + return reinterpret_cast<EncodedPartitionFreelistEntry*>(Transform(ptr)); + } + + private: + friend struct EncodedPartitionFreelistEntry; + static ALWAYS_INLINE void* Transform(void* ptr) { + // We use bswap on little endian as a fast mask for two reasons: + // 1) If an object is freed and its vtable used where the attacker doesn't + // get the chance to run allocations between the free and use, the vtable + // dereference is likely to fault. + // 2) If the attacker has a linear buffer overflow and elects to try and + // corrupt a freelist pointer, partial pointer overwrite attacks are + // thwarted. + // For big endian, similar guarantees are arrived at with a negation. #if defined(ARCH_CPU_BIG_ENDIAN) uintptr_t masked = ~reinterpret_cast<uintptr_t>(ptr); #else uintptr_t masked = ByteSwapUintPtrT(reinterpret_cast<uintptr_t>(ptr)); #endif - return reinterpret_cast<PartitionFreelistEntry*>(masked); + return reinterpret_cast<void*>(masked); + } +}; + +struct EncodedPartitionFreelistEntry { + char scrambled[sizeof(PartitionFreelistEntry*)]; + + EncodedPartitionFreelistEntry() = delete; + ~EncodedPartitionFreelistEntry() = delete; + + ALWAYS_INLINE static PartitionFreelistEntry* Decode( + EncodedPartitionFreelistEntry* ptr) { + return reinterpret_cast<PartitionFreelistEntry*>( + PartitionFreelistEntry::Transform(ptr)); } }; +static_assert(sizeof(PartitionFreelistEntry) == + sizeof(EncodedPartitionFreelistEntry), + "Should not have padding"); + } // namespace internal } // namespace base diff --git a/chromium/base/allocator/partition_allocator/partition_page.h b/chromium/base/allocator/partition_allocator/partition_page.h index 5a0e70f9711..d2e580bdda8 100644 --- a/chromium/base/allocator/partition_allocator/partition_page.h +++ b/chromium/base/allocator/partition_allocator/partition_page.h @@ -11,8 +11,27 @@ #include "base/allocator/partition_allocator/partition_bucket.h" #include "base/allocator/partition_allocator/partition_cookie.h" #include "base/allocator/partition_allocator/partition_freelist_entry.h" +#include "base/allocator/partition_allocator/random.h" #include "base/logging.h" +namespace { + +// Returns true if we've hit the end of a random-length period. We don't want to +// invoke `RandomValue` too often, because we call this function in a hot spot +// (`Free`), and `RandomValue` incurs the cost of atomics. +#if !DCHECK_IS_ON() +bool RandomPeriod() { + static thread_local uint8_t counter = 0; + if (UNLIKELY(counter == 0)) { + counter = base::RandomValue(); + } + counter--; + return counter == 0; +} +#endif + +} // namespace + namespace base { namespace internal { @@ -201,29 +220,35 @@ ALWAYS_INLINE size_t PartitionPage::get_raw_size() const { } ALWAYS_INLINE void PartitionPage::Free(void* ptr) { -#if DCHECK_IS_ON() size_t slot_size = this->bucket->slot_size; const size_t raw_size = get_raw_size(); if (raw_size) { slot_size = raw_size; } +#if DCHECK_IS_ON() // If these asserts fire, you probably corrupted memory. PartitionCookieCheckValue(ptr); PartitionCookieCheckValue(reinterpret_cast<char*>(ptr) + slot_size - kCookieSize); memset(ptr, kFreedByte, slot_size); +#else + // `memset` only once in a while. + if (UNLIKELY(RandomPeriod())) { + memset(ptr, kFreedByte, slot_size); + } #endif DCHECK(this->num_allocated_slots); - CHECK(ptr != freelist_head); // Catches an immediate double free. + // Catches an immediate double free. + CHECK(ptr != freelist_head); // Look for double free one level deeper in debug. - DCHECK(!freelist_head || ptr != internal::PartitionFreelistEntry::Transform( - freelist_head->next)); + DCHECK(!freelist_head || + ptr != EncodedPartitionFreelistEntry::Decode(freelist_head->next)); internal::PartitionFreelistEntry* entry = static_cast<internal::PartitionFreelistEntry*>(ptr); - entry->next = internal::PartitionFreelistEntry::Transform(freelist_head); + entry->next = internal::PartitionFreelistEntry::Encode(freelist_head); freelist_head = entry; --this->num_allocated_slots; if (UNLIKELY(this->num_allocated_slots <= 0)) { diff --git a/chromium/base/allocator/partition_allocator/partition_root_base.h b/chromium/base/allocator/partition_allocator/partition_root_base.h index 9e971f9f712..a3f9175b3cb 100644 --- a/chromium/base/allocator/partition_allocator/partition_root_base.h +++ b/chromium/base/allocator/partition_allocator/partition_root_base.h @@ -107,8 +107,8 @@ ALWAYS_INLINE void* PartitionRootBase::AllocFromBucket(PartitionBucket* bucket, // the size metadata. DCHECK(page->get_raw_size() == 0); internal::PartitionFreelistEntry* new_head = - internal::PartitionFreelistEntry::Transform( - static_cast<internal::PartitionFreelistEntry*>(ret)->next); + internal::EncodedPartitionFreelistEntry::Decode( + page->freelist_head->next); page->freelist_head = new_head; page->num_allocated_slots++; } else { diff --git a/chromium/base/allocator/partition_allocator/random.h b/chromium/base/allocator/partition_allocator/random.h index 85cb66d21de..a9aaa7f6ccb 100644 --- a/chromium/base/allocator/partition_allocator/random.h +++ b/chromium/base/allocator/partition_allocator/random.h @@ -15,7 +15,7 @@ namespace base { // `base::RandUint64` which is very unpredictable, but which is expensive due to // the need to call into the kernel. Therefore this generator uses a fast, // entirely user-space function after initialization. -uint32_t RandomValue(); +BASE_EXPORT uint32_t RandomValue(); // Sets the seed for the random number generator to a known value, to cause the // RNG to generate a predictable sequence of outputs. May be called multiple diff --git a/chromium/base/android/jni_generator/BUILD.gn b/chromium/base/android/jni_generator/BUILD.gn index dc671366bab..83d4b3ea3e5 100644 --- a/chromium/base/android/jni_generator/BUILD.gn +++ b/chromium/base/android/jni_generator/BUILD.gn @@ -111,5 +111,4 @@ java_annotation_processor("jni_processor") { ] srcjar_deps = [ ":processor_args_java" ] - jacoco_never_instrument = true } diff --git a/chromium/base/auto_reset.h b/chromium/base/auto_reset.h index 0ab0af7bb21..d8ce87b600d 100644 --- a/chromium/base/auto_reset.h +++ b/chromium/base/auto_reset.h @@ -7,8 +7,6 @@ #include <utility> -#include "base/macros.h" - // base::AutoReset<> is useful for setting a variable to a new value only within // a particular scope. An base::AutoReset<> object resets a variable to its // original value upon destruction, making it an alternative to writing @@ -20,19 +18,23 @@ namespace base { -template<typename T> +template <typename T> class AutoReset { public: - AutoReset(T* scoped_variable, T new_value) + template <typename U> + AutoReset(T* scoped_variable, U&& new_value) : scoped_variable_(scoped_variable), - original_value_(std::move(*scoped_variable)) { - *scoped_variable_ = std::move(new_value); - } + original_value_( + std::exchange(*scoped_variable_, std::forward<U>(new_value))) {} AutoReset(AutoReset&& other) - : scoped_variable_(other.scoped_variable_), - original_value_(std::move(other.original_value_)) { - other.scoped_variable_ = nullptr; + : scoped_variable_(std::exchange(other.scoped_variable_, nullptr)), + original_value_(std::move(other.original_value_)) {} + + AutoReset& operator=(AutoReset&& rhs) { + scoped_variable_ = std::exchange(rhs.scoped_variable_, nullptr); + original_value_ = std::move(rhs.original_value_); + return *this; } ~AutoReset() { @@ -40,20 +42,9 @@ class AutoReset { *scoped_variable_ = std::move(original_value_); } - AutoReset& operator=(AutoReset&& rhs) { - if (this != &rhs) { - scoped_variable_ = rhs.scoped_variable_; - rhs.scoped_variable_ = nullptr; - original_value_ = std::move(rhs.original_value_); - } - return *this; - } - private: T* scoped_variable_; T original_value_; - - DISALLOW_COPY_AND_ASSIGN(AutoReset); }; } // namespace base diff --git a/chromium/base/auto_reset_unittests.cc b/chromium/base/auto_reset_unittest.cc index d42c6a08b07..d42c6a08b07 100644 --- a/chromium/base/auto_reset_unittests.cc +++ b/chromium/base/auto_reset_unittest.cc diff --git a/chromium/base/big_endian.cc b/chromium/base/big_endian.cc index 514581fe415..9e9e672e490 100644 --- a/chromium/base/big_endian.cc +++ b/chromium/base/big_endian.cc @@ -4,6 +4,7 @@ #include "base/big_endian.h" +#include "base/numerics/checked_math.h" #include "base/strings/string_piece.h" namespace base { @@ -59,6 +60,29 @@ bool BigEndianReader::ReadU64(uint64_t* value) { return Read(value); } +template <typename T> +bool BigEndianReader::ReadLengthPrefixed(base::StringPiece* out) { + T t_len; + if (!Read(&t_len)) + return false; + size_t len = strict_cast<size_t>(t_len); + const char* original_ptr = ptr_; + if (!Skip(len)) { + ptr_ -= sizeof(T); + return false; + } + *out = base::StringPiece(original_ptr, len); + return true; +} + +bool BigEndianReader::ReadU8LengthPrefixed(base::StringPiece* out) { + return ReadLengthPrefixed<uint8_t>(out); +} + +bool BigEndianReader::ReadU16LengthPrefixed(base::StringPiece* out) { + return ReadLengthPrefixed<uint16_t>(out); +} + BigEndianWriter::BigEndianWriter(char* buf, size_t len) : ptr_(buf), end_(ptr_ + len) {} diff --git a/chromium/base/big_endian.h b/chromium/base/big_endian.h index 8fea64f248d..96650a6955f 100644 --- a/chromium/base/big_endian.h +++ b/chromium/base/big_endian.h @@ -67,10 +67,26 @@ class BASE_EXPORT BigEndianReader { bool ReadU32(uint32_t* value); bool ReadU64(uint64_t* value); + // Reads a length-prefixed region: + // 1. reads a big-endian length L from the buffer; + // 2. sets |*out| to a StringPiece over the next L many bytes + // of the buffer (beyond the end of the bytes encoding the length); and + // 3. skips the main reader past this L-byte substring. + // + // Fails if reading a U8 or U16 fails, or if the parsed length is greater + // than the number of bytes remaining in the stream. + // + // On failure, leaves the stream at the same position + // as before the call. + bool ReadU8LengthPrefixed(base::StringPiece* out); + bool ReadU16LengthPrefixed(base::StringPiece* out); + private: // Hidden to promote type safety. template<typename T> bool Read(T* v); + template <typename T> + bool ReadLengthPrefixed(base::StringPiece* out); const char* ptr_; const char* end_; diff --git a/chromium/base/big_endian_unittest.cc b/chromium/base/big_endian_unittest.cc index 7f8d9a556d8..07c208a3a1c 100644 --- a/chromium/base/big_endian_unittest.cc +++ b/chromium/base/big_endian_unittest.cc @@ -42,6 +42,64 @@ TEST(BigEndianReaderTest, ReadsValues) { EXPECT_EQ(expected.data(), piece.data()); } +TEST(BigEndianReaderTest, ReadsLengthPrefixedValues) { + { + char u8_prefixed_data[] = {8, 8, 9, 0xA, 0xB, 0xC, 0xD, + 0xE, 0xF, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E}; + BigEndianReader reader(u8_prefixed_data, sizeof(u8_prefixed_data)); + + base::StringPiece piece; + ASSERT_TRUE(reader.ReadU8LengthPrefixed(&piece)); + // |reader| should skip both a u8 and the length-8 length-prefixed field. + EXPECT_EQ(reader.ptr(), u8_prefixed_data + 9); + EXPECT_EQ(piece.size(), 8u); + EXPECT_EQ(piece.data(), u8_prefixed_data + 1); + } + + { + char u16_prefixed_data[] = {0, 8, 0xD, 0xE, 0xF, + 0x1A, 0x2B, 0x3C, 0x4D, 0x5E}; + BigEndianReader reader(u16_prefixed_data, sizeof(u16_prefixed_data)); + base::StringPiece piece; + ASSERT_TRUE(reader.ReadU16LengthPrefixed(&piece)); + // |reader| should skip both a u16 and the length-8 length-prefixed field. + EXPECT_EQ(reader.ptr(), u16_prefixed_data + 10); + EXPECT_EQ(piece.size(), 8u); + EXPECT_EQ(piece.data(), u16_prefixed_data + 2); + + // With no data left, we shouldn't be able to + // read another u8 length prefix (or a u16 length prefix, + // for that matter). + EXPECT_FALSE(reader.ReadU8LengthPrefixed(&piece)); + EXPECT_FALSE(reader.ReadU16LengthPrefixed(&piece)); + } + + { + // Make sure there's no issue reading a zero-value length prefix. + char u16_prefixed_data[3] = {}; + BigEndianReader reader(u16_prefixed_data, sizeof(u16_prefixed_data)); + base::StringPiece piece; + ASSERT_TRUE(reader.ReadU16LengthPrefixed(&piece)); + EXPECT_EQ(reader.ptr(), u16_prefixed_data + 2); + EXPECT_EQ(piece.data(), u16_prefixed_data + 2); + EXPECT_EQ(piece.size(), 0u); + } +} + +TEST(BigEndianReaderTest, LengthPrefixedReadsFailGracefully) { + // We can't read 0xF (or, for that matter, 0xF8) bytes after the length + // prefix: there isn't enough data. + char data[] = {0xF, 8, 9, 0xA, 0xB, 0xC, 0xD, + 0xE, 0xF, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E}; + BigEndianReader reader(data, sizeof(data)); + base::StringPiece piece; + EXPECT_FALSE(reader.ReadU8LengthPrefixed(&piece)); + EXPECT_EQ(data, reader.ptr()); + + EXPECT_FALSE(reader.ReadU16LengthPrefixed(&piece)); + EXPECT_EQ(data, reader.ptr()); +} + TEST(BigEndianReaderTest, RespectsLength) { char data[8]; char buf[2]; diff --git a/chromium/base/bind_internal.h b/chromium/base/bind_internal.h index b2f1c3c4c9c..02c06b7b584 100644 --- a/chromium/base/bind_internal.h +++ b/chromium/base/bind_internal.h @@ -354,10 +354,9 @@ template <typename Functor, typename SFINAE> struct FunctorTraits; // For empty callable types. -// This specialization is intended to allow binding captureless lambdas by -// base::Bind(), based on the fact that captureless lambdas are empty while -// capturing lambdas are not. This also allows any functors as far as it's an -// empty class. +// This specialization is intended to allow binding captureless lambdas, based +// on the fact that captureless lambdas are empty while capturing lambdas are +// not. This also allows any functors as far as it's an empty class. // Example: // // // Captureless lambdas are allowed. @@ -791,10 +790,10 @@ BanUnconstructedRefCountedReceiver(const Receiver& receiver, Unused&&...) { // // scoped_refptr<Foo> oo = Foo::Create(); DCHECK(receiver->HasAtLeastOneRef()) - << "base::Bind() refuses to create the first reference to ref-counted " - "objects. That is typically happens around PostTask() in their " - "constructor, and such objects can be destroyed before `new` returns " - "if the task resolves fast enough."; + << "base::Bind{Once,Repeating}() refuses to create the first reference " + "to ref-counted objects. That typically happens around PostTask() in " + "their constructor, and such objects can be destroyed before `new` " + "returns if the task resolves fast enough."; } // BindState<> @@ -917,7 +916,7 @@ using MakeBindStateType = // }; // // WeakPtr<Foo> oo = nullptr; -// base::Bind(&Foo::bar, oo).Run(); +// base::BindOnce(&Foo::bar, oo).Run(); template <typename T> struct IsWeakReceiver : std::false_type {}; diff --git a/chromium/base/callback.h b/chromium/base/callback.h index e08f9b872b7..1427faaaea9 100644 --- a/chromium/base/callback.h +++ b/chromium/base/callback.h @@ -140,7 +140,7 @@ class RepeatingCallback<R(Args...)> : public internal::CallbackBaseCopyable { RepeatingCallback cb = std::move(*this); PolymorphicInvoke f = reinterpret_cast<PolymorphicInvoke>(cb.polymorphic_invoke()); - return f(cb.bind_state_.get(), std::forward<Args>(args)...); + return f(std::move(cb).bind_state_.get(), std::forward<Args>(args)...); } }; diff --git a/chromium/base/callback_helpers_unittest.cc b/chromium/base/callback_helpers_unittest.cc index 00b2ef59ebe..78c7ad0ad5d 100644 --- a/chromium/base/callback_helpers_unittest.cc +++ b/chromium/base/callback_helpers_unittest.cc @@ -13,8 +13,8 @@ namespace { TEST(CallbackHelpersTest, IsBaseCallback) { - // Check that base::Closures and references to them are considered - // base::Callbacks. + // Check that base::{Once,Repeating}Closures and references to them are + // considered base::{Once,Repeating}Callbacks. static_assert(base::IsBaseCallback<base::OnceClosure>::value, ""); static_assert(base::IsBaseCallback<base::RepeatingClosure>::value, ""); static_assert(base::IsBaseCallback<base::OnceClosure&&>::value, ""); diff --git a/chromium/base/containers/README.md b/chromium/base/containers/README.md index 4adc38d36d4..7700c37945f 100644 --- a/chromium/base/containers/README.md +++ b/chromium/base/containers/README.md @@ -29,30 +29,35 @@ Google naming. Be sure to use the base namespace. ### Usage advice - * Generally avoid `std::unordered_set` and `std::unordered_map`. In the - common case, query performance is unlikely to be sufficiently higher than +* Generally avoid `std::unordered_set` and `std::unordered_map`. In the common + case, query performance is unlikely to be sufficiently higher than `std::map` to make a difference, insert performance is slightly worse, and the memory overhead is high. This makes sense mostly for large tables where you expect a lot of lookups. - * Most maps and sets in Chrome are small and contain objects that can be - moved efficiently. In this case, consider `base::flat_map` and - `base::flat_set`. You need to be aware of the maximum expected size of - the container since individual inserts and deletes are O(n), giving O(n^2) - construction time for the entire map. But because it avoids mallocs in most - cases, inserts are better or comparable to other containers even for - several dozen items, and efficiently-moved types are unlikely to have - performance problems for most cases until you have hundreds of items. If - your container can be constructed in one shot, the constructor from vector - gives O(n log n) construction times and it should be strictly better than - a `std::map`. - - * `base::small_map` has better runtime memory usage without the poor - mutation performance of large containers that `base::flat_map` has. But this - advantage is partially offset by additional code size. Prefer in cases - where you make many objects so that the code/heap tradeoff is good. - - * Use `std::map` and `std::set` if you can't decide. Even if they're not +* Most maps and sets in Chrome are small and contain objects that can be moved + efficiently. In this case, consider `base::flat_map` and `base::flat_set`. + You need to be aware of the maximum expected size of the container since + individual inserts and deletes are O(n), giving O(n^2) construction time for + the entire map. But because it avoids mallocs in most cases, inserts are + better or comparable to other containers even for several dozen items, and + efficiently-moved types are unlikely to have performance problems for most + cases until you have hundreds of items. If your container can be constructed + in one shot, the constructor from vector gives O(n log n) construction times + and it should be strictly better than a `std::map`. + + Conceptually inserting a range of n elements into a `base::flat_map` or + `base::flat_set` behaves as if insert() was called for each individually + element. Thus in case the input range contains repeated elements, only the + first one of these duplicates will be inserted into the container. This + behaviour applies to construction from a range as well. + +* `base::small_map` has better runtime memory usage without the poor mutation + performance of large containers that `base::flat_map` has. But this + advantage is partially offset by additional code size. Prefer in cases where + you make many objects so that the code/heap tradeoff is good. + +* Use `std::map` and `std::set` if you can't decide. Even if they're not great, they're unlikely to be bad or surprising. ### Map and set details @@ -155,7 +160,7 @@ std::generate_n(std::back_inserter(ptr_vec), 5, []{ }); // Construct a set. -UniquePtrSet<int> ptr_set(std::move(ptr_vec), base::KEEP_FIRST_OF_DUPES); +UniquePtrSet<int> ptr_set(std::move(ptr_vec)); // Use raw pointers to lookup keys. int* ptr = ptr_set.begin()->get(); @@ -165,8 +170,7 @@ EXPECT_TRUE(ptr_set.find(ptr) == ptr_set.begin()); Example `flat_map<std::string, int>`: ```cpp -base::flat_map<std::string, int> str_to_int({{"a", 1}, {"c", 2},{"b", 2}}, - base::KEEP_FIRST_OF_DUPES); +base::flat_map<std::string, int> str_to_int({{"a", 1}, {"c", 2},{"b", 2}}); // Does not construct temporary strings. str_to_int.find("c")->second = 3; diff --git a/chromium/base/containers/checked_iterators.h b/chromium/base/containers/checked_iterators.h index b7eb7b4c7a9..bf3d6949371 100644 --- a/chromium/base/containers/checked_iterators.h +++ b/chromium/base/containers/checked_iterators.h @@ -7,6 +7,7 @@ #include <iterator> #include <memory> +#include <type_traits> #include "base/containers/util.h" #include "base/logging.h" @@ -14,234 +15,106 @@ namespace base { template <typename T> -class CheckedRandomAccessConstIterator; - -template <typename T> -class CheckedRandomAccessIterator { +class CheckedContiguousIterator { public: using difference_type = std::ptrdiff_t; - using value_type = typename std::iterator_traits<T*>::value_type; + using value_type = std::remove_cv_t<T>; using pointer = T*; using reference = T&; using iterator_category = std::random_access_iterator_tag; - friend class CheckedRandomAccessConstIterator<T>; + // Required for converting constructor below. + template <typename U> + friend class CheckedContiguousIterator; - CheckedRandomAccessIterator() = default; - CheckedRandomAccessIterator(T* start, const T* end) - : CheckedRandomAccessIterator(start, start, end) {} - CheckedRandomAccessIterator(T* start, T* current, const T* end) + constexpr CheckedContiguousIterator() = default; + constexpr CheckedContiguousIterator(T* start, const T* end) + : CheckedContiguousIterator(start, start, end) {} + constexpr CheckedContiguousIterator(const T* start, T* current, const T* end) : start_(start), current_(current), end_(end) { - CHECK(start <= current); - CHECK(current <= end); + CHECK_LE(start, current); + CHECK_LE(current, end); } - CheckedRandomAccessIterator(const CheckedRandomAccessIterator& other) = + constexpr CheckedContiguousIterator(const CheckedContiguousIterator& other) = default; - ~CheckedRandomAccessIterator() = default; - - CheckedRandomAccessIterator& operator=( - const CheckedRandomAccessIterator& other) = default; - - bool operator==(const CheckedRandomAccessIterator& other) const { - CheckComparable(other); - return current_ == other.current_; - } - - bool operator!=(const CheckedRandomAccessIterator& other) const { - CheckComparable(other); - return current_ != other.current_; - } - - bool operator<(const CheckedRandomAccessIterator& other) const { - CheckComparable(other); - return current_ < other.current_; - } - - bool operator<=(const CheckedRandomAccessIterator& other) const { - CheckComparable(other); - return current_ <= other.current_; - } - - CheckedRandomAccessIterator& operator++() { - CHECK(current_ != end_); - ++current_; - return *this; - } - - CheckedRandomAccessIterator operator++(int) { - CheckedRandomAccessIterator old = *this; - ++*this; - return old; - } - - CheckedRandomAccessIterator& operator--() { - CHECK(current_ != start_); - --current_; - return *this; - } - - CheckedRandomAccessIterator& operator--(int) { - CheckedRandomAccessIterator old = *this; - --*this; - return old; - } - - CheckedRandomAccessIterator& operator+=(difference_type rhs) { - if (rhs > 0) { - CHECK_LE(rhs, end_ - current_); - } else { - CHECK_LE(-rhs, current_ - start_); - } - current_ += rhs; - return *this; - } - - CheckedRandomAccessIterator operator+(difference_type rhs) const { - CheckedRandomAccessIterator it = *this; - it += rhs; - return it; - } - - CheckedRandomAccessIterator& operator-=(difference_type rhs) { - if (rhs < 0) { - CHECK_LE(rhs, end_ - current_); - } else { - CHECK_LE(-rhs, current_ - start_); - } - current_ -= rhs; - return *this; - } - - CheckedRandomAccessIterator operator-(difference_type rhs) const { - CheckedRandomAccessIterator it = *this; - it -= rhs; - return it; - } - - friend difference_type operator-(const CheckedRandomAccessIterator& lhs, - const CheckedRandomAccessIterator& rhs) { - CHECK(lhs.start_ == rhs.start_); - CHECK(lhs.end_ == rhs.end_); - return lhs.current_ - rhs.current_; - } - - reference operator*() const { - CHECK(current_ != end_); - return *current_; - } - - pointer operator->() const { - CHECK(current_ != end_); - return current_; - } - - static bool IsRangeMoveSafe(const CheckedRandomAccessIterator& from_begin, - const CheckedRandomAccessIterator& from_end, - const CheckedRandomAccessIterator& to) - WARN_UNUSED_RESULT { - if (from_end < from_begin) - return false; - const auto from_begin_uintptr = get_uintptr(from_begin.current_); - const auto from_end_uintptr = get_uintptr(from_end.current_); - const auto to_begin_uintptr = get_uintptr(to.current_); - const auto to_end_uintptr = - get_uintptr((to + std::distance(from_begin, from_end)).current_); - - return to_begin_uintptr >= from_end_uintptr || - to_end_uintptr <= from_begin_uintptr; - } - - private: - void CheckComparable(const CheckedRandomAccessIterator& other) const { - CHECK_EQ(start_, other.start_); - CHECK_EQ(end_, other.end_); - } - - const T* start_ = nullptr; - T* current_ = nullptr; - const T* end_ = nullptr; -}; - -template <typename T> -class CheckedRandomAccessConstIterator { - public: - using difference_type = std::ptrdiff_t; - using value_type = typename std::iterator_traits<T*>::value_type; - using pointer = const T*; - using reference = const T&; - using iterator_category = std::random_access_iterator_tag; - CheckedRandomAccessConstIterator() = default; - CheckedRandomAccessConstIterator(T* start, const T* end) - : CheckedRandomAccessConstIterator(start, start, end) {} - CheckedRandomAccessConstIterator(T* start, T* current, const T* end) - : start_(start), current_(current), end_(end) { - CHECK(start <= current); - CHECK(current <= end); - } - CheckedRandomAccessConstIterator( - const CheckedRandomAccessConstIterator& other) = default; - CheckedRandomAccessConstIterator(const CheckedRandomAccessIterator<T>& other) + // Converting constructor allowing conversions like CRAI<T> to CRAI<const T>, + // but disallowing CRAI<const T> to CRAI<T> or CRAI<Derived> to CRAI<Base>, + // which are unsafe. Furthermore, this is the same condition as used by the + // converting constructors of std::span<T> and std::unique_ptr<T[]>. + // See https://wg21.link/n4042 for details. + template < + typename U, + std::enable_if_t<std::is_convertible<U (*)[], T (*)[]>::value>* = nullptr> + constexpr CheckedContiguousIterator(const CheckedContiguousIterator<U>& other) : start_(other.start_), current_(other.current_), end_(other.end_) { // We explicitly don't delegate to the 3-argument constructor here. Its // CHECKs would be redundant, since we expect |other| to maintain its own // invariant. However, DCHECKs never hurt anybody. Presumably. - DCHECK(other.start_ <= other.current_); - DCHECK(other.current_ <= other.end_); + DCHECK_LE(other.start_, other.current_); + DCHECK_LE(other.current_, other.end_); } - ~CheckedRandomAccessConstIterator() = default; - CheckedRandomAccessConstIterator& operator=( - const CheckedRandomAccessConstIterator& other) = default; + ~CheckedContiguousIterator() = default; - CheckedRandomAccessConstIterator& operator=( - CheckedRandomAccessConstIterator& other) = default; + constexpr CheckedContiguousIterator& operator=( + const CheckedContiguousIterator& other) = default; - bool operator==(const CheckedRandomAccessConstIterator& other) const { + constexpr bool operator==(const CheckedContiguousIterator& other) const { CheckComparable(other); return current_ == other.current_; } - bool operator!=(const CheckedRandomAccessConstIterator& other) const { + constexpr bool operator!=(const CheckedContiguousIterator& other) const { CheckComparable(other); return current_ != other.current_; } - bool operator<(const CheckedRandomAccessConstIterator& other) const { + constexpr bool operator<(const CheckedContiguousIterator& other) const { CheckComparable(other); return current_ < other.current_; } - bool operator<=(const CheckedRandomAccessConstIterator& other) const { + constexpr bool operator<=(const CheckedContiguousIterator& other) const { CheckComparable(other); return current_ <= other.current_; } - CheckedRandomAccessConstIterator& operator++() { - CHECK(current_ != end_); + constexpr bool operator>(const CheckedContiguousIterator& other) const { + CheckComparable(other); + return current_ > other.current_; + } + + constexpr bool operator>=(const CheckedContiguousIterator& other) const { + CheckComparable(other); + return current_ >= other.current_; + } + + constexpr CheckedContiguousIterator& operator++() { + CHECK_NE(current_, end_); ++current_; return *this; } - CheckedRandomAccessConstIterator operator++(int) { - CheckedRandomAccessConstIterator old = *this; + constexpr CheckedContiguousIterator operator++(int) { + CheckedContiguousIterator old = *this; ++*this; return old; } - CheckedRandomAccessConstIterator& operator--() { - CHECK(current_ != start_); + constexpr CheckedContiguousIterator& operator--() { + CHECK_NE(current_, start_); --current_; return *this; } - CheckedRandomAccessConstIterator& operator--(int) { - CheckedRandomAccessConstIterator old = *this; + constexpr CheckedContiguousIterator& operator--(int) { + CheckedContiguousIterator old = *this; --*this; return old; } - CheckedRandomAccessConstIterator& operator+=(difference_type rhs) { + constexpr CheckedContiguousIterator& operator+=(difference_type rhs) { if (rhs > 0) { CHECK_LE(rhs, end_ - current_); } else { @@ -251,13 +124,13 @@ class CheckedRandomAccessConstIterator { return *this; } - CheckedRandomAccessConstIterator operator+(difference_type rhs) const { - CheckedRandomAccessConstIterator it = *this; + constexpr CheckedContiguousIterator operator+(difference_type rhs) const { + CheckedContiguousIterator it = *this; it += rhs; return it; } - CheckedRandomAccessConstIterator& operator-=(difference_type rhs) { + constexpr CheckedContiguousIterator& operator-=(difference_type rhs) { if (rhs < 0) { CHECK_LE(rhs, end_ - current_); } else { @@ -267,34 +140,40 @@ class CheckedRandomAccessConstIterator { return *this; } - CheckedRandomAccessConstIterator operator-(difference_type rhs) const { - CheckedRandomAccessConstIterator it = *this; + constexpr CheckedContiguousIterator operator-(difference_type rhs) const { + CheckedContiguousIterator it = *this; it -= rhs; return it; } - friend difference_type operator-( - const CheckedRandomAccessConstIterator& lhs, - const CheckedRandomAccessConstIterator& rhs) { - CHECK(lhs.start_ == rhs.start_); - CHECK(lhs.end_ == rhs.end_); + constexpr friend difference_type operator-( + const CheckedContiguousIterator& lhs, + const CheckedContiguousIterator& rhs) { + CHECK_EQ(lhs.start_, rhs.start_); + CHECK_EQ(lhs.end_, rhs.end_); return lhs.current_ - rhs.current_; } - reference operator*() const { - CHECK(current_ != end_); + constexpr reference operator*() const { + CHECK_NE(current_, end_); return *current_; } - pointer operator->() const { - CHECK(current_ != end_); + constexpr pointer operator->() const { + CHECK_NE(current_, end_); return current_; } - static bool IsRangeMoveSafe( - const CheckedRandomAccessConstIterator& from_begin, - const CheckedRandomAccessConstIterator& from_end, - const CheckedRandomAccessConstIterator& to) WARN_UNUSED_RESULT { + constexpr reference operator[](difference_type rhs) const { + CHECK_GE(rhs, 0); + CHECK_LT(rhs, end_ - current_); + return current_[rhs]; + } + + static bool IsRangeMoveSafe(const CheckedContiguousIterator& from_begin, + const CheckedContiguousIterator& from_end, + const CheckedContiguousIterator& to) + WARN_UNUSED_RESULT { if (from_end < from_begin) return false; const auto from_begin_uintptr = get_uintptr(from_begin.current_); @@ -308,16 +187,19 @@ class CheckedRandomAccessConstIterator { } private: - void CheckComparable(const CheckedRandomAccessConstIterator& other) const { + constexpr void CheckComparable(const CheckedContiguousIterator& other) const { CHECK_EQ(start_, other.start_); CHECK_EQ(end_, other.end_); } const T* start_ = nullptr; - const T* current_ = nullptr; + T* current_ = nullptr; const T* end_ = nullptr; }; +template <typename T> +using CheckedContiguousConstIterator = CheckedContiguousIterator<const T>; + } // namespace base #endif // BASE_CONTAINERS_CHECKED_ITERATORS_H_ diff --git a/chromium/base/containers/flat_map.h b/chromium/base/containers/flat_map.h index 481e40861ce..a98f0bcd298 100644 --- a/chromium/base/containers/flat_map.h +++ b/chromium/base/containers/flat_map.h @@ -60,15 +60,12 @@ struct GetKeyFromValuePairFirst { // // Constructors (inputs need not be sorted): // flat_map(InputIterator first, InputIterator last, -// FlatContainerDupes = KEEP_FIRST_OF_DUPES, // const Compare& compare = Compare()); // flat_map(const flat_map&); // flat_map(flat_map&&); // flat_map(std::vector<value_type>, -// FlatContainerDupes = KEEP_FIRST_OF_DUPES, // const Compare& compare = Compare()); // Re-use storage. // flat_map(std::initializer_list<value_type> ilist, -// FlatContainerDupes = KEEP_FIRST_OF_DUPES, // const Compare& comp = Compare()); // // Assignment functions: @@ -108,8 +105,7 @@ struct GetKeyFromValuePairFirst { // pair<iterator, bool> insert(value_type&&); // iterator insert(const_iterator hint, const value_type&); // iterator insert(const_iterator hint, value_type&&); -// void insert(InputIterator first, InputIterator last, -// FlatContainerDupes = KEEP_FIRST_OF_DUPES); +// void insert(InputIterator first, InputIterator last); // pair<iterator, bool> insert_or_assign(K&&, M&&); // iterator insert_or_assign(const_iterator hint, K&&, M&&); // pair<iterator, bool> emplace(Args&&...); @@ -183,18 +179,15 @@ class flat_map : public ::base::internal::flat_tree< template <class InputIterator> flat_map(InputIterator first, InputIterator last, - FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES, const Compare& comp = Compare()); flat_map(const flat_map&) = default; flat_map(flat_map&&) noexcept = default; flat_map(std::vector<value_type> items, - FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES, const Compare& comp = Compare()); flat_map(std::initializer_list<value_type> ilist, - FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES, const Compare& comp = Compare()); ~flat_map() = default; @@ -249,22 +242,19 @@ template <class Key, class Mapped, class Compare> template <class InputIterator> flat_map<Key, Mapped, Compare>::flat_map(InputIterator first, InputIterator last, - FlatContainerDupes dupe_handling, const Compare& comp) - : tree(first, last, dupe_handling, comp) {} + : tree(first, last, comp) {} template <class Key, class Mapped, class Compare> flat_map<Key, Mapped, Compare>::flat_map(std::vector<value_type> items, - FlatContainerDupes dupe_handling, const Compare& comp) - : tree(std::move(items), dupe_handling, comp) {} + : tree(std::move(items), comp) {} template <class Key, class Mapped, class Compare> flat_map<Key, Mapped, Compare>::flat_map( std::initializer_list<value_type> ilist, - FlatContainerDupes dupe_handling, const Compare& comp) - : flat_map(std::begin(ilist), std::end(ilist), dupe_handling, comp) {} + : flat_map(std::begin(ilist), std::end(ilist), comp) {} // ---------------------------------------------------------------------------- // Assignments. diff --git a/chromium/base/containers/flat_map_unittest.cc b/chromium/base/containers/flat_map_unittest.cc index 87958bde414..e48de3cd972 100644 --- a/chromium/base/containers/flat_map_unittest.cc +++ b/chromium/base/containers/flat_map_unittest.cc @@ -41,11 +41,6 @@ TEST(FlatMap, RangeConstructor) { flat_map<int, int> first(std::begin(input_vals), std::end(input_vals)); EXPECT_THAT(first, ElementsAre(std::make_pair(1, 1), std::make_pair(2, 1), std::make_pair(3, 1))); - - flat_map<int, int> last(std::begin(input_vals), std::end(input_vals), - KEEP_LAST_OF_DUPES); - EXPECT_THAT(last, ElementsAre(std::make_pair(1, 3), std::make_pair(2, 3), - std::make_pair(3, 3))); } TEST(FlatMap, MoveConstructor) { @@ -68,36 +63,18 @@ TEST(FlatMap, MoveConstructor) { TEST(FlatMap, VectorConstructor) { using IntPair = std::pair<int, int>; using IntMap = flat_map<int, int>; - { - std::vector<IntPair> vect{{1, 1}, {1, 2}, {2, 1}}; - IntMap map(std::move(vect)); - EXPECT_THAT(map, ElementsAre(IntPair(1, 1), IntPair(2, 1))); - } - { - std::vector<IntPair> vect{{1, 1}, {1, 2}, {2, 1}}; - IntMap map(std::move(vect), KEEP_LAST_OF_DUPES); - EXPECT_THAT(map, ElementsAre(IntPair(1, 2), IntPair(2, 1))); - } + std::vector<IntPair> vect{{1, 1}, {1, 2}, {2, 1}}; + IntMap map(std::move(vect)); + EXPECT_THAT(map, ElementsAre(IntPair(1, 1), IntPair(2, 1))); } TEST(FlatMap, InitializerListConstructor) { - { - flat_map<int, int> cont( - {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {1, 2}, {10, 10}, {8, 8}}); - EXPECT_THAT(cont, ElementsAre(std::make_pair(1, 1), std::make_pair(2, 2), - std::make_pair(3, 3), std::make_pair(4, 4), - std::make_pair(5, 5), std::make_pair(8, 8), - std::make_pair(10, 10))); - } - { - flat_map<int, int> cont( - {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {1, 2}, {10, 10}, {8, 8}}, - KEEP_LAST_OF_DUPES); - EXPECT_THAT(cont, ElementsAre(std::make_pair(1, 2), std::make_pair(2, 2), - std::make_pair(3, 3), std::make_pair(4, 4), - std::make_pair(5, 5), std::make_pair(8, 8), - std::make_pair(10, 10))); - } + flat_map<int, int> cont( + {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {1, 2}, {10, 10}, {8, 8}}); + EXPECT_THAT(cont, ElementsAre(std::make_pair(1, 1), std::make_pair(2, 2), + std::make_pair(3, 3), std::make_pair(4, 4), + std::make_pair(5, 5), std::make_pair(8, 8), + std::make_pair(10, 10))); } TEST(FlatMap, InitializerListAssignment) { diff --git a/chromium/base/containers/flat_set.h b/chromium/base/containers/flat_set.h index 9fab64af3bf..8e890ef233d 100644 --- a/chromium/base/containers/flat_set.h +++ b/chromium/base/containers/flat_set.h @@ -46,15 +46,12 @@ namespace base { // // Constructors (inputs need not be sorted): // flat_set(InputIterator first, InputIterator last, -// FlatContainerDupes = KEEP_FIRST_OF_DUPES, // const Compare& compare = Compare()); // flat_set(const flat_set&); // flat_set(flat_set&&); // flat_set(std::vector<Key>, -// FlatContainerDupes = KEEP_FIRST_OF_DUPES, // const Compare& compare = Compare()); // Re-use storage. // flat_set(std::initializer_list<value_type> ilist, -// FlatContainerDupes = KEEP_FIRST_OF_DUPES, // const Compare& comp = Compare()); // // Assignment functions: @@ -90,8 +87,7 @@ namespace base { // Insert and accessor functions: // pair<iterator, bool> insert(const key_type&); // pair<iterator, bool> insert(key_type&&); -// void insert(InputIterator first, InputIterator last, -// FlatContainerDupes = KEEP_FIRST_OF_DUPES); +// void insert(InputIterator first, InputIterator last); // iterator insert(const_iterator hint, const key_type&); // iterator insert(const_iterator hint, key_type&&); // pair<iterator, bool> emplace(Args&&...); diff --git a/chromium/base/containers/flat_set_unittest.cc b/chromium/base/containers/flat_set_unittest.cc index 45969754985..7b10c730645 100644 --- a/chromium/base/containers/flat_set_unittest.cc +++ b/chromium/base/containers/flat_set_unittest.cc @@ -38,16 +38,15 @@ TEST(FlatSet, IncompleteType) { TEST(FlatSet, RangeConstructor) { flat_set<int>::value_type input_vals[] = {1, 1, 1, 2, 2, 2, 3, 3, 3}; - flat_set<int> cont(std::begin(input_vals), std::end(input_vals), - base::KEEP_FIRST_OF_DUPES); + flat_set<int> cont(std::begin(input_vals), std::end(input_vals)); EXPECT_THAT(cont, ElementsAre(1, 2, 3)); } TEST(FlatSet, MoveConstructor) { int input_range[] = {1, 2, 3, 4}; - flat_set<MoveOnlyInt> original(std::begin(input_range), std::end(input_range), - base::KEEP_FIRST_OF_DUPES); + flat_set<MoveOnlyInt> original(std::begin(input_range), + std::end(input_range)); flat_set<MoveOnlyInt> moved(std::move(original)); EXPECT_EQ(1U, moved.count(MoveOnlyInt(1))); @@ -57,7 +56,7 @@ TEST(FlatSet, MoveConstructor) { } TEST(FlatSet, InitializerListConstructor) { - flat_set<int> cont({1, 2, 3, 4, 5, 6, 10, 8}, KEEP_FIRST_OF_DUPES); + flat_set<int> cont({1, 2, 3, 4, 5, 6, 10, 8}); EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 8, 10)); } diff --git a/chromium/base/containers/flat_tree.h b/chromium/base/containers/flat_tree.h index 5222e388ea9..89c75d70692 100644 --- a/chromium/base/containers/flat_tree.h +++ b/chromium/base/containers/flat_tree.h @@ -14,11 +14,6 @@ namespace base { -enum FlatContainerDupes { - KEEP_FIRST_OF_DUPES, - KEEP_LAST_OF_DUPES, -}; - namespace internal { // This is a convenience method returning true if Iterator is at least a @@ -30,34 +25,6 @@ constexpr bool is_multipass() { typename std::iterator_traits<Iterator>::iterator_category>::value; } -// This algorithm is like unique() from the standard library except it -// selects only the last of consecutive values instead of the first. -template <class Iterator, class BinaryPredicate> -Iterator LastUnique(Iterator first, Iterator last, BinaryPredicate compare) { - Iterator replacable = std::adjacent_find(first, last, compare); - - // No duplicate elements found. - if (replacable == last) - return last; - - first = std::next(replacable); - - // Last element is a duplicate but all others are unique. - if (first == last) - return replacable; - - // This loop is based on std::adjacent_find but std::adjacent_find doesn't - // quite cut it. - for (Iterator next = std::next(first); next != last; ++next, ++first) { - if (!compare(*first, *next)) - *replacable++ = std::move(*first); - } - - // Last element should be copied unconditionally. - *replacable++ = std::move(*first); - return replacable; -} - // Uses SFINAE to detect whether type has is_transparent member. template <typename T, typename = void> struct IsTransparentCompare : std::false_type {}; @@ -136,18 +103,15 @@ class flat_tree { template <class InputIterator> flat_tree(InputIterator first, InputIterator last, - FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES, const key_compare& comp = key_compare()); flat_tree(const flat_tree&); flat_tree(flat_tree&&) noexcept = default; flat_tree(std::vector<value_type> items, - FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES, const key_compare& comp = key_compare()); flat_tree(std::initializer_list<value_type> ilist, - FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES, const key_compare& comp = key_compare()); ~flat_tree(); @@ -222,12 +186,9 @@ class flat_tree { iterator insert(const_iterator position_hint, value_type&& x); // This method inserts the values from the range [first, last) into the - // current tree. In case of KEEP_LAST_OF_DUPES newly added elements can - // overwrite existing values. + // current tree. template <class InputIterator> - void insert(InputIterator first, - InputIterator last, - FlatContainerDupes dupes = KEEP_FIRST_OF_DUPES); + void insert(InputIterator first, InputIterator last); template <class... Args> std::pair<iterator, bool> emplace(Args&&... args); @@ -447,28 +408,17 @@ class flat_tree { return {position, false}; } - void sort_and_unique(iterator first, - iterator last, - FlatContainerDupes dupes) { + void sort_and_unique(iterator first, iterator last) { // Preserve stability for the unique code below. - std::stable_sort(first, last, impl_.get_value_comp()); + std::stable_sort(first, last, value_comp()); - auto comparator = [this](const value_type& lhs, const value_type& rhs) { + auto equal_comp = [this](const value_type& lhs, const value_type& rhs) { // lhs is already <= rhs due to sort, therefore // !(lhs < rhs) <=> lhs == rhs. - return !impl_.get_value_comp()(lhs, rhs); + return !value_comp()(lhs, rhs); }; - iterator erase_after; - switch (dupes) { - case KEEP_FIRST_OF_DUPES: - erase_after = std::unique(first, last, comparator); - break; - case KEEP_LAST_OF_DUPES: - erase_after = LastUnique(first, last, comparator); - break; - } - erase(erase_after, last); + erase(std::unique(first, last, equal_comp), last); } // To support comparators that may not be possible to default-construct, we @@ -512,10 +462,9 @@ template <class InputIterator> flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree( InputIterator first, InputIterator last, - FlatContainerDupes dupe_handling, const KeyCompare& comp) : impl_(comp, first, last) { - sort_and_unique(begin(), end(), dupe_handling); + sort_and_unique(begin(), end()); } template <class Key, class Value, class GetKeyFromValue, class KeyCompare> @@ -525,18 +474,16 @@ flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree( template <class Key, class Value, class GetKeyFromValue, class KeyCompare> flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree( std::vector<value_type> items, - FlatContainerDupes dupe_handling, const KeyCompare& comp) : impl_(comp, std::move(items)) { - sort_and_unique(begin(), end(), dupe_handling); + sort_and_unique(begin(), end()); } template <class Key, class Value, class GetKeyFromValue, class KeyCompare> flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree( std::initializer_list<value_type> ilist, - FlatContainerDupes dupe_handling, const KeyCompare& comp) - : flat_tree(std::begin(ilist), std::end(ilist), dupe_handling, comp) {} + : flat_tree(std::begin(ilist), std::end(ilist), comp) {} template <class Key, class Value, class GetKeyFromValue, class KeyCompare> flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::~flat_tree() = default; @@ -556,7 +503,7 @@ template <class Key, class Value, class GetKeyFromValue, class KeyCompare> auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::operator=( std::initializer_list<value_type> ilist) -> flat_tree& { impl_.body_ = ilist; - sort_and_unique(begin(), end(), KEEP_FIRST_OF_DUPES); + sort_and_unique(begin(), end()); return *this; } @@ -717,62 +664,38 @@ template <class Key, class Value, class GetKeyFromValue, class KeyCompare> template <class InputIterator> void flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::insert( InputIterator first, - InputIterator last, - FlatContainerDupes dupes) { + InputIterator last) { if (first == last) return; - // Cache results whether existing elements should be overwritten and whether - // inserting new elements happens immediately or will be done in a batch. - const bool overwrite_existing = dupes == KEEP_LAST_OF_DUPES; - const bool insert_inplace = - is_multipass<InputIterator>() && std::next(first) == last; - - if (insert_inplace) { - if (overwrite_existing) { - for (; first != last; ++first) - insert_or_assign(*first); - } else - std::copy(first, last, std::inserter(*this, end())); + // Dispatch to single element insert if the input range contains a single + // element. + if (is_multipass<InputIterator>() && std::next(first) == last) { + insert(end(), *first); return; } // Provide a convenience lambda to obtain an iterator pointing past the last // old element. This needs to be dymanic due to possible re-allocations. - const size_type original_size = size(); - auto middle = [this, original_size]() { - return std::next(begin(), original_size); - }; + auto middle = [this, size = size()] { return std::next(begin(), size); }; // For batch updates initialize the first insertion point. - difference_type pos_first_new = original_size; + difference_type pos_first_new = size(); // Loop over the input range while appending new values and overwriting // existing ones, if applicable. Keep track of the first insertion point. - if (overwrite_existing) { - for (; first != last; ++first) { - std::pair<iterator, bool> result = - append_or_assign(begin(), middle(), *first); - if (result.second) { - pos_first_new = - std::min(pos_first_new, std::distance(begin(), result.first)); - } - } - } else { - for (; first != last; ++first) { - std::pair<iterator, bool> result = - append_unique(begin(), middle(), *first); - if (result.second) { - pos_first_new = - std::min(pos_first_new, std::distance(begin(), result.first)); - } + for (; first != last; ++first) { + std::pair<iterator, bool> result = append_unique(begin(), middle(), *first); + if (result.second) { + pos_first_new = + std::min(pos_first_new, std::distance(begin(), result.first)); } } // The new elements might be unordered and contain duplicates, so post-process // the just inserted elements and merge them with the rest, inserting them at // the previously found spot. - sort_and_unique(middle(), end(), dupes); + sort_and_unique(middle(), end()); std::inplace_merge(std::next(begin(), pos_first_new), middle(), end(), value_comp()); } diff --git a/chromium/base/containers/flat_tree_unittest.cc b/chromium/base/containers/flat_tree_unittest.cc index 212ef773166..c305a0c20c8 100644 --- a/chromium/base/containers/flat_tree_unittest.cc +++ b/chromium/base/containers/flat_tree_unittest.cc @@ -188,68 +188,6 @@ TEST(FlatTree, IsMultipass) { "RandomAccessIterator is multipass"); } -TEST(FlatTree, LastUnique) { - using Pair = std::pair<int, int>; - using Vect = std::vector<Pair>; - - auto cmp = [](const Pair& lhs, const Pair& rhs) { - return lhs.first == rhs.first; - }; - - // Empty case. - Vect empty; - EXPECT_EQ(empty.end(), LastUnique(empty.begin(), empty.end(), cmp)); - - // Single element. - Vect one; - one.push_back(Pair(1, 1)); - EXPECT_EQ(one.end(), LastUnique(one.begin(), one.end(), cmp)); - ASSERT_EQ(1u, one.size()); - EXPECT_THAT(one, ElementsAre(Pair(1, 1))); - - // Two elements, already unique. - Vect two_u; - two_u.push_back(Pair(1, 1)); - two_u.push_back(Pair(2, 2)); - EXPECT_EQ(two_u.end(), LastUnique(two_u.begin(), two_u.end(), cmp)); - EXPECT_THAT(two_u, ElementsAre(Pair(1, 1), Pair(2, 2))); - - // Two elements, dupes. - Vect two_d; - two_d.push_back(Pair(1, 1)); - two_d.push_back(Pair(1, 2)); - auto last = LastUnique(two_d.begin(), two_d.end(), cmp); - EXPECT_EQ(two_d.begin() + 1, last); - two_d.erase(last, two_d.end()); - EXPECT_THAT(two_d, ElementsAre(Pair(1, 2))); - - // Non-dupes, dupes, non-dupes. - Vect ndn; - ndn.push_back(Pair(1, 1)); - ndn.push_back(Pair(2, 1)); - ndn.push_back(Pair(2, 2)); - ndn.push_back(Pair(2, 3)); - ndn.push_back(Pair(3, 1)); - last = LastUnique(ndn.begin(), ndn.end(), cmp); - EXPECT_EQ(ndn.begin() + 3, last); - ndn.erase(last, ndn.end()); - EXPECT_THAT(ndn, ElementsAre(Pair(1, 1), Pair(2, 3), Pair(3, 1))); - - // Dupes, non-dupes, dupes. - Vect dnd; - dnd.push_back(Pair(1, 1)); - dnd.push_back(Pair(1, 2)); - dnd.push_back(Pair(1, 3)); - dnd.push_back(Pair(2, 1)); - dnd.push_back(Pair(3, 1)); - dnd.push_back(Pair(3, 2)); - dnd.push_back(Pair(3, 3)); - last = LastUnique(dnd.begin(), dnd.end(), cmp); - EXPECT_EQ(dnd.begin() + 3, last); - dnd.erase(last, dnd.end()); - EXPECT_THAT(dnd, ElementsAre(Pair(1, 3), Pair(2, 1), Pair(3, 3))); -} - // ---------------------------------------------------------------------------- // Class. @@ -350,7 +288,6 @@ TEST(FlatTree, DefaultConstructor) { // flat_tree(InputIterator first, // InputIterator last, -// FlatContainerDupes dupe_handling, // const Compare& comp = Compare()) TEST(FlatTree, RangeConstructor) { @@ -362,12 +299,6 @@ TEST(FlatTree, RangeConstructor) { MakeInputIterator(std::end(input_vals))); EXPECT_THAT(first_of, ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1))); - - IntPairTree last_of(MakeInputIterator(std::begin(input_vals)), - MakeInputIterator(std::end(input_vals)), - KEEP_LAST_OF_DUPES); - EXPECT_THAT(last_of, - ElementsAre(IntPair(1, 3), IntPair(2, 3), IntPair(3, 3))); } { TreeWithStrangeCompare::value_type input_vals[] = {1, 1, 1, 2, 2, @@ -375,7 +306,6 @@ TEST(FlatTree, RangeConstructor) { TreeWithStrangeCompare cont(MakeInputIterator(std::begin(input_vals)), MakeInputIterator(std::end(input_vals)), - KEEP_FIRST_OF_DUPES, NonDefaultConstructibleCompare(0)); EXPECT_THAT(cont, ElementsAre(1, 2, 3)); } @@ -408,7 +338,7 @@ TEST(FlatTree, MoveConstructor) { EXPECT_EQ(1U, moved.count(MoveOnlyInt(4))); } -// flat_tree(std::vector<value_type>, FlatContainerDupes dupe_handling) +// flat_tree(std::vector<value_type>) TEST(FlatTree, VectorConstructor) { using Pair = std::pair<int, MoveOnlyInt>; @@ -434,15 +364,9 @@ TEST(FlatTree, VectorConstructor) { const Pair& first = *(tree.begin() + 1); ASSERT_EQ(2, first.first); ASSERT_EQ(0, first.second.data()); - - // Test KEEP_LAST_OF_DUPES with a simple vector constructor. - std::vector<IntPair> int_storage{{1, 1}, {1, 2}, {2, 1}}; - IntPairTree int_tree(std::move(int_storage), KEEP_LAST_OF_DUPES); - EXPECT_THAT(int_tree, ElementsAre(IntPair(1, 2), IntPair(2, 1))); } // flat_tree(std::initializer_list<value_type> ilist, -// FlatContainerDupes dupe_handling, // const Compare& comp = Compare()) TEST(FlatTree, InitializerListConstructor) { @@ -455,7 +379,7 @@ TEST(FlatTree, InitializerListConstructor) { EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 8, 10)); } { - TreeWithStrangeCompare cont({1, 2, 3, 4, 5, 6, 10, 8}, KEEP_FIRST_OF_DUPES, + TreeWithStrangeCompare cont({1, 2, 3, 4, 5, 6, 10, 8}, NonDefaultConstructibleCompare(0)); EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 8, 10)); } @@ -463,10 +387,6 @@ TEST(FlatTree, InitializerListConstructor) { IntPairTree first_of({{1, 1}, {2, 1}, {1, 2}}); EXPECT_THAT(first_of, ElementsAre(IntPair(1, 1), IntPair(2, 1))); } - { - IntPairTree last_of({{1, 1}, {2, 1}, {1, 2}}, KEEP_LAST_OF_DUPES); - EXPECT_THAT(last_of, ElementsAre(IntPair(1, 2), IntPair(2, 1))); - } } // ---------------------------------------------------------------------------- @@ -791,14 +711,6 @@ TEST(FlatTree, InsertIterIter) { { IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}}); - IntPair int_pairs[] = {{1, 2}}; - cont.insert(std::begin(int_pairs), std::end(int_pairs), KEEP_LAST_OF_DUPES); - EXPECT_THAT(cont, ElementsAre(IntPair(1, 2), IntPair(2, 1), IntPair(3, 1), - IntPair(4, 1))); - } - - { - IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}}); IntPair int_pairs[] = {{5, 1}}; cont.insert(std::begin(int_pairs), std::end(int_pairs)); EXPECT_THAT(cont, ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1), @@ -807,14 +719,6 @@ TEST(FlatTree, InsertIterIter) { { IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}}); - IntPair int_pairs[] = {{5, 1}}; - cont.insert(std::begin(int_pairs), std::end(int_pairs), KEEP_LAST_OF_DUPES); - EXPECT_THAT(cont, ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1), - IntPair(4, 1), IntPair(5, 1))); - } - - { - IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}}); IntPair int_pairs[] = {{3, 2}, {1, 2}, {4, 2}, {2, 2}}; cont.insert(std::begin(int_pairs), std::end(int_pairs)); EXPECT_THAT(cont, ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1), @@ -823,14 +727,6 @@ TEST(FlatTree, InsertIterIter) { { IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}}); - IntPair int_pairs[] = {{3, 2}, {1, 2}, {4, 2}, {2, 2}}; - cont.insert(std::begin(int_pairs), std::end(int_pairs), KEEP_LAST_OF_DUPES); - EXPECT_THAT(cont, ElementsAre(IntPair(1, 2), IntPair(2, 2), IntPair(3, 2), - IntPair(4, 2))); - } - - { - IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}}); IntPair int_pairs[] = {{3, 2}, {1, 2}, {4, 2}, {2, 2}, {7, 2}, {6, 2}, {8, 2}, {5, 2}, {5, 3}, {6, 3}, {7, 3}, {8, 3}}; cont.insert(std::begin(int_pairs), std::end(int_pairs)); @@ -838,16 +734,6 @@ TEST(FlatTree, InsertIterIter) { IntPair(4, 1), IntPair(5, 2), IntPair(6, 2), IntPair(7, 2), IntPair(8, 2))); } - - { - IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}}); - IntPair int_pairs[] = {{3, 2}, {1, 2}, {4, 2}, {2, 2}, {7, 2}, {6, 2}, - {8, 2}, {5, 2}, {5, 3}, {6, 3}, {7, 3}, {8, 3}}; - cont.insert(std::begin(int_pairs), std::end(int_pairs), KEEP_LAST_OF_DUPES); - EXPECT_THAT(cont, ElementsAre(IntPair(1, 2), IntPair(2, 2), IntPair(3, 2), - IntPair(4, 2), IntPair(5, 3), IntPair(6, 3), - IntPair(7, 3), IntPair(8, 3))); - } } // template <class... Args> diff --git a/chromium/base/containers/intrusive_heap.h b/chromium/base/containers/intrusive_heap.h index 53d3909560c..99f61b46784 100644 --- a/chromium/base/containers/intrusive_heap.h +++ b/chromium/base/containers/intrusive_heap.h @@ -131,6 +131,7 @@ #include <algorithm> #include <functional> +#include <limits> #include <type_traits> #include <utility> #include <vector> @@ -149,7 +150,7 @@ namespace base { // in place. class BASE_EXPORT HeapHandle { public: - enum : size_t { kInvalidIndex = -1 }; + enum : size_t { kInvalidIndex = std::numeric_limits<size_t>::max() }; constexpr HeapHandle() = default; constexpr HeapHandle(const HeapHandle& other) = default; @@ -715,7 +716,7 @@ IntrusiveHeap<T, Compare, HeapHandleAccessor>::~IntrusiveHeap() { template <typename T, typename Compare, typename HeapHandleAccessor> IntrusiveHeap<T, Compare, HeapHandleAccessor>& IntrusiveHeap<T, Compare, HeapHandleAccessor>::operator=( - IntrusiveHeap&& other) { + IntrusiveHeap&& other) noexcept { clear(); impl_ = std::move(other.impl_); return *this; diff --git a/chromium/base/containers/span.h b/chromium/base/containers/span.h index f56172d1a06..fc7f58d8e4b 100644 --- a/chromium/base/containers/span.h +++ b/chromium/base/containers/span.h @@ -224,8 +224,8 @@ class span : public internal::ExtentStorage<Extent> { using difference_type = ptrdiff_t; using pointer = T*; using reference = T&; - using iterator = CheckedRandomAccessIterator<T>; - using const_iterator = CheckedRandomAccessConstIterator<T>; + using iterator = CheckedContiguousIterator<T>; + using const_iterator = CheckedContiguousConstIterator<T>; using reverse_iterator = std::reverse_iterator<iterator>; using const_reverse_iterator = std::reverse_iterator<const_iterator>; static constexpr index_type extent = Extent; @@ -405,8 +405,10 @@ class span : public internal::ExtentStorage<Extent> { constexpr T* data() const noexcept { return data_; } // [span.iter], span iterator support - iterator begin() const noexcept { return iterator(data_, data_ + size()); } - iterator end() const noexcept { + constexpr iterator begin() const noexcept { + return iterator(data_, data_ + size()); + } + constexpr iterator end() const noexcept { return iterator(data_, data_ + size(), data_ + size()); } diff --git a/chromium/base/containers/span_unittest.cc b/chromium/base/containers/span_unittest.cc index 5ec3f3f4748..e8daf948420 100644 --- a/chromium/base/containers/span_unittest.cc +++ b/chromium/base/containers/span_unittest.cc @@ -9,6 +9,7 @@ #include <algorithm> #include <memory> #include <string> +#include <type_traits> #include <vector> #include "base/containers/checked_iterators.h" @@ -22,6 +23,24 @@ using ::testing::Pointwise; namespace base { +namespace { + +// constexpr implementation of std::equal's 4 argument overload. +template <class InputIterator1, class InputIterator2> +constexpr bool constexpr_equal(InputIterator1 first1, + InputIterator1 last1, + InputIterator2 first2, + InputIterator2 last2) { + for (; first1 != last1 && first2 != last2; ++first1, ++first2) { + if (*first1 != *first2) + return false; + } + + return first1 == last1 && first2 == last2; +} + +} // namespace + TEST(SpanTest, DefaultConstructor) { span<int> dynamic_span; EXPECT_EQ(nullptr, dynamic_span.data()); @@ -474,6 +493,17 @@ TEST(SpanTest, TemplatedSubspan) { } } +TEST(SpanTest, SubscriptedBeginIterator) { + int array[] = {1, 2, 3}; + span<const int> const_span(array); + for (size_t i = 0; i < const_span.size(); ++i) + EXPECT_EQ(array[i], const_span.begin()[i]); + + span<int> mutable_span(array); + for (size_t i = 0; i < mutable_span.size(); ++i) + EXPECT_EQ(array[i], mutable_span.begin()[i]); +} + TEST(SpanTest, TemplatedFirstOnDynamicSpan) { int array[] = {1, 2, 3}; span<const int> span(array); @@ -950,6 +980,21 @@ TEST(SpanTest, Iterator) { EXPECT_THAT(results, ElementsAre(1, 6, 1, 8, 0)); } +TEST(SpanTest, ConstexprIterator) { + static constexpr int kArray[] = {1, 6, 1, 8, 0}; + constexpr span<const int> span(kArray); + + static_assert(constexpr_equal(std::begin(kArray), std::end(kArray), + span.begin(), span.end()), + ""); + static_assert(1 == span.begin()[0], ""); + static_assert(1 == *(span.begin() += 0), ""); + static_assert(6 == *(span.begin() += 1), ""); + + static_assert(1 == *((span.begin() + 1) -= 1), ""); + static_assert(6 == *((span.begin() + 1) -= 0), ""); +} + TEST(SpanTest, ReverseIterator) { static constexpr int kArray[] = {1, 6, 1, 8, 0}; constexpr span<const int> span(kArray); @@ -1212,12 +1257,16 @@ TEST(SpanTest, EnsureConstexprGoodness) { TEST(SpanTest, OutOfBoundsDeath) { constexpr span<int, 0> kEmptySpan; ASSERT_DEATH_IF_SUPPORTED(kEmptySpan[0], ""); + ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.begin()[0], ""); + ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.end()[0], ""); ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.first(1), ""); ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.last(1), ""); ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.subspan(1), ""); constexpr span<int> kEmptyDynamicSpan; ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan[0], ""); + ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.begin()[0], ""); + ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.end()[0], ""); ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.front(), ""); ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.first(1), ""); ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.last(1), ""); @@ -1228,6 +1277,8 @@ TEST(SpanTest, OutOfBoundsDeath) { constexpr span<const int> kNonEmptyDynamicSpan(kArray); EXPECT_EQ(3U, kNonEmptyDynamicSpan.size()); ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan[4], ""); + ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.begin()[-1], ""); + ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.begin()[3], ""); ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.subspan(10), ""); ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.subspan(1, 7), ""); } @@ -1242,47 +1293,73 @@ TEST(SpanTest, IteratorIsRangeMoveSafe) { // Overlapping ranges. for (const int dest_start_index : kOverlappingStartIndexes) { - EXPECT_FALSE(CheckedRandomAccessIterator<const int>::IsRangeMoveSafe( + EXPECT_FALSE(CheckedContiguousIterator<const int>::IsRangeMoveSafe( span.begin(), span.end(), - CheckedRandomAccessIterator<const int>( + CheckedContiguousIterator<const int>( span.data() + dest_start_index, span.data() + dest_start_index + kNumElements))); - EXPECT_FALSE(CheckedRandomAccessConstIterator<const int>::IsRangeMoveSafe( + EXPECT_FALSE(CheckedContiguousConstIterator<const int>::IsRangeMoveSafe( span.cbegin(), span.cend(), - CheckedRandomAccessConstIterator<const int>( + CheckedContiguousConstIterator<const int>( span.data() + dest_start_index, span.data() + dest_start_index + kNumElements))); } // Non-overlapping ranges. for (const int dest_start_index : kNonOverlappingStartIndexes) { - EXPECT_TRUE(CheckedRandomAccessIterator<const int>::IsRangeMoveSafe( + EXPECT_TRUE(CheckedContiguousIterator<const int>::IsRangeMoveSafe( span.begin(), span.end(), - CheckedRandomAccessIterator<const int>( + CheckedContiguousIterator<const int>( span.data() + dest_start_index, span.data() + dest_start_index + kNumElements))); - EXPECT_TRUE(CheckedRandomAccessConstIterator<const int>::IsRangeMoveSafe( + EXPECT_TRUE(CheckedContiguousConstIterator<const int>::IsRangeMoveSafe( span.cbegin(), span.cend(), - CheckedRandomAccessConstIterator<const int>( + CheckedContiguousConstIterator<const int>( span.data() + dest_start_index, span.data() + dest_start_index + kNumElements))); } // IsRangeMoveSafe is true if the length to be moved is 0. - EXPECT_TRUE(CheckedRandomAccessIterator<const int>::IsRangeMoveSafe( + EXPECT_TRUE(CheckedContiguousIterator<const int>::IsRangeMoveSafe( span.begin(), span.begin(), - CheckedRandomAccessIterator<const int>(span.data(), span.data()))); - EXPECT_TRUE(CheckedRandomAccessConstIterator<const int>::IsRangeMoveSafe( + CheckedContiguousIterator<const int>(span.data(), span.data()))); + EXPECT_TRUE(CheckedContiguousConstIterator<const int>::IsRangeMoveSafe( span.cbegin(), span.cbegin(), - CheckedRandomAccessConstIterator<const int>(span.data(), span.data()))); + CheckedContiguousConstIterator<const int>(span.data(), span.data()))); // IsRangeMoveSafe is false if end < begin. - EXPECT_FALSE(CheckedRandomAccessIterator<const int>::IsRangeMoveSafe( + EXPECT_FALSE(CheckedContiguousIterator<const int>::IsRangeMoveSafe( span.end(), span.begin(), - CheckedRandomAccessIterator<const int>(span.data(), span.data()))); - EXPECT_FALSE(CheckedRandomAccessConstIterator<const int>::IsRangeMoveSafe( + CheckedContiguousIterator<const int>(span.data(), span.data()))); + EXPECT_FALSE(CheckedContiguousConstIterator<const int>::IsRangeMoveSafe( span.cend(), span.cbegin(), - CheckedRandomAccessConstIterator<const int>(span.data(), span.data()))); + CheckedContiguousConstIterator<const int>(span.data(), span.data()))); +} + +TEST(SpanTest, Sort) { + int array[] = {5, 4, 3, 2, 1}; + + span<int> dynamic_span = array; + std::sort(dynamic_span.begin(), dynamic_span.end()); + EXPECT_THAT(array, ElementsAre(1, 2, 3, 4, 5)); + std::sort(dynamic_span.rbegin(), dynamic_span.rend()); + EXPECT_THAT(array, ElementsAre(5, 4, 3, 2, 1)); + + span<int, 5> static_span = array; + std::sort(static_span.rbegin(), static_span.rend(), std::greater<>()); + EXPECT_THAT(array, ElementsAre(1, 2, 3, 4, 5)); + std::sort(static_span.begin(), static_span.end(), std::greater<>()); + EXPECT_THAT(array, ElementsAre(5, 4, 3, 2, 1)); +} + +TEST(SpanTest, IteratorConversions) { + static_assert(std::is_convertible<span<int>::iterator, + span<int>::const_iterator>::value, + "Error: iterator should be convertible to const_iterator"); + + static_assert(!std::is_convertible<span<int>::const_iterator, + span<int>::iterator>::value, + "Error: const_iterator should not be convertible to iterator"); } } // namespace base diff --git a/chromium/base/feature_list.cc b/chromium/base/feature_list.cc index 41e1775bed9..916b82485fe 100644 --- a/chromium/base/feature_list.cc +++ b/chromium/base/feature_list.cc @@ -29,6 +29,17 @@ FeatureList* g_feature_list_instance = nullptr; // Tracks whether the FeatureList instance was initialized via an accessor. bool g_initialized_from_accessor = false; +#if DCHECK_IS_ON() +const char* g_reason_overrides_disallowed = nullptr; + +void DCheckOverridesAllowed() { + const bool feature_overrides_allowed = !g_reason_overrides_disallowed; + DCHECK(feature_overrides_allowed) << g_reason_overrides_disallowed; +} +#else +void DCheckOverridesAllowed() {} +#endif + // An allocator entry for a feature in shared memory. The FeatureEntry is // followed by a base::Pickle object that contains the feature and trial name. struct FeatureEntry { @@ -86,6 +97,23 @@ FeatureList::FeatureList() = default; FeatureList::~FeatureList() = default; +FeatureList::ScopedDisallowOverrides::ScopedDisallowOverrides( + const char* reason) +#if DCHECK_IS_ON() + : previous_reason_(g_reason_overrides_disallowed) { + g_reason_overrides_disallowed = reason; +} +#else +{ +} +#endif + +FeatureList::ScopedDisallowOverrides::~ScopedDisallowOverrides() { +#if DCHECK_IS_ON() + g_reason_overrides_disallowed = previous_reason_; +#endif +} + void FeatureList::InitializeFromCommandLine( const std::string& enable_features, const std::string& disable_features) { @@ -385,6 +413,7 @@ void FeatureList::RegisterOverride(StringPiece feature_name, OverrideState overridden_state, FieldTrial* field_trial) { DCHECK(!initialized_); + DCheckOverridesAllowed(); if (field_trial) { DCHECK(IsValidFeatureOrFieldTrialName(field_trial->trial_name())) << field_trial->trial_name(); diff --git a/chromium/base/feature_list.h b/chromium/base/feature_list.h index a6f39daa25e..f46b30dd1ee 100644 --- a/chromium/base/feature_list.h +++ b/chromium/base/feature_list.h @@ -24,6 +24,8 @@ class FieldTrial; class FieldTrialList; // Specifies whether a given feature is enabled or disabled by default. +// NOTE: The actual runtime state may be different, due to a field trial or a +// command line switch. enum FeatureState { FEATURE_DISABLED_BY_DEFAULT, FEATURE_ENABLED_BY_DEFAULT, @@ -42,6 +44,8 @@ struct BASE_EXPORT Feature { const char* const name; // The default state (i.e. enabled or disabled) for this feature. + // NOTE: The actual runtime state may be different, due to a field trial or a + // command line switch. const FeatureState default_state; }; @@ -96,6 +100,21 @@ class BASE_EXPORT FeatureList { FeatureList(); ~FeatureList(); + // Used by common test fixture classes to prevent abuse of ScopedFeatureList + // after multiple threads have started. + class BASE_EXPORT ScopedDisallowOverrides { + public: + explicit ScopedDisallowOverrides(const char* reason); + ~ScopedDisallowOverrides(); + + private: +#if DCHECK_IS_ON() + const char* const previous_reason_; +#endif + + DISALLOW_COPY_AND_ASSIGN(ScopedDisallowOverrides); + }; + // Specifies whether a feature override enables or disables the feature. enum OverrideState { OVERRIDE_USE_DEFAULT, diff --git a/chromium/base/feature_list_unittest.cc b/chromium/base/feature_list_unittest.cc index 813fb25229f..de9bc5d7fe8 100644 --- a/chromium/base/feature_list_unittest.cc +++ b/chromium/base/feature_list_unittest.cc @@ -18,6 +18,7 @@ #include "base/strings/string_piece.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" +#include "base/test/scoped_feature_list.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { @@ -45,26 +46,14 @@ std::string SortFeatureListString(const std::string& feature_list) { class FeatureListTest : public testing::Test { public: - FeatureListTest() : feature_list_(nullptr) { - RegisterFeatureListInstance(std::make_unique<FeatureList>()); + FeatureListTest() { + // Provide an empty FeatureList to each test by default. + scoped_feature_list_.InitWithFeatureList(std::make_unique<FeatureList>()); } - ~FeatureListTest() override { ClearFeatureListInstance(); } - - void RegisterFeatureListInstance(std::unique_ptr<FeatureList> feature_list) { - FeatureList::ClearInstanceForTesting(); - feature_list_ = feature_list.get(); - FeatureList::SetInstance(std::move(feature_list)); - } - void ClearFeatureListInstance() { - FeatureList::ClearInstanceForTesting(); - feature_list_ = nullptr; - } - - FeatureList* feature_list() { return feature_list_; } + ~FeatureListTest() override = default; private: - // Weak. Owned by the FeatureList::SetInstance(). - FeatureList* feature_list_; + test::ScopedFeatureList scoped_feature_list_; DISALLOW_COPY_AND_ASSIGN(FeatureListTest); }; @@ -96,11 +85,11 @@ TEST_F(FeatureListTest, InitializeFromCommandLine) { test_case.enable_features, test_case.disable_features)); - ClearFeatureListInstance(); - std::unique_ptr<FeatureList> feature_list(new FeatureList); + auto feature_list = std::make_unique<FeatureList>(); feature_list->InitializeFromCommandLine(test_case.enable_features, test_case.disable_features); - RegisterFeatureListInstance(std::move(feature_list)); + test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatureList(std::move(feature_list)); EXPECT_EQ(test_case.expected_feature_on_state, FeatureList::IsEnabled(kFeatureOnByDefault)) @@ -115,19 +104,23 @@ TEST_F(FeatureListTest, CheckFeatureIdentity) { // Tests that CheckFeatureIdentity() correctly detects when two different // structs with the same feature name are passed to it. + test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatureList(std::make_unique<FeatureList>()); + FeatureList* feature_list = FeatureList::GetInstance(); + // Call it twice for each feature at the top of the file, since the first call // makes it remember the entry and the second call will verify it. - EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOnByDefault)); - EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOnByDefault)); - EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOffByDefault)); - EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOffByDefault)); + EXPECT_TRUE(feature_list->CheckFeatureIdentity(kFeatureOnByDefault)); + EXPECT_TRUE(feature_list->CheckFeatureIdentity(kFeatureOnByDefault)); + EXPECT_TRUE(feature_list->CheckFeatureIdentity(kFeatureOffByDefault)); + EXPECT_TRUE(feature_list->CheckFeatureIdentity(kFeatureOffByDefault)); // Now, call it with a distinct struct for |kFeatureOnByDefaultName|, which // should return false. struct Feature kFeatureOnByDefault2 { kFeatureOnByDefaultName, FEATURE_ENABLED_BY_DEFAULT }; - EXPECT_FALSE(feature_list()->CheckFeatureIdentity(kFeatureOnByDefault2)); + EXPECT_FALSE(feature_list->CheckFeatureIdentity(kFeatureOnByDefault2)); } TEST_F(FeatureListTest, FieldTrialOverrides) { @@ -150,10 +143,8 @@ TEST_F(FeatureListTest, FieldTrialOverrides) { const auto& test_case = test_cases[i]; SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]", i)); - ClearFeatureListInstance(); - FieldTrialList field_trial_list(nullptr); - std::unique_ptr<FeatureList> feature_list(new FeatureList); + auto feature_list = std::make_unique<FeatureList>(); FieldTrial* trial1 = FieldTrialList::CreateFieldTrial("TrialExample1", "A"); FieldTrial* trial2 = FieldTrialList::CreateFieldTrial("TrialExample2", "B"); @@ -161,7 +152,8 @@ TEST_F(FeatureListTest, FieldTrialOverrides) { test_case.trial1_state, trial1); feature_list->RegisterFieldTrialOverride(kFeatureOffByDefaultName, test_case.trial2_state, trial2); - RegisterFeatureListInstance(std::move(feature_list)); + test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatureList(std::move(feature_list)); // Initially, neither trial should be active. EXPECT_FALSE(FieldTrialList::IsTrialActive(trial1->trial_name())); @@ -185,7 +177,7 @@ TEST_F(FeatureListTest, FieldTrialOverrides) { TEST_F(FeatureListTest, FieldTrialAssociateUseDefault) { FieldTrialList field_trial_list(nullptr); - std::unique_ptr<FeatureList> feature_list(new FeatureList); + auto feature_list = std::make_unique<FeatureList>(); FieldTrial* trial1 = FieldTrialList::CreateFieldTrial("TrialExample1", "A"); FieldTrial* trial2 = FieldTrialList::CreateFieldTrial("TrialExample2", "B"); @@ -193,7 +185,8 @@ TEST_F(FeatureListTest, FieldTrialAssociateUseDefault) { kFeatureOnByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial1); feature_list->RegisterFieldTrialOverride( kFeatureOffByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial2); - RegisterFeatureListInstance(std::move(feature_list)); + test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatureList(std::move(feature_list)); // Initially, neither trial should be active. EXPECT_FALSE(FieldTrialList::IsTrialActive(trial1->trial_name())); @@ -213,10 +206,8 @@ TEST_F(FeatureListTest, FieldTrialAssociateUseDefault) { } TEST_F(FeatureListTest, CommandLineEnableTakesPrecedenceOverFieldTrial) { - ClearFeatureListInstance(); - FieldTrialList field_trial_list(nullptr); - std::unique_ptr<FeatureList> feature_list(new FeatureList); + auto feature_list = std::make_unique<FeatureList>(); // The feature is explicitly enabled on the command-line. feature_list->InitializeFromCommandLine(kFeatureOffByDefaultName, ""); @@ -225,7 +216,8 @@ TEST_F(FeatureListTest, CommandLineEnableTakesPrecedenceOverFieldTrial) { FieldTrial* trial = FieldTrialList::CreateFieldTrial("TrialExample2", "A"); feature_list->RegisterFieldTrialOverride( kFeatureOffByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE, trial); - RegisterFeatureListInstance(std::move(feature_list)); + test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatureList(std::move(feature_list)); EXPECT_FALSE(FieldTrialList::IsTrialActive(trial->trial_name())); // Command-line should take precedence. @@ -237,10 +229,8 @@ TEST_F(FeatureListTest, CommandLineEnableTakesPrecedenceOverFieldTrial) { } TEST_F(FeatureListTest, CommandLineDisableTakesPrecedenceOverFieldTrial) { - ClearFeatureListInstance(); - FieldTrialList field_trial_list(nullptr); - std::unique_ptr<FeatureList> feature_list(new FeatureList); + auto feature_list = std::make_unique<FeatureList>(); // The feature is explicitly disabled on the command-line. feature_list->InitializeFromCommandLine("", kFeatureOffByDefaultName); @@ -249,7 +239,8 @@ TEST_F(FeatureListTest, CommandLineDisableTakesPrecedenceOverFieldTrial) { FieldTrial* trial = FieldTrialList::CreateFieldTrial("TrialExample2", "A"); feature_list->RegisterFieldTrialOverride( kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE, trial); - RegisterFeatureListInstance(std::move(feature_list)); + test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatureList(std::move(feature_list)); EXPECT_FALSE(FieldTrialList::IsTrialActive(trial->trial_name())); // Command-line should take precedence. @@ -261,10 +252,8 @@ TEST_F(FeatureListTest, CommandLineDisableTakesPrecedenceOverFieldTrial) { } TEST_F(FeatureListTest, IsFeatureOverriddenFromCommandLine) { - ClearFeatureListInstance(); - FieldTrialList field_trial_list(nullptr); - std::unique_ptr<FeatureList> feature_list(new FeatureList); + auto feature_list = std::make_unique<FeatureList>(); // No features are overridden from the command line yet EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine( @@ -304,7 +293,8 @@ TEST_F(FeatureListTest, IsFeatureOverriddenFromCommandLine) { kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE)); EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine( kFeatureOnByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE)); - RegisterFeatureListInstance(std::move(feature_list)); + test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatureList(std::move(feature_list)); // Check the expected feature states for good measure. EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOffByDefault)); @@ -336,10 +326,8 @@ TEST_F(FeatureListTest, AssociateReportingFieldTrial) { test_case.enable_features, test_case.disable_features)); - ClearFeatureListInstance(); - FieldTrialList field_trial_list(nullptr); - std::unique_ptr<FeatureList> feature_list(new FeatureList); + auto feature_list = std::make_unique<FeatureList>(); feature_list->InitializeFromCommandLine(test_case.enable_features, test_case.disable_features); @@ -364,7 +352,8 @@ TEST_F(FeatureListTest, AssociateReportingFieldTrial) { EXPECT_EQ(test_case.expected_enable_trial_created, enable_trial != nullptr); EXPECT_EQ(test_case.expected_disable_trial_created, disable_trial != nullptr); - RegisterFeatureListInstance(std::move(feature_list)); + test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatureList(std::move(feature_list)); EXPECT_FALSE(FieldTrialList::IsTrialActive(kTrialName)); if (disable_trial) { @@ -380,8 +369,6 @@ TEST_F(FeatureListTest, AssociateReportingFieldTrial) { } TEST_F(FeatureListTest, RegisterExtraFeatureOverrides) { - ClearFeatureListInstance(); - auto feature_list = std::make_unique<FeatureList>(); std::vector<FeatureList::FeatureOverrideInfo> overrides; overrides.push_back({std::cref(kFeatureOnByDefault), @@ -389,15 +376,14 @@ TEST_F(FeatureListTest, RegisterExtraFeatureOverrides) { overrides.push_back({std::cref(kFeatureOffByDefault), FeatureList::OverrideState::OVERRIDE_ENABLE_FEATURE}); feature_list->RegisterExtraFeatureOverrides(std::move(overrides)); - RegisterFeatureListInstance(std::move(feature_list)); + test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatureList(std::move(feature_list)); EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOnByDefault)); EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOffByDefault)); } TEST_F(FeatureListTest, InitializeFromCommandLineThenRegisterExtraOverrides) { - ClearFeatureListInstance(); - auto feature_list = std::make_unique<FeatureList>(); feature_list->InitializeFromCommandLine(kFeatureOnByDefaultName, kFeatureOffByDefaultName); @@ -407,7 +393,8 @@ TEST_F(FeatureListTest, InitializeFromCommandLineThenRegisterExtraOverrides) { overrides.push_back({std::cref(kFeatureOffByDefault), FeatureList::OverrideState::OVERRIDE_ENABLE_FEATURE}); feature_list->RegisterExtraFeatureOverrides(std::move(overrides)); - RegisterFeatureListInstance(std::move(feature_list)); + test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatureList(std::move(feature_list)); // The InitializeFromCommandLine supersedes the RegisterExtraFeatureOverrides // because it was called first. @@ -423,9 +410,8 @@ TEST_F(FeatureListTest, InitializeFromCommandLineThenRegisterExtraOverrides) { } TEST_F(FeatureListTest, GetFeatureOverrides) { - ClearFeatureListInstance(); FieldTrialList field_trial_list(nullptr); - std::unique_ptr<FeatureList> feature_list(new FeatureList); + auto feature_list = std::make_unique<FeatureList>(); feature_list->InitializeFromCommandLine("A,X", "D"); Feature feature_b = {"B", FEATURE_ENABLED_BY_DEFAULT}; @@ -442,7 +428,8 @@ TEST_F(FeatureListTest, GetFeatureOverrides) { FeatureList::OVERRIDE_ENABLE_FEATURE, trial); - RegisterFeatureListInstance(std::move(feature_list)); + test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatureList(std::move(feature_list)); std::string enable_features; std::string disable_features; @@ -458,16 +445,16 @@ TEST_F(FeatureListTest, GetFeatureOverrides) { } TEST_F(FeatureListTest, GetFeatureOverrides_UseDefault) { - ClearFeatureListInstance(); FieldTrialList field_trial_list(nullptr); - std::unique_ptr<FeatureList> feature_list(new FeatureList); + auto feature_list = std::make_unique<FeatureList>(); feature_list->InitializeFromCommandLine("A,X", "D"); FieldTrial* trial = FieldTrialList::CreateFieldTrial("Trial", "Group"); feature_list->RegisterFieldTrialOverride( kFeatureOffByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial); - RegisterFeatureListInstance(std::move(feature_list)); + test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatureList(std::move(feature_list)); std::string enable_features; std::string disable_features; @@ -478,25 +465,25 @@ TEST_F(FeatureListTest, GetFeatureOverrides_UseDefault) { } TEST_F(FeatureListTest, GetFieldTrial) { - ClearFeatureListInstance(); FieldTrialList field_trial_list(nullptr); FieldTrial* trial = FieldTrialList::CreateFieldTrial("Trial", "Group"); - std::unique_ptr<FeatureList> feature_list(new FeatureList); + auto feature_list = std::make_unique<FeatureList>(); feature_list->RegisterFieldTrialOverride( kFeatureOnByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial); - RegisterFeatureListInstance(std::move(feature_list)); + test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatureList(std::move(feature_list)); EXPECT_EQ(trial, FeatureList::GetFieldTrial(kFeatureOnByDefault)); EXPECT_EQ(nullptr, FeatureList::GetFieldTrial(kFeatureOffByDefault)); } TEST_F(FeatureListTest, InitializeFromCommandLine_WithFieldTrials) { - ClearFeatureListInstance(); FieldTrialList field_trial_list(nullptr); FieldTrialList::CreateFieldTrial("Trial", "Group"); - std::unique_ptr<FeatureList> feature_list(new FeatureList); + auto feature_list = std::make_unique<FeatureList>(); feature_list->InitializeFromCommandLine("A,OffByDefault<Trial,X", "D"); - RegisterFeatureListInstance(std::move(feature_list)); + test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatureList(std::move(feature_list)); EXPECT_FALSE(FieldTrialList::IsTrialActive("Trial")); EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOffByDefault)); @@ -504,14 +491,14 @@ TEST_F(FeatureListTest, InitializeFromCommandLine_WithFieldTrials) { } TEST_F(FeatureListTest, InitializeFromCommandLine_UseDefault) { - ClearFeatureListInstance(); FieldTrialList field_trial_list(nullptr); FieldTrialList::CreateFieldTrial("T1", "Group"); FieldTrialList::CreateFieldTrial("T2", "Group"); - std::unique_ptr<FeatureList> feature_list(new FeatureList); + auto feature_list = std::make_unique<FeatureList>(); feature_list->InitializeFromCommandLine( "A,*OffByDefault<T1,*OnByDefault<T2,X", "D"); - RegisterFeatureListInstance(std::move(feature_list)); + test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatureList(std::move(feature_list)); EXPECT_FALSE(FieldTrialList::IsTrialActive("T1")); EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault)); @@ -523,10 +510,10 @@ TEST_F(FeatureListTest, InitializeFromCommandLine_UseDefault) { } TEST_F(FeatureListTest, InitializeInstance) { - ClearFeatureListInstance(); - std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList); - FeatureList::SetInstance(std::move(feature_list)); + test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatureList(std::move(feature_list)); + EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault)); EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault)); @@ -542,7 +529,9 @@ TEST_F(FeatureListTest, InitializeInstance) { } TEST_F(FeatureListTest, UninitializedInstance_IsEnabledReturnsFalse) { - ClearFeatureListInstance(); + std::unique_ptr<FeatureList> original_feature_list = + FeatureList::ClearInstanceForTesting(); + // This test case simulates the calling pattern found in code which does not // explicitly initialize the features list. // All IsEnabled() calls should return the default value in this scenario. @@ -550,6 +539,9 @@ TEST_F(FeatureListTest, UninitializedInstance_IsEnabledReturnsFalse) { EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault)); EXPECT_EQ(nullptr, FeatureList::GetInstance()); EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault)); + + if (original_feature_list) + FeatureList::RestoreInstanceForTesting(std::move(original_feature_list)); } TEST_F(FeatureListTest, StoreAndRetrieveFeaturesFromSharedMemory) { diff --git a/chromium/base/file_version_info_win.cc b/chromium/base/file_version_info_win.cc index c45765dba5d..8b8e5c6b5c3 100644 --- a/chromium/base/file_version_info_win.cc +++ b/chromium/base/file_version_info_win.cc @@ -7,6 +7,8 @@ #include <windows.h> #include <stddef.h> +#include <utility> + #include "base/files/file_path.h" #include "base/logging.h" #include "base/memory/ptr_util.h" @@ -15,8 +17,6 @@ #include "base/threading/scoped_blocking_call.h" #include "base/win/resource_util.h" -using base::FilePath; - namespace { struct LanguageAndCodePage { @@ -24,26 +24,23 @@ struct LanguageAndCodePage { WORD code_page; }; -// Returns the \\VarFileInfo\\Translation value extracted from the +// Returns the \VarFileInfo\Translation value extracted from the // VS_VERSION_INFO resource in |data|. LanguageAndCodePage* GetTranslate(const void* data) { - LanguageAndCodePage* translate = nullptr; - UINT length; - if (::VerQueryValue(data, L"\\VarFileInfo\\Translation", - reinterpret_cast<void**>(&translate), &length)) { - return translate; - } + static constexpr wchar_t kTranslation[] = L"\\VarFileInfo\\Translation"; + LPVOID translate = nullptr; + UINT dummy_size; + if (::VerQueryValue(data, kTranslation, &translate, &dummy_size)) + return static_cast<LanguageAndCodePage*>(translate); return nullptr; } -VS_FIXEDFILEINFO* GetVsFixedFileInfo(const void* data) { - VS_FIXEDFILEINFO* fixed_file_info = nullptr; - UINT length; - if (::VerQueryValue(data, L"\\", reinterpret_cast<void**>(&fixed_file_info), - &length)) { - return fixed_file_info; - } - return nullptr; +const VS_FIXEDFILEINFO& GetVsFixedFileInfo(const void* data) { + static constexpr wchar_t kRoot[] = L"\\"; + LPVOID fixed_file_info = nullptr; + UINT dummy_size; + CHECK(::VerQueryValue(data, kRoot, &fixed_file_info, &dummy_size)); + return *static_cast<VS_FIXEDFILEINFO*>(fixed_file_info); } } // namespace @@ -70,13 +67,13 @@ FileVersionInfo::CreateFileVersionInfoForModule(HMODULE module) { // static std::unique_ptr<FileVersionInfo> FileVersionInfo::CreateFileVersionInfo( - const FilePath& file_path) { + const base::FilePath& file_path) { return FileVersionInfoWin::CreateFileVersionInfoWin(file_path); } // static std::unique_ptr<FileVersionInfoWin> -FileVersionInfoWin::CreateFileVersionInfoWin(const FilePath& file_path) { +FileVersionInfoWin::CreateFileVersionInfoWin(const base::FilePath& file_path) { base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, base::BlockingType::MAY_BLOCK); @@ -140,47 +137,46 @@ base::string16 FileVersionInfoWin::special_build() { } bool FileVersionInfoWin::GetValue(const base::char16* name, - base::string16* value_str) { - WORD lang_codepage[8]; - size_t i = 0; - // Use the language and codepage from the DLL. - lang_codepage[i++] = language_; - lang_codepage[i++] = code_page_; - // Use the default language and codepage from the DLL. - lang_codepage[i++] = ::GetUserDefaultLangID(); - lang_codepage[i++] = code_page_; - // Use the language from the DLL and Latin codepage (most common). - lang_codepage[i++] = language_; - lang_codepage[i++] = 1252; - // Use the default language and Latin codepage (most common). - lang_codepage[i++] = ::GetUserDefaultLangID(); - lang_codepage[i++] = 1252; - - i = 0; - while (i < base::size(lang_codepage)) { + base::string16* value) const { + const struct LanguageAndCodePage lang_codepages[] = { + // Use the language and codepage from the DLL. + {language_, code_page_}, + // Use the default language and codepage from the DLL. + {::GetUserDefaultLangID(), code_page_}, + // Use the language from the DLL and Latin codepage (most common). + {language_, 1252}, + // Use the default language and Latin codepage (most common). + {::GetUserDefaultLangID(), 1252}, + }; + + for (const auto& lang_codepage : lang_codepages) { wchar_t sub_block[MAX_PATH]; - WORD language = lang_codepage[i++]; - WORD code_page = lang_codepage[i++]; _snwprintf_s(sub_block, MAX_PATH, MAX_PATH, - L"\\StringFileInfo\\%04x%04x\\%ls", language, code_page, - base::as_wcstr(name)); - LPVOID value = NULL; + L"\\StringFileInfo\\%04x%04x\\%ls", lang_codepage.language, + lang_codepage.code_page, base::as_wcstr(name)); + LPVOID value_ptr = nullptr; uint32_t size; - BOOL r = ::VerQueryValue(data_, sub_block, &value, &size); - if (r && value) { - value_str->assign(static_cast<base::char16*>(value)); + BOOL r = ::VerQueryValue(data_, sub_block, &value_ptr, &size); + if (r && value_ptr && size) { + value->assign(static_cast<base::char16*>(value_ptr), size - 1); return true; } } return false; } -base::string16 FileVersionInfoWin::GetStringValue(const base::char16* name) { +base::string16 FileVersionInfoWin::GetStringValue( + const base::char16* name) const { base::string16 str; - if (GetValue(name, &str)) - return str; - else - return base::string16(); + GetValue(name, &str); + return str; +} + +base::Version FileVersionInfoWin::GetFileVersion() const { + return base::Version({HIWORD(fixed_file_info_.dwFileVersionMS), + LOWORD(fixed_file_info_.dwFileVersionMS), + HIWORD(fixed_file_info_.dwFileVersionLS), + LOWORD(fixed_file_info_.dwFileVersionLS)}); } FileVersionInfoWin::FileVersionInfoWin(std::vector<uint8_t>&& data, diff --git a/chromium/base/file_version_info_win.h b/chromium/base/file_version_info_win.h index 7c83dafc147..f0dbde87972 100644 --- a/chromium/base/file_version_info_win.h +++ b/chromium/base/file_version_info_win.h @@ -16,6 +16,7 @@ #include "base/base_export.h" #include "base/file_version_info.h" #include "base/macros.h" +#include "base/version.h" struct tagVS_FIXEDFILEINFO; typedef tagVS_FIXEDFILEINFO VS_FIXEDFILEINFO; @@ -37,15 +38,16 @@ class BASE_EXPORT FileVersionInfoWin : public FileVersionInfo { base::string16 file_description() override; base::string16 file_version() override; - // Lets you access other properties not covered above. - bool GetValue(const base::char16* name, base::string16* value); + // Lets you access other properties not covered above. |value| is only + // modified if GetValue() returns true. + bool GetValue(const base::char16* name, base::string16* value) const; // Similar to GetValue but returns a string16 (empty string if the property // does not exist). - base::string16 GetStringValue(const base::char16* name); + base::string16 GetStringValue(const base::char16* name) const; - // Get the fixed file info if it exists. Otherwise NULL - const VS_FIXEDFILEINFO* fixed_file_info() const { return fixed_file_info_; } + // Get file version number in dotted version format. + base::Version GetFileVersion() const; // Behaves like CreateFileVersionInfo, but returns a FileVersionInfoWin. static std::unique_ptr<FileVersionInfoWin> CreateFileVersionInfoWin( @@ -66,8 +68,8 @@ class BASE_EXPORT FileVersionInfoWin : public FileVersionInfo { const WORD language_; const WORD code_page_; - // This is a pointer into |data_| if it exists. Otherwise nullptr. - const VS_FIXEDFILEINFO* const fixed_file_info_; + // This is a reference for a portion of |data_|. + const VS_FIXEDFILEINFO& fixed_file_info_; DISALLOW_COPY_AND_ASSIGN(FileVersionInfoWin); }; diff --git a/chromium/base/file_version_info_win_unittest.cc b/chromium/base/file_version_info_win_unittest.cc index 7a761abcae1..6c81bb7cb30 100644 --- a/chromium/base/file_version_info_win_unittest.cc +++ b/chromium/base/file_version_info_win_unittest.cc @@ -152,4 +152,15 @@ TYPED_TEST(FileVersionInfoTest, CustomProperties) { version_info_win->GetValue(STRING16_LITERAL("Unknown property"), &str)); EXPECT_EQ(base::string16(), version_info_win->GetStringValue( STRING16_LITERAL("Unknown property"))); + + EXPECT_EQ(base::Version(std::vector<uint32_t>{1, 0, 0, 1}), + version_info_win->GetFileVersion()); +} + +TYPED_TEST(FileVersionInfoTest, NoVersionInfo) { + FilePath dll_path = GetTestDataPath(); + dll_path = dll_path.AppendASCII("no_version_info.dll"); + + TypeParam factory(dll_path); + ASSERT_FALSE(factory.Create()); } diff --git a/chromium/base/files/file.cc b/chromium/base/files/file.cc index e8934b1cdc3..da0b52ef48a 100644 --- a/chromium/base/files/file.cc +++ b/chromium/base/files/file.cc @@ -6,6 +6,7 @@ #include "base/files/file_path.h" #include "base/files/file_tracing.h" #include "base/metrics/histogram.h" +#include "base/numerics/safe_conversions.h" #include "base/timer/elapsed_timer.h" #include "build/build_config.h" @@ -96,6 +97,29 @@ void File::Initialize(const FilePath& path, uint32_t flags) { } #endif +bool File::ReadAndCheck(int64_t offset, span<uint8_t> data) { + int size = checked_cast<int>(data.size()); + return Read(offset, reinterpret_cast<char*>(data.data()), size) == size; +} + +bool File::ReadAtCurrentPosAndCheck(span<uint8_t> data) { + int size = checked_cast<int>(data.size()); + return ReadAtCurrentPos(reinterpret_cast<char*>(data.data()), size) == size; +} + +bool File::WriteAndCheck(int64_t offset, span<const uint8_t> data) { + int size = checked_cast<int>(data.size()); + return Write(offset, reinterpret_cast<const char*>(data.data()), size) == + size; +} + +bool File::WriteAtCurrentPosAndCheck(span<const uint8_t> data) { + int size = checked_cast<int>(data.size()); + return WriteAtCurrentPos(reinterpret_cast<const char*>(data.data()), size) == + size; +} + +// static std::string File::ErrorToString(Error error) { switch (error) { case FILE_OK: diff --git a/chromium/base/files/file.h b/chromium/base/files/file.h index cc157a49e97..d61c9f43b7b 100644 --- a/chromium/base/files/file.h +++ b/chromium/base/files/file.h @@ -10,10 +10,10 @@ #include <string> #include "base/base_export.h" +#include "base/containers/span.h" #include "base/files/file_path.h" #include "base/files/file_tracing.h" #include "base/files/platform_file.h" -#include "base/files/scoped_file.h" #include "base/macros.h" #include "base/time/time.h" #include "build/build_config.h" @@ -201,6 +201,14 @@ class BASE_EXPORT File { // (relative to the start) or -1 in case of error. int64_t Seek(Whence whence, int64_t offset); + // Simplified versions of Read() and friends (see below) that check the int + // return value and just return a boolean. They return true if and only if + // the function read in / wrote out exactly |size| bytes of data. + bool ReadAndCheck(int64_t offset, span<uint8_t> data); + bool ReadAtCurrentPosAndCheck(span<uint8_t> data); + bool WriteAndCheck(int64_t offset, span<const uint8_t> data); + bool WriteAtCurrentPosAndCheck(span<const uint8_t> data); + // Reads the given number of bytes (or until EOF is reached) starting with the // given offset. Returns the number of bytes read, or -1 on error. Note that // this function makes a best effort to read all data on all platforms, so it diff --git a/chromium/base/files/file_path_watcher_unittest.cc b/chromium/base/files/file_path_watcher_unittest.cc index 35ee50294ca..91e116d2075 100644 --- a/chromium/base/files/file_path_watcher_unittest.cc +++ b/chromium/base/files/file_path_watcher_unittest.cc @@ -399,7 +399,7 @@ TEST_F(FilePathWatcherTest, DisappearingDirectory) { std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector())); ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get(), false)); - ASSERT_TRUE(base::DeleteFile(dir, true)); + ASSERT_TRUE(base::DeleteFileRecursively(dir)); ASSERT_TRUE(WaitForEvents()); } diff --git a/chromium/base/files/file_posix.cc b/chromium/base/files/file_posix.cc index 00ce7dee428..f8bdf1b4fd9 100644 --- a/chromium/base/files/file_posix.cc +++ b/chromium/base/files/file_posix.cc @@ -130,6 +130,13 @@ void File::Info::FromStat(const stat_wrapper_t& stat_info) { is_symbolic_link = S_ISLNK(stat_info.st_mode); size = stat_info.st_size; + // Get last modification time, last access time, and creation time from + // |stat_info|. + // Note: st_ctime is actually last status change time when the inode was last + // updated, which happens on any metadata change. It is not the file's + // creation time. However, other than on Mac & iOS where the actual file + // creation time is included as st_birthtime, the rest of POSIX platforms have + // no portable way to get the creation time. #if defined(OS_LINUX) || defined(OS_FUCHSIA) time_t last_modified_sec = stat_info.st_mtim.tv_sec; int64_t last_modified_nsec = stat_info.st_mtim.tv_nsec; @@ -144,7 +151,14 @@ void File::Info::FromStat(const stat_wrapper_t& stat_info) { int64_t last_accessed_nsec = stat_info.st_atime_nsec; time_t creation_time_sec = stat_info.st_ctime; int64_t creation_time_nsec = stat_info.st_ctime_nsec; -#elif defined(OS_MACOSX) || defined(OS_IOS) || defined(OS_BSD) +#elif defined(OS_MACOSX) || defined(OS_IOS) + time_t last_modified_sec = stat_info.st_mtimespec.tv_sec; + int64_t last_modified_nsec = stat_info.st_mtimespec.tv_nsec; + time_t last_accessed_sec = stat_info.st_atimespec.tv_sec; + int64_t last_accessed_nsec = stat_info.st_atimespec.tv_nsec; + time_t creation_time_sec = stat_info.st_birthtimespec.tv_sec; + int64_t creation_time_nsec = stat_info.st_birthtimespec.tv_nsec; +#elif defined(OS_BSD) time_t last_modified_sec = stat_info.st_mtimespec.tv_sec; int64_t last_modified_nsec = stat_info.st_mtimespec.tv_nsec; time_t last_accessed_sec = stat_info.st_atimespec.tv_sec; diff --git a/chromium/base/files/file_unittest.cc b/chromium/base/files/file_unittest.cc index 6d536538ce0..799883cfc8a 100644 --- a/chromium/base/files/file_unittest.cc +++ b/chromium/base/files/file_unittest.cc @@ -432,6 +432,29 @@ TEST(FileTest, DISABLED_TouchGetInfo) { creation_time.ToInternalValue()); } +// Test we can retrieve the file's creation time through File::GetInfo(). +TEST(FileTest, GetInfoForCreationTime) { + int64_t before_creation_time_s = + base::Time::Now().ToDeltaSinceWindowsEpoch().InSeconds(); + + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + FilePath file_path = temp_dir.GetPath().AppendASCII("test_file"); + File file(file_path, base::File::FLAG_CREATE | base::File::FLAG_READ | + base::File::FLAG_WRITE); + EXPECT_TRUE(file.IsValid()); + + int64_t after_creation_time_s = + base::Time::Now().ToDeltaSinceWindowsEpoch().InSeconds(); + + base::File::Info info; + EXPECT_TRUE(file.GetInfo(&info)); + EXPECT_GE(info.creation_time.ToDeltaSinceWindowsEpoch().InSeconds(), + before_creation_time_s); + EXPECT_LE(info.creation_time.ToDeltaSinceWindowsEpoch().InSeconds(), + after_creation_time_s); +} + TEST(FileTest, ReadAtCurrentPosition) { base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); diff --git a/chromium/base/files/file_util.h b/chromium/base/files/file_util.h index 59fbf44e6e0..571f90f8eff 100644 --- a/chromium/base/files/file_util.h +++ b/chromium/base/files/file_util.h @@ -63,13 +63,28 @@ BASE_EXPORT int64_t ComputeDirectorySize(const FilePath& root_path); // Returns true if successful, false otherwise. It is considered successful // to attempt to delete a file that does not exist. // -// In posix environment and if |path| is a symbolic link, this deletes only +// In POSIX environment and if |path| is a symbolic link, this deletes only // the symlink. (even if the symlink points to a non-existent file) // // WARNING: USING THIS WITH recursive==true IS EQUIVALENT // TO "rm -rf", SO USE WITH CAUTION. +// +// Note: The |recursive| parameter is in the process of being removed. Use +// DeleteFileRecursively() instead. See https://crbug.com/1009837 BASE_EXPORT bool DeleteFile(const FilePath& path, bool recursive); +// Deletes the given path, whether it's a file or a directory. +// If it's a directory, it's perfectly happy to delete all of the +// directory's contents, including subdirectories and their contents. +// Returns true if successful, false otherwise. It is considered successful +// to attempt to delete a file that does not exist. +// +// In POSIX environment and if |path| is a symbolic link, this deletes only +// the symlink. (even if the symlink points to a non-existent file) +// +// WARNING: USING THIS EQUIVALENT TO "rm -rf", SO USE WITH CAUTION. +BASE_EXPORT bool DeleteFileRecursively(const FilePath& path); + #if defined(OS_WIN) // Schedules to delete the given path, whether it's a file or a directory, until // the operating system is restarted. diff --git a/chromium/base/files/file_util_posix.cc b/chromium/base/files/file_util_posix.cc index 5743863ed89..bedcc78877a 100644 --- a/chromium/base/files/file_util_posix.cc +++ b/chromium/base/files/file_util_posix.cc @@ -43,6 +43,7 @@ #include "base/system/sys_info.h" #include "base/threading/scoped_blocking_call.h" #include "base/time/time.h" +#include "build/branding_buildflags.h" #include "build/build_config.h" #if defined(OS_MACOSX) @@ -134,7 +135,7 @@ std::string TempFileName() { return StringPrintf(".%s.XXXXXX", base::mac::BaseBundleID()); #endif -#if defined(GOOGLE_CHROME_BUILD) +#if BUILDFLAG(GOOGLE_CHROME_BRANDING) return std::string(".com.google.Chrome.XXXXXX"); #else return std::string(".org.chromium.Chromium.XXXXXX"); @@ -319,37 +320,12 @@ bool DoCopyDirectory(const FilePath& from_path, return true; } -#endif // !defined(OS_NACL_NONSFI) - -#if !defined(OS_MACOSX) -// Appends |mode_char| to |mode| before the optional character set encoding; see -// https://www.gnu.org/software/libc/manual/html_node/Opening-Streams.html for -// details. -std::string AppendModeCharacter(StringPiece mode, char mode_char) { - std::string result(mode.as_string()); - size_t comma_pos = result.find(','); - result.insert(comma_pos == std::string::npos ? result.length() : comma_pos, 1, - mode_char); - return result; -} -#endif - -} // namespace - -#if !defined(OS_NACL_NONSFI) -FilePath MakeAbsoluteFilePath(const FilePath& input) { - ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK); - char full_path[PATH_MAX]; - if (realpath(input.value().c_str(), full_path) == nullptr) - return FilePath(); - return FilePath(full_path); -} // TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*" // which works both with and without the recursive flag. I'm not sure we need // that functionality. If not, remove from file_util_win.cc, otherwise add it // here. -bool DeleteFile(const FilePath& path, bool recursive) { +bool DoDeleteFile(const FilePath& path, bool recursive) { ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK); #if defined(OS_ANDROID) @@ -389,6 +365,39 @@ bool DeleteFile(const FilePath& path, bool recursive) { } return success; } +#endif // !defined(OS_NACL_NONSFI) + +#if !defined(OS_MACOSX) +// Appends |mode_char| to |mode| before the optional character set encoding; see +// https://www.gnu.org/software/libc/manual/html_node/Opening-Streams.html for +// details. +std::string AppendModeCharacter(StringPiece mode, char mode_char) { + std::string result(mode.as_string()); + size_t comma_pos = result.find(','); + result.insert(comma_pos == std::string::npos ? result.length() : comma_pos, 1, + mode_char); + return result; +} +#endif + +} // namespace + +#if !defined(OS_NACL_NONSFI) +FilePath MakeAbsoluteFilePath(const FilePath& input) { + ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK); + char full_path[PATH_MAX]; + if (realpath(input.value().c_str(), full_path) == nullptr) + return FilePath(); + return FilePath(full_path); +} + +bool DeleteFile(const FilePath& path, bool recursive) { + return DoDeleteFile(path, recursive); +} + +bool DeleteFileRecursively(const FilePath& path) { + return DoDeleteFile(path, /*recursive=*/true); +} bool ReplaceFile(const FilePath& from_path, const FilePath& to_path, @@ -1172,7 +1181,7 @@ bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path) { if (!CopyDirectory(from_path, to_path, true)) return false; - DeleteFile(from_path, true); + DeleteFileRecursively(from_path); return true; } diff --git a/chromium/base/files/file_util_unittest.cc b/chromium/base/files/file_util_unittest.cc index fc6e5ed57f0..d79ec1fd2e7 100644 --- a/chromium/base/files/file_util_unittest.cc +++ b/chromium/base/files/file_util_unittest.cc @@ -1015,7 +1015,7 @@ TEST_F(FileUtilTest, ChangeDirectoryPermissionsAndEnumerate) { EXPECT_EQ(1, c2.size()); // Delete the file. - EXPECT_TRUE(DeleteFile(subdir_path, true)); + EXPECT_TRUE(DeleteFileRecursively(subdir_path)); EXPECT_FALSE(PathExists(subdir_path)); } @@ -1395,7 +1395,7 @@ TEST_F(FileUtilTest, DeleteNonExistent) { EXPECT_TRUE(DeleteFile(non_existent, false)); ASSERT_FALSE(PathExists(non_existent)); - EXPECT_TRUE(DeleteFile(non_existent, true)); + EXPECT_TRUE(DeleteFileRecursively(non_existent)); ASSERT_FALSE(PathExists(non_existent)); } @@ -1406,7 +1406,7 @@ TEST_F(FileUtilTest, DeleteNonExistentWithNonExistentParent) { EXPECT_TRUE(DeleteFile(non_existent, false)); ASSERT_FALSE(PathExists(non_existent)); - EXPECT_TRUE(DeleteFile(non_existent, true)); + EXPECT_TRUE(DeleteFileRecursively(non_existent)); ASSERT_FALSE(PathExists(non_existent)); } @@ -1426,7 +1426,7 @@ TEST_F(FileUtilTest, DeleteFile) { ASSERT_TRUE(PathExists(file_name)); // Make sure it's deleted - EXPECT_TRUE(DeleteFile(file_name, true)); + EXPECT_TRUE(DeleteFileRecursively(file_name)); EXPECT_FALSE(PathExists(file_name)); } @@ -1482,7 +1482,7 @@ TEST_F(FileUtilTest, DeleteWildCard) { EXPECT_TRUE(PathExists(subdir_path)); // Delete recursively and make sure all contents are deleted - EXPECT_TRUE(DeleteFile(directory_contents, true)); + EXPECT_TRUE(DeleteFileRecursively(directory_contents)); EXPECT_FALSE(PathExists(file_name)); EXPECT_FALSE(PathExists(subdir_path)); } @@ -1504,7 +1504,7 @@ TEST_F(FileUtilTest, DeleteNonExistantWildCard) { EXPECT_TRUE(PathExists(subdir_path)); // Delete recursively and check nothing got deleted - EXPECT_TRUE(DeleteFile(directory_contents, true)); + EXPECT_TRUE(DeleteFileRecursively(directory_contents)); EXPECT_TRUE(PathExists(subdir_path)); } #endif @@ -1560,11 +1560,11 @@ TEST_F(FileUtilTest, DeleteDirRecursive) { ASSERT_TRUE(PathExists(subdir_path2)); // Delete recursively and check that the empty dir got deleted - EXPECT_TRUE(DeleteFile(subdir_path2, true)); + EXPECT_TRUE(DeleteFileRecursively(subdir_path2)); EXPECT_FALSE(PathExists(subdir_path2)); // Delete recursively and check that everything got deleted - EXPECT_TRUE(DeleteFile(test_subdir, true)); + EXPECT_TRUE(DeleteFileRecursively(test_subdir)); EXPECT_FALSE(PathExists(file_name)); EXPECT_FALSE(PathExists(subdir_path1)); EXPECT_FALSE(PathExists(test_subdir)); @@ -1609,7 +1609,7 @@ TEST_F(FileUtilTest, DeleteDirRecursiveWithOpenFile) { // Delete recursively and check that at least the second file got deleted. // This ensures that un-deletable files don't impact those that can be. - DeleteFile(test_subdir, true); + DeleteFileRecursively(test_subdir); EXPECT_FALSE(PathExists(file_name2)); #if defined(OS_LINUX) @@ -2750,7 +2750,7 @@ TEST_F(FileUtilTest, CreateDirectoryTest) { EXPECT_TRUE(PathExists(test_path)); EXPECT_FALSE(CreateDirectory(test_path)); - EXPECT_TRUE(DeleteFile(test_root, true)); + EXPECT_TRUE(DeleteFileRecursively(test_root)); EXPECT_FALSE(PathExists(test_root)); EXPECT_FALSE(PathExists(test_path)); @@ -2797,7 +2797,7 @@ TEST_F(FileUtilTest, DetectDirectoryTest) { EXPECT_FALSE(DirectoryExists(test_path)); EXPECT_TRUE(DeleteFile(test_path, false)); - EXPECT_TRUE(DeleteFile(test_root, true)); + EXPECT_TRUE(DeleteFileRecursively(test_root)); } TEST_F(FileUtilTest, FileEnumeratorTest) { @@ -2954,13 +2954,13 @@ TEST_F(FileUtilTest, AppendToFile) { // Create a fresh, empty copy of this directory. if (PathExists(data_dir)) { - ASSERT_TRUE(DeleteFile(data_dir, true)); + ASSERT_TRUE(DeleteFileRecursively(data_dir)); } ASSERT_TRUE(CreateDirectory(data_dir)); // Create a fresh, empty copy of this directory. if (PathExists(data_dir)) { - ASSERT_TRUE(DeleteFile(data_dir, true)); + ASSERT_TRUE(DeleteFileRecursively(data_dir)); } ASSERT_TRUE(CreateDirectory(data_dir)); FilePath foobar(data_dir.Append(FILE_PATH_LITERAL("foobar.txt"))); @@ -3470,7 +3470,7 @@ TEST_F(FileUtilTest, TouchFile) { // Create a fresh, empty copy of this directory. if (PathExists(data_dir)) { - ASSERT_TRUE(DeleteFile(data_dir, true)); + ASSERT_TRUE(DeleteFileRecursively(data_dir)); } ASSERT_TRUE(CreateDirectory(data_dir)); diff --git a/chromium/base/files/file_util_win.cc b/chromium/base/files/file_util_win.cc index 94cff1ee29f..4d47144d0c4 100644 --- a/chromium/base/files/file_util_win.cc +++ b/chromium/base/files/file_util_win.cc @@ -348,18 +348,7 @@ DWORD DoDeleteFile(const FilePath& path, bool recursive) { : ReturnLastErrorOrSuccessOnNotFound(); } -} // namespace - -FilePath MakeAbsoluteFilePath(const FilePath& input) { - ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK); - char16 file_path[MAX_PATH]; - if (!_wfullpath(as_writable_wcstr(file_path), as_wcstr(input.value()), - MAX_PATH)) - return FilePath(); - return FilePath(file_path); -} - -bool DeleteFile(const FilePath& path, bool recursive) { +bool DeleteFileAndRecordMetrics(const FilePath& path, bool recursive) { static constexpr char kRecursive[] = "DeleteFile.Recursive"; static constexpr char kNonRecursive[] = "DeleteFile.NonRecursive"; const StringPiece operation(recursive ? kRecursive : kNonRecursive); @@ -379,6 +368,25 @@ bool DeleteFile(const FilePath& path, bool recursive) { return false; } +} // namespace + +FilePath MakeAbsoluteFilePath(const FilePath& input) { + ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK); + char16 file_path[MAX_PATH]; + if (!_wfullpath(as_writable_wcstr(file_path), as_wcstr(input.value()), + MAX_PATH)) + return FilePath(); + return FilePath(file_path); +} + +bool DeleteFile(const FilePath& path, bool recursive) { + return DeleteFileAndRecordMetrics(path, recursive); +} + +bool DeleteFileRecursively(const FilePath& path) { + return DeleteFileAndRecordMetrics(path, /*recursive=*/true); +} + bool DeleteFileAfterReboot(const FilePath& path) { ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK); @@ -1025,7 +1033,7 @@ bool CopyAndDeleteDirectory(const FilePath& from_path, const FilePath& to_path) { ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK); if (CopyDirectory(from_path, to_path, true)) { - if (DeleteFile(from_path, true)) + if (DeleteFileRecursively(from_path)) return true; // Like Move, this function is not transactional, so we just diff --git a/chromium/base/files/scoped_temp_dir.cc b/chromium/base/files/scoped_temp_dir.cc index 01ec0f0caab..359b442579e 100644 --- a/chromium/base/files/scoped_temp_dir.cc +++ b/chromium/base/files/scoped_temp_dir.cc @@ -29,7 +29,7 @@ bool ScopedTempDir::CreateUniqueTempDir() { // This "scoped_dir" prefix is only used on Windows and serves as a template // for the unique name. - if (!base::CreateNewTempDirectory(kScopedDirPrefix, &path_)) + if (!CreateNewTempDirectory(kScopedDirPrefix, &path_)) return false; return true; @@ -40,11 +40,11 @@ bool ScopedTempDir::CreateUniqueTempDirUnderPath(const FilePath& base_path) { return false; // If |base_path| does not exist, create it. - if (!base::CreateDirectory(base_path)) + if (!CreateDirectory(base_path)) return false; // Create a new, uniquely named directory under |base_path|. - if (!base::CreateTemporaryDirInDir(base_path, kScopedDirPrefix, &path_)) + if (!CreateTemporaryDirInDir(base_path, kScopedDirPrefix, &path_)) return false; return true; @@ -54,7 +54,7 @@ bool ScopedTempDir::Set(const FilePath& path) { if (!path_.empty()) return false; - if (!DirectoryExists(path) && !base::CreateDirectory(path)) + if (!DirectoryExists(path) && !CreateDirectory(path)) return false; path_ = path; @@ -65,7 +65,7 @@ bool ScopedTempDir::Delete() { if (path_.empty()) return false; - bool ret = base::DeleteFile(path_, true); + bool ret = DeleteFileRecursively(path_); if (ret) { // We only clear the path if deleted the directory. path_.clear(); diff --git a/chromium/base/files/scoped_temp_dir_unittest.cc b/chromium/base/files/scoped_temp_dir_unittest.cc index 024b438aa01..122d138bdb7 100644 --- a/chromium/base/files/scoped_temp_dir_unittest.cc +++ b/chromium/base/files/scoped_temp_dir_unittest.cc @@ -14,8 +14,7 @@ namespace base { TEST(ScopedTempDir, FullPath) { FilePath test_path; - base::CreateNewTempDirectory(FILE_PATH_LITERAL("scoped_temp_dir"), - &test_path); + CreateNewTempDirectory(FILE_PATH_LITERAL("scoped_temp_dir"), &test_path); // Against an existing dir, it should get destroyed when leaving scope. EXPECT_TRUE(DirectoryExists(test_path)); @@ -56,7 +55,7 @@ TEST(ScopedTempDir, TempDir) { test_path = dir.GetPath(); EXPECT_TRUE(DirectoryExists(test_path)); FilePath tmp_dir; - EXPECT_TRUE(base::GetTempDir(&tmp_dir)); + EXPECT_TRUE(GetTempDir(&tmp_dir)); EXPECT_TRUE(test_path.value().find(tmp_dir.value()) != std::string::npos); } EXPECT_FALSE(DirectoryExists(test_path)); @@ -65,8 +64,8 @@ TEST(ScopedTempDir, TempDir) { TEST(ScopedTempDir, UniqueTempDirUnderPath) { // Create a path which will contain a unique temp path. FilePath base_path; - ASSERT_TRUE(base::CreateNewTempDirectory(FILE_PATH_LITERAL("base_dir"), - &base_path)); + ASSERT_TRUE( + CreateNewTempDirectory(FILE_PATH_LITERAL("base_dir"), &base_path)); FilePath test_path; { @@ -78,7 +77,7 @@ TEST(ScopedTempDir, UniqueTempDirUnderPath) { EXPECT_TRUE(test_path.value().find(base_path.value()) != std::string::npos); } EXPECT_FALSE(DirectoryExists(test_path)); - base::DeleteFile(base_path, true); + DeleteFileRecursively(base_path); } TEST(ScopedTempDir, MultipleInvocations) { @@ -99,10 +98,10 @@ TEST(ScopedTempDir, MultipleInvocations) { TEST(ScopedTempDir, LockedTempDir) { ScopedTempDir dir; EXPECT_TRUE(dir.CreateUniqueTempDir()); - base::File file(dir.GetPath().Append(FILE_PATH_LITERAL("temp")), - base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); + File file(dir.GetPath().Append(FILE_PATH_LITERAL("temp")), + File::FLAG_CREATE_ALWAYS | File::FLAG_WRITE); EXPECT_TRUE(file.IsValid()); - EXPECT_EQ(base::File::FILE_OK, file.error_details()); + EXPECT_EQ(File::FILE_OK, file.error_details()); EXPECT_FALSE(dir.Delete()); // We should not be able to delete. EXPECT_FALSE(dir.GetPath().empty()); // We should still have a valid path. file.Close(); diff --git a/chromium/base/fuchsia/filtered_service_directory.h b/chromium/base/fuchsia/filtered_service_directory.h index 8b4bce395b6..dc1205423d8 100644 --- a/chromium/base/fuchsia/filtered_service_directory.h +++ b/chromium/base/fuchsia/filtered_service_directory.h @@ -18,7 +18,7 @@ namespace base { namespace fuchsia { -// ServiceDirectory that uses the supplied ServiceDirectoryClient to satisfy +// ServiceDirectory that uses the supplied sys::ServiceDirectory to satisfy // requests for only a restricted set of services. class BASE_EXPORT FilteredServiceDirectory { public: diff --git a/chromium/base/fuchsia/scoped_service_binding.h b/chromium/base/fuchsia/scoped_service_binding.h index ea11d0272b7..fe37cbeedae 100644 --- a/chromium/base/fuchsia/scoped_service_binding.h +++ b/chromium/base/fuchsia/scoped_service_binding.h @@ -152,7 +152,7 @@ class ScopedSingleClientServiceBinding dispatcher); } - void OnBindingEmpty() { + void OnBindingEmpty(zx_status_t status) { binding_.set_error_handler(nullptr); std::move(on_last_client_callback_).Run(); } diff --git a/chromium/base/fuchsia/scoped_service_binding_unittest.cc b/chromium/base/fuchsia/scoped_service_binding_unittest.cc index ddbea039e2a..4368055cad4 100644 --- a/chromium/base/fuchsia/scoped_service_binding_unittest.cc +++ b/chromium/base/fuchsia/scoped_service_binding_unittest.cc @@ -106,5 +106,21 @@ TEST_F(ScopedServiceBindingTest, ConnectDebugService) { VerifyTestInterface(&release_stub, ZX_ERR_PEER_CLOSED); } +TEST_F(ScopedServiceBindingTest, SingleBindingSetOnLastClientCallback) { + service_binding_.reset(); + ScopedSingleClientServiceBinding<testfidl::TestInterface> + single_service_binding(outgoing_directory_.get(), &test_service_); + + base::RunLoop run_loop; + single_service_binding.SetOnLastClientCallback(run_loop.QuitClosure()); + + auto current_client = + public_service_directory_->Connect<testfidl::TestInterface>(); + VerifyTestInterface(¤t_client, ZX_OK); + current_client.Unbind(); + + run_loop.Run(); +} + } // namespace fuchsia } // namespace base diff --git a/chromium/base/fuchsia/service_directory_test_base.h b/chromium/base/fuchsia/service_directory_test_base.h index 3ff6006627e..8cfbeb3aa06 100644 --- a/chromium/base/fuchsia/service_directory_test_base.h +++ b/chromium/base/fuchsia/service_directory_test_base.h @@ -31,9 +31,8 @@ class ServiceDirectoryTestBase : public testing::Test { protected: const RunLoop::ScopedRunTimeoutForTest run_timeout_; - base::test::TaskEnvironment task_environment_{ - base::test::TaskEnvironment::ThreadingMode::MAIN_THREAD_ONLY, - base::test::TaskEnvironment::MainThreadType::IO}; + base::test::SingleThreadTaskEnvironment task_environment_{ + base::test::SingleThreadTaskEnvironment::MainThreadType::IO}; std::unique_ptr<sys::OutgoingDirectory> outgoing_directory_; TestInterfaceImpl test_service_; diff --git a/chromium/base/fuchsia/service_provider_impl_unittest.cc b/chromium/base/fuchsia/service_provider_impl_unittest.cc index dcb8c0b7690..bc77af08065 100644 --- a/chromium/base/fuchsia/service_provider_impl_unittest.cc +++ b/chromium/base/fuchsia/service_provider_impl_unittest.cc @@ -55,9 +55,8 @@ class ServiceProviderImplTest : public testing::Test { } protected: - base::test::TaskEnvironment task_environment_{ - base::test::TaskEnvironment::ThreadingMode::MAIN_THREAD_ONLY, - base::test::TaskEnvironment::MainThreadType::IO}; + base::test::SingleThreadTaskEnvironment task_environment_{ + base::test::SingleThreadTaskEnvironment::MainThreadType::IO}; TestInterfaceImpl test_service_; sys::OutgoingDirectory service_directory_; diff --git a/chromium/base/hash/sha1.cc b/chromium/base/hash/sha1.cc index 1a809d1cb46..4528d0bea8f 100644 --- a/chromium/base/hash/sha1.cc +++ b/chromium/base/hash/sha1.cc @@ -29,10 +29,6 @@ namespace base { // // to reuse the instance of sha, call sha.Init(); -// TODO(jhawkins): Replace this implementation with a per-platform -// implementation using each platform's crypto library. See -// http://crbug.com/47218 - class SecureHashAlgorithm { public: SecureHashAlgorithm() { Init(); } @@ -191,6 +187,12 @@ void SecureHashAlgorithm::Process() { cursor = 0; } +std::array<uint8_t, kSHA1Length> SHA1HashSpan(span<const uint8_t> data) { + std::array<uint8_t, kSHA1Length> hash; + SHA1HashBytes(data.data(), data.size(), hash.data()); + return hash; +} + std::string SHA1HashString(const std::string& str) { char hash[SecureHashAlgorithm::kDigestSizeBytes]; SHA1HashBytes(reinterpret_cast<const unsigned char*>(str.c_str()), diff --git a/chromium/base/hash/sha1.h b/chromium/base/hash/sha1.h index 525137eab3b..dc0d59f153e 100644 --- a/chromium/base/hash/sha1.h +++ b/chromium/base/hash/sha1.h @@ -7,9 +7,11 @@ #include <stddef.h> +#include <array> #include <string> #include "base/base_export.h" +#include "base/containers/span.h" namespace base { @@ -17,6 +19,10 @@ namespace base { enum { kSHA1Length = 20 }; // Length in bytes of a SHA-1 hash. +// Computes the SHA-1 hash of the input |data| and returns the full hash. +BASE_EXPORT std::array<uint8_t, kSHA1Length> SHA1HashSpan( + span<const uint8_t> data); + // Computes the SHA-1 hash of the input string |str| and returns the full // hash. BASE_EXPORT std::string SHA1HashString(const std::string& str); diff --git a/chromium/base/hash/sha1_boringssl.cc b/chromium/base/hash/sha1_boringssl.cc index 53eafbc84db..7e9d604fb34 100644 --- a/chromium/base/hash/sha1_boringssl.cc +++ b/chromium/base/hash/sha1_boringssl.cc @@ -12,17 +12,24 @@ namespace base { -void SHA1HashBytes(const unsigned char* data, size_t len, unsigned char* hash) { +std::array<uint8_t, kSHA1Length> SHA1HashSpan(span<const uint8_t> data) { CRYPTO_library_init(); - SHA1(data, len, hash); + std::array<uint8_t, kSHA1Length> digest; + SHA1(data.data(), data.size(), digest.data()); + return digest; } std::string SHA1HashString(const std::string& str) { CRYPTO_library_init(); std::string digest; SHA1(reinterpret_cast<const uint8_t*>(str.data()), str.size(), - reinterpret_cast<uint8_t*>(base::WriteInto(&digest, kSHA1Length + 1))); + reinterpret_cast<uint8_t*>(WriteInto(&digest, kSHA1Length + 1))); return digest; } +void SHA1HashBytes(const unsigned char* data, size_t len, unsigned char* hash) { + CRYPTO_library_init(); + SHA1(data, len, hash); +} + } // namespace base diff --git a/chromium/base/hash/sha1_unittest.cc b/chromium/base/hash/sha1_unittest.cc index 3d69ef6c3cf..f87221dd5af 100644 --- a/chromium/base/hash/sha1_unittest.cc +++ b/chromium/base/hash/sha1_unittest.cc @@ -14,12 +14,14 @@ TEST(SHA1Test, Test1) { // Example A.1 from FIPS 180-2: one-block message. std::string input = "abc"; - int expected[] = {0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, 0xba, 0x3e, - 0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d}; + static constexpr int kExpected[] = {0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, + 0x6a, 0xba, 0x3e, 0x25, 0x71, 0x78, 0x50, + 0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d}; std::string output = base::SHA1HashString(input); + ASSERT_EQ(base::kSHA1Length, output.size()); for (size_t i = 0; i < base::kSHA1Length; i++) - EXPECT_EQ(expected[i], output[i] & 0xFF); + EXPECT_EQ(kExpected[i], output[i] & 0xFF); } TEST(SHA1Test, Test2) { @@ -27,68 +29,87 @@ TEST(SHA1Test, Test2) { std::string input = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; - int expected[] = {0x84, 0x98, 0x3e, 0x44, 0x1c, 0x3b, 0xd2, 0x6e, 0xba, 0xae, - 0x4a, 0xa1, 0xf9, 0x51, 0x29, 0xe5, 0xe5, 0x46, 0x70, 0xf1}; + static constexpr int kExpected[] = {0x84, 0x98, 0x3e, 0x44, 0x1c, 0x3b, 0xd2, + 0x6e, 0xba, 0xae, 0x4a, 0xa1, 0xf9, 0x51, + 0x29, 0xe5, 0xe5, 0x46, 0x70, 0xf1}; std::string output = base::SHA1HashString(input); + ASSERT_EQ(base::kSHA1Length, output.size()); for (size_t i = 0; i < base::kSHA1Length; i++) - EXPECT_EQ(expected[i], output[i] & 0xFF); + EXPECT_EQ(kExpected[i], output[i] & 0xFF); } TEST(SHA1Test, Test3) { // Example A.3 from FIPS 180-2: long message. std::string input(1000000, 'a'); - int expected[] = {0x34, 0xaa, 0x97, 0x3c, 0xd4, 0xc4, 0xda, 0xa4, 0xf6, 0x1e, - 0xeb, 0x2b, 0xdb, 0xad, 0x27, 0x31, 0x65, 0x34, 0x01, 0x6f}; + static constexpr int kExpected[] = {0x34, 0xaa, 0x97, 0x3c, 0xd4, 0xc4, 0xda, + 0xa4, 0xf6, 0x1e, 0xeb, 0x2b, 0xdb, 0xad, + 0x27, 0x31, 0x65, 0x34, 0x01, 0x6f}; std::string output = base::SHA1HashString(input); + ASSERT_EQ(base::kSHA1Length, output.size()); for (size_t i = 0; i < base::kSHA1Length; i++) - EXPECT_EQ(expected[i], output[i] & 0xFF); + EXPECT_EQ(kExpected[i], output[i] & 0xFF); } -TEST(SHA1Test, Test1Bytes) { +TEST(SHA1Test, Test1BytesAndSpan) { // Example A.1 from FIPS 180-2: one-block message. std::string input = "abc"; unsigned char output[base::kSHA1Length]; - unsigned char expected[] = {0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, - 0x6a, 0xba, 0x3e, 0x25, 0x71, 0x78, 0x50, - 0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d}; + static constexpr unsigned char kExpected[] = { + 0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, 0xba, 0x3e, + 0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d}; base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(input.c_str()), - input.length(), output); + input.size(), output); for (size_t i = 0; i < base::kSHA1Length; i++) - EXPECT_EQ(expected[i], output[i]); + EXPECT_EQ(kExpected[i], output[i]); + + std::array<uint8_t, base::kSHA1Length> output_array = + base::SHA1HashSpan(base::as_bytes(base::make_span(input))); + for (size_t i = 0; i < base::kSHA1Length; i++) + EXPECT_EQ(kExpected[i], output_array[i]); } -TEST(SHA1Test, Test2Bytes) { +TEST(SHA1Test, Test2BytesAndSpan) { // Example A.2 from FIPS 180-2: multi-block message. std::string input = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; unsigned char output[base::kSHA1Length]; - unsigned char expected[] = {0x84, 0x98, 0x3e, 0x44, 0x1c, 0x3b, 0xd2, - 0x6e, 0xba, 0xae, 0x4a, 0xa1, 0xf9, 0x51, - 0x29, 0xe5, 0xe5, 0x46, 0x70, 0xf1}; + static constexpr unsigned char kExpected[] = { + 0x84, 0x98, 0x3e, 0x44, 0x1c, 0x3b, 0xd2, 0x6e, 0xba, 0xae, + 0x4a, 0xa1, 0xf9, 0x51, 0x29, 0xe5, 0xe5, 0x46, 0x70, 0xf1}; base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(input.c_str()), - input.length(), output); + input.size(), output); + for (size_t i = 0; i < base::kSHA1Length; i++) + EXPECT_EQ(kExpected[i], output[i]); + + std::array<uint8_t, base::kSHA1Length> output_array = + base::SHA1HashSpan(base::as_bytes(base::make_span(input))); for (size_t i = 0; i < base::kSHA1Length; i++) - EXPECT_EQ(expected[i], output[i]); + EXPECT_EQ(kExpected[i], output_array[i]); } -TEST(SHA1Test, Test3Bytes) { +TEST(SHA1Test, Test3BytesAndSpan) { // Example A.3 from FIPS 180-2: long message. std::string input(1000000, 'a'); unsigned char output[base::kSHA1Length]; - unsigned char expected[] = {0x34, 0xaa, 0x97, 0x3c, 0xd4, 0xc4, 0xda, - 0xa4, 0xf6, 0x1e, 0xeb, 0x2b, 0xdb, 0xad, - 0x27, 0x31, 0x65, 0x34, 0x01, 0x6f}; + static constexpr unsigned char kExpected[] = { + 0x34, 0xaa, 0x97, 0x3c, 0xd4, 0xc4, 0xda, 0xa4, 0xf6, 0x1e, + 0xeb, 0x2b, 0xdb, 0xad, 0x27, 0x31, 0x65, 0x34, 0x01, 0x6f}; base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(input.c_str()), - input.length(), output); + input.size(), output); + for (size_t i = 0; i < base::kSHA1Length; i++) + EXPECT_EQ(kExpected[i], output[i]); + + std::array<uint8_t, base::kSHA1Length> output_array = + base::SHA1HashSpan(base::as_bytes(base::make_span(input))); for (size_t i = 0; i < base::kSHA1Length; i++) - EXPECT_EQ(expected[i], output[i]); + EXPECT_EQ(kExpected[i], output_array[i]); } diff --git a/chromium/base/i18n/icu_util.cc b/chromium/base/i18n/icu_util.cc index f4942878550..38d39108dc5 100644 --- a/chromium/base/i18n/icu_util.cc +++ b/chromium/base/i18n/icu_util.cc @@ -37,10 +37,6 @@ #include "base/mac/foundation_util.h" #endif -#if defined(OS_FUCHSIA) -#include "base/base_paths_fuchsia.h" -#endif - namespace base { namespace i18n { @@ -69,9 +65,11 @@ wchar_t g_debug_icu_pf_filename[_MAX_PATH]; // build pkg configurations, etc). 'l' stands for Little Endian. // This variable is exported through the header file. const char kIcuDataFileName[] = "icudtl.dat"; +const char kIcuExtraDataFileName[] = "icudtl_extra.dat"; + #if defined(OS_ANDROID) -const char kAndroidAssetsIcuDataFileName[] = "assets/icudtl.dat"; -#endif +const char kAssetsPathPrefix[] = "assets/"; +#endif // defined(OS_ANDROID) // File handle intentionally never closed. Not using File here because its // Windows implementation guards against two instances owning the same @@ -79,25 +77,31 @@ const char kAndroidAssetsIcuDataFileName[] = "assets/icudtl.dat"; PlatformFile g_icudtl_pf = kInvalidPlatformFile; MemoryMappedFile* g_icudtl_mapped_file = nullptr; MemoryMappedFile::Region g_icudtl_region; - -void LazyInitIcuDataFile() { - if (g_icudtl_pf != kInvalidPlatformFile) { - return; - } +PlatformFile g_icudtl_extra_pf = kInvalidPlatformFile; +MemoryMappedFile* g_icudtl_extra_mapped_file = nullptr; +MemoryMappedFile::Region g_icudtl_extra_region; + +struct PfRegion { + public: + PlatformFile pf; + MemoryMappedFile::Region region; +}; + +std::unique_ptr<PfRegion> OpenIcuDataFile(const std::string& filename) { + auto result = std::make_unique<PfRegion>(); #if defined(OS_ANDROID) - int fd = - android::OpenApkAsset(kAndroidAssetsIcuDataFileName, &g_icudtl_region); - g_icudtl_pf = fd; - if (fd != -1) { - return; + result->pf = + android::OpenApkAsset(kAssetsPathPrefix + filename, &result->region); + if (result->pf != -1) { + return result; } -// For unit tests, data file is located on disk, so try there as a fallback. #endif // defined(OS_ANDROID) + // For unit tests, data file is located on disk, so try there as a fallback. #if !defined(OS_MACOSX) FilePath data_path; if (!PathService::Get(DIR_ASSETS, &data_path)) { - LOG(ERROR) << "Can't find " << kIcuDataFileName; - return; + LOG(ERROR) << "Can't find " << filename; + return nullptr; } #if defined(OS_WIN) // TODO(brucedawson): http://crbug.com/445616 @@ -105,7 +109,7 @@ void LazyInitIcuDataFile() { wcscpy_s(tmp_buffer, as_wcstr(data_path.value())); debug::Alias(tmp_buffer); #endif - data_path = data_path.AppendASCII(kIcuDataFileName); + data_path = data_path.AppendASCII(filename); #if defined(OS_WIN) // TODO(brucedawson): http://crbug.com/445616 @@ -116,8 +120,7 @@ void LazyInitIcuDataFile() { #else // !defined(OS_MACOSX) // Assume it is in the framework bundle's Resources directory. - ScopedCFTypeRef<CFStringRef> data_file_name( - SysUTF8ToCFStringRef(kIcuDataFileName)); + ScopedCFTypeRef<CFStringRef> data_file_name(SysUTF8ToCFStringRef(filename)); FilePath data_path = mac::PathForFrameworkBundleResource(data_file_name); #if defined(OS_IOS) FilePath override_data_path = ios::FilePathOfEmbeddedICU(); @@ -126,8 +129,8 @@ void LazyInitIcuDataFile() { } #endif // !defined(OS_IOS) if (data_path.empty()) { - LOG(ERROR) << kIcuDataFileName << " not found in bundle"; - return; + LOG(ERROR) << filename << " not found in bundle"; + return nullptr; } #endif // !defined(OS_MACOSX) File file(data_path, File::FLAG_OPEN | File::FLAG_READ); @@ -139,8 +142,8 @@ void LazyInitIcuDataFile() { g_debug_icu_pf_filename[0] = 0; #endif // OS_WIN - g_icudtl_pf = file.TakePlatformFile(); - g_icudtl_region = MemoryMappedFile::Region::kWholeFile; + result->pf = file.TakePlatformFile(); + result->region = MemoryMappedFile::Region::kWholeFile; } #if defined(OS_WIN) else { @@ -150,6 +153,47 @@ void LazyInitIcuDataFile() { wcscpy_s(g_debug_icu_pf_filename, as_wcstr(data_path.value())); } #endif // OS_WIN + + return result; +} + +void LazyOpenIcuDataFile() { + if (g_icudtl_pf != kInvalidPlatformFile) { + return; + } + auto pf_region = OpenIcuDataFile(kIcuDataFileName); + if (!pf_region) { + return; + } + g_icudtl_pf = pf_region->pf; + g_icudtl_region = pf_region->region; +} + +int LoadIcuData(PlatformFile data_fd, + const MemoryMappedFile::Region& data_region, + std::unique_ptr<MemoryMappedFile>* out_mapped_data_file, + UErrorCode* out_error_code) { + if (data_fd == kInvalidPlatformFile) { + LOG(ERROR) << "Invalid file descriptor to ICU data received."; + return 1; // To debug http://crbug.com/445616. + } + + out_mapped_data_file->reset(new MemoryMappedFile()); + if (!(*out_mapped_data_file)->Initialize(File(data_fd), data_region)) { + LOG(ERROR) << "Couldn't mmap icu data file"; + return 2; // To debug http://crbug.com/445616. + } + + (*out_error_code) = U_ZERO_ERROR; + udata_setCommonData(const_cast<uint8_t*>((*out_mapped_data_file)->data()), + out_error_code); + if (U_FAILURE(*out_error_code)) { + LOG(ERROR) << "Failed to initialize ICU with data file: " + << u_errorName(*out_error_code); + return 3; // To debug http://crbug.com/445616. + } + + return 0; } bool InitializeICUWithFileDescriptorInternal( @@ -160,28 +204,20 @@ bool InitializeICUWithFileDescriptorInternal( g_debug_icu_load = 0; // To debug http://crbug.com/445616. return true; } - if (data_fd == kInvalidPlatformFile) { - g_debug_icu_load = 1; // To debug http://crbug.com/445616. - LOG(ERROR) << "Invalid file descriptor to ICU data received."; - return false; - } - std::unique_ptr<MemoryMappedFile> icudtl_mapped_file(new MemoryMappedFile()); - if (!icudtl_mapped_file->Initialize(File(data_fd), data_region)) { - g_debug_icu_load = 2; // To debug http://crbug.com/445616. - LOG(ERROR) << "Couldn't mmap icu data file"; + std::unique_ptr<MemoryMappedFile> mapped_file; + UErrorCode err; + g_debug_icu_load = LoadIcuData(data_fd, data_region, &mapped_file, &err); + if (g_debug_icu_load == 1 || g_debug_icu_load == 2) { return false; } - g_icudtl_mapped_file = icudtl_mapped_file.release(); + g_icudtl_mapped_file = mapped_file.release(); - UErrorCode err = U_ZERO_ERROR; - udata_setCommonData(const_cast<uint8_t*>(g_icudtl_mapped_file->data()), &err); - if (err != U_ZERO_ERROR) { - g_debug_icu_load = 3; // To debug http://crbug.com/445616. + if (g_debug_icu_load == 3) { g_debug_icu_last_error = err; } #if defined(OS_ANDROID) - else { + else if (g_debug_icu_load == 0) { // On Android, we can't leave it up to ICU to set the default timezone // because ICU's timezone detection does not work in many timezones (e.g. // Australia/Sydney, Asia/Seoul, Europe/Paris ). Use JNI to detect the host @@ -195,7 +231,7 @@ bool InitializeICUWithFileDescriptorInternal( #endif // Never try to load ICU data from files. udata_setFileAccess(UDATA_ONLY_PACKAGES, &err); - return err == U_ZERO_ERROR; + return U_SUCCESS(err); } #endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE #endif // !defined(OS_NACL) @@ -204,7 +240,23 @@ bool InitializeICUWithFileDescriptorInternal( #if !defined(OS_NACL) #if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE -#if defined(OS_ANDROID) +bool InitializeExtraICUWithFileDescriptor( + PlatformFile data_fd, + const MemoryMappedFile::Region& data_region) { + if (g_icudtl_pf != kInvalidPlatformFile) { + // Must call InitializeExtraICUWithFileDescriptor() before + // InitializeICUWithFileDescriptor(). + return false; + } + std::unique_ptr<MemoryMappedFile> mapped_file; + UErrorCode err; + if (LoadIcuData(data_fd, data_region, &mapped_file, &err) != 0) { + return false; + } + g_icudtl_extra_mapped_file = mapped_file.release(); + return true; +} + bool InitializeICUWithFileDescriptor( PlatformFile data_fd, const MemoryMappedFile::Region& data_region) { @@ -220,7 +272,14 @@ PlatformFile GetIcuDataFileHandle(MemoryMappedFile::Region* out_region) { *out_region = g_icudtl_region; return g_icudtl_pf; } -#endif + +PlatformFile GetIcuExtraDataFileHandle(MemoryMappedFile::Region* out_region) { + if (g_icudtl_extra_pf == kInvalidPlatformFile) { + return kInvalidPlatformFile; + } + *out_region = g_icudtl_extra_region; + return g_icudtl_extra_pf; +} const uint8_t* GetRawIcuMemory() { CHECK(g_icudtl_mapped_file); @@ -244,7 +303,28 @@ bool InitializeICUFromRawMemory(const uint8_t* raw_memory) { #endif } -#endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE +bool InitializeExtraICU() { + if (g_icudtl_pf != kInvalidPlatformFile) { + // Must call InitializeExtraICU() before InitializeICU(). + return false; + } + auto pf_region = OpenIcuDataFile(kIcuExtraDataFileName); + if (!pf_region) { + return false; + } + g_icudtl_extra_pf = pf_region->pf; + g_icudtl_extra_region = pf_region->region; + std::unique_ptr<MemoryMappedFile> mapped_file; + UErrorCode err; + if (LoadIcuData(g_icudtl_extra_pf, g_icudtl_extra_region, &mapped_file, + &err) != 0) { + return false; + } + g_icudtl_extra_mapped_file = mapped_file.release(); + return true; +} + +#endif // (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE) bool InitializeICU() { #if DCHECK_IS_ON() @@ -261,7 +341,7 @@ bool InitializeICU() { // it is needed. This can fail if the process is sandboxed at that time. // Instead, we map the file in and hand off the data so the sandbox won't // cause any problems. - LazyInitIcuDataFile(); + LazyOpenIcuDataFile(); result = InitializeICUWithFileDescriptorInternal(g_icudtl_pf, g_icudtl_region); #if defined(OS_WIN) @@ -299,5 +379,16 @@ void AllowMultipleInitializeCallsForTesting() { #endif } +#if !defined(OS_NACL) +#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE +void ResetGlobalsForTesting() { + g_icudtl_pf = kInvalidPlatformFile; + g_icudtl_mapped_file = nullptr; + g_icudtl_extra_pf = kInvalidPlatformFile; + g_icudtl_extra_mapped_file = nullptr; +} +#endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE +#endif // !defined(OS_NACL) + } // namespace i18n } // namespace base diff --git a/chromium/base/i18n/icu_util.h b/chromium/base/i18n/icu_util.h index a861ce6c9ac..4324dc577a7 100644 --- a/chromium/base/i18n/icu_util.h +++ b/chromium/base/i18n/icu_util.h @@ -23,18 +23,28 @@ namespace i18n { BASE_I18N_EXPORT bool InitializeICU(); #if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE -#if defined(OS_ANDROID) -// Returns the PlatformFile and Region that was initialized by InitializeICU(). -// Use with InitializeICUWithFileDescriptor(). +// Loads ICU's extra data tables from disk for the current process. If used must +// be called before InitializeICU(). +BASE_I18N_EXPORT bool InitializeExtraICU(); +// Returns the PlatformFile and Region that was initialized by InitializeICU() +// or InitializeExtraICU(). Use with InitializeICUWithFileDescriptor() or +// InitializeExtraICUWithFileDescriptor(). BASE_I18N_EXPORT PlatformFile GetIcuDataFileHandle( MemoryMappedFile::Region* out_region); +BASE_I18N_EXPORT PlatformFile +GetIcuExtraDataFileHandle(MemoryMappedFile::Region* out_region); -// Android uses a file descriptor passed by browser process to initialize ICU -// in render processes. +// Loads ICU extra data file from file descriptor passed by browser process to +// initialize ICU in render processes. If used must be called before +// InitializeICUWithFileDescriptor(). +BASE_I18N_EXPORT bool InitializeExtraICUWithFileDescriptor( + PlatformFile data_fd, + const MemoryMappedFile::Region& data_region); +// Loads ICU data file from file descriptor passed by browser process to +// initialize ICU in render processes. BASE_I18N_EXPORT bool InitializeICUWithFileDescriptor( PlatformFile data_fd, const MemoryMappedFile::Region& data_region); -#endif // Returns a void pointer to the memory mapped ICU data file. // @@ -60,6 +70,12 @@ BASE_I18N_EXPORT bool InitializeICUFromRawMemory(const uint8_t* raw_memory); // In a test binary, the call above might occur twice. BASE_I18N_EXPORT void AllowMultipleInitializeCallsForTesting(); +#if !defined(OS_NACL) +#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE +BASE_I18N_EXPORT void ResetGlobalsForTesting(); +#endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE +#endif // !defined(OS_NACL) + } // namespace i18n } // namespace base diff --git a/chromium/base/i18n/icu_util_unittest.cc b/chromium/base/i18n/icu_util_unittest.cc new file mode 100644 index 00000000000..75b6c4aab2e --- /dev/null +++ b/chromium/base/i18n/icu_util_unittest.cc @@ -0,0 +1,82 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/i18n/icu_util.h" + +#include "build/build_config.h" +#include "testing/gtest/include/gtest/gtest.h" + +#if !defined(OS_NACL) +#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE + +namespace base { +namespace i18n { + +class IcuUtilTest : public testing::Test { + protected: + void SetUp() override { ResetGlobalsForTesting(); } +}; + +#if defined(OS_ANDROID) + +TEST_F(IcuUtilTest, InitializeIcuSucceeds) { + bool success = InitializeICU(); + + ASSERT_TRUE(success); +} + +TEST_F(IcuUtilTest, ExtraFileNotInitializedAtStart) { + MemoryMappedFile::Region region; + PlatformFile file = GetIcuExtraDataFileHandle(®ion); + + ASSERT_EQ(file, kInvalidPlatformFile); +} + +TEST_F(IcuUtilTest, InitializeExtraIcuSucceeds) { + bool success = InitializeExtraICU(); + + ASSERT_TRUE(success); +} + +TEST_F(IcuUtilTest, CannotInitializeExtraIcuAfterIcu) { + InitializeICU(); + bool success = InitializeExtraICU(); + + ASSERT_FALSE(success); +} + +TEST_F(IcuUtilTest, ExtraFileInitializedAfterInit) { + InitializeExtraICU(); + MemoryMappedFile::Region region; + PlatformFile file = GetIcuExtraDataFileHandle(®ion); + + ASSERT_NE(file, kInvalidPlatformFile); +} + +TEST_F(IcuUtilTest, InitializeExtraIcuFromFdSucceeds) { + InitializeExtraICU(); + MemoryMappedFile::Region region; + PlatformFile pf = GetIcuExtraDataFileHandle(®ion); + bool success = InitializeExtraICUWithFileDescriptor(pf, region); + + ASSERT_TRUE(success); +} + +TEST_F(IcuUtilTest, CannotInitializeExtraIcuFromFdAfterIcu) { + InitializeExtraICU(); + InitializeICU(); + MemoryMappedFile::Region region; + PlatformFile pf = GetIcuExtraDataFileHandle(®ion); + bool success = InitializeExtraICUWithFileDescriptor(pf, region); + + ASSERT_FALSE(success); +} + +#endif // defined(OS_ANDROID) + +} // namespace i18n +} // namespace base + +#endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE +#endif // !defined(OS_NACL) diff --git a/chromium/base/immediate_crash.h b/chromium/base/immediate_crash.h index 94ee14f1289..94158fe3ceb 100644 --- a/chromium/base/immediate_crash.h +++ b/chromium/base/immediate_crash.h @@ -101,20 +101,14 @@ #define TRAP_SEQUENCE1_() __asm volatile("brk #0\n") // Intentionally empty: __builtin_unreachable() is always part of the sequence -// (see IMMEDIATE_CRASH below) and already emits a ud2 on Win64 +// (see IMMEDIATE_CRASH below) and already emits a ud2 on Win64, +// https://crbug.com/958373 #define TRAP_SEQUENCE2_() __asm volatile("") #else #define TRAP_SEQUENCE1_() asm volatile("int3") - -#if defined(ARCH_CPU_64_BITS) -// Intentionally empty: __builtin_unreachable() is always part of the sequence -// (see IMMEDIATE_CRASH below) and already emits a ud2 on Win64 -#define TRAP_SEQUENCE2_() asm volatile("") -#else #define TRAP_SEQUENCE2_() asm volatile("ud2") -#endif // defined(ARCH_CPU_64_bits) #endif // __clang__ diff --git a/chromium/base/ios/crb_protocol_observers_unittest.mm b/chromium/base/ios/crb_protocol_observers_unittest.mm index 07f5cff035c..6321ac1378a 100644 --- a/chromium/base/ios/crb_protocol_observers_unittest.mm +++ b/chromium/base/ios/crb_protocol_observers_unittest.mm @@ -5,7 +5,6 @@ #import "base/ios/crb_protocol_observers.h" #include "base/ios/weak_nsobject.h" #include "base/logging.h" -#include "base/mac/scoped_nsautorelease_pool.h" #include "base/mac/scoped_nsobject.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest_mac.h" @@ -122,11 +121,10 @@ TEST_F(CRBProtocolObserversTest, WeakReference) { [observers_ addObserver:partial_observer_]; - { - // Need an autorelease pool here, because - // -[CRBProtocolObservers forwardInvocation:] creates a temporary - // autoreleased array that holds all the observers. - base::mac::ScopedNSAutoreleasePool pool; + // Need an autorelease pool here, because + // -[CRBProtocolObservers forwardInvocation:] creates a temporary + // autoreleased array that holds all the observers. + @autoreleasepool { [observers_ requiredMethod]; EXPECT_TRUE([partial_observer_ requiredMethodInvoked]); } diff --git a/chromium/base/ios/weak_nsobject.mm b/chromium/base/ios/weak_nsobject.mm index c017b1d1877..eaf700f6c4e 100644 --- a/chromium/base/ios/weak_nsobject.mm +++ b/chromium/base/ios/weak_nsobject.mm @@ -4,7 +4,6 @@ #include "base/ios/weak_nsobject.h" -#include "base/mac/scoped_nsautorelease_pool.h" #include "base/mac/scoped_nsobject.h" namespace { @@ -34,23 +33,24 @@ WeakContainer::~WeakContainer() {} + (scoped_refptr<base::WeakContainer>)containerForObject:(id)object { if (object == nil) return nullptr; - // The autoreleasePool is needed here as the call to objc_getAssociatedObject + // The autoreleasepool is needed here as the call to objc_getAssociatedObject // returns an autoreleased object which is better released sooner than later. - base::mac::ScopedNSAutoreleasePool pool; - CRBWeakNSProtocolSentinel* sentinel = - objc_getAssociatedObject(object, &sentinelObserverKey_); - if (!sentinel) { - base::scoped_nsobject<CRBWeakNSProtocolSentinel> newSentinel( - [[CRBWeakNSProtocolSentinel alloc] - initWithContainer:new base::WeakContainer(object)]); - sentinel = newSentinel; - objc_setAssociatedObject(object, &sentinelObserverKey_, sentinel, - OBJC_ASSOCIATION_RETAIN); - // The retain count is 2. One retain is due to the alloc, the other to the - // association with the weak object. - DCHECK_EQ(2u, [sentinel retainCount]); + @autoreleasepool { + CRBWeakNSProtocolSentinel* sentinel = + objc_getAssociatedObject(object, &sentinelObserverKey_); + if (!sentinel) { + base::scoped_nsobject<CRBWeakNSProtocolSentinel> newSentinel( + [[CRBWeakNSProtocolSentinel alloc] + initWithContainer:new base::WeakContainer(object)]); + sentinel = newSentinel; + objc_setAssociatedObject(object, &sentinelObserverKey_, sentinel, + OBJC_ASSOCIATION_RETAIN); + // The retain count is 2. One retain is due to the alloc, the other to the + // association with the weak object. + DCHECK_EQ(2u, [sentinel retainCount]); + } + return [sentinel container]; } - return [sentinel container]; } - (id)initWithContainer:(scoped_refptr<base::WeakContainer>)container { diff --git a/chromium/base/json/json_common.h b/chromium/base/json/json_common.h new file mode 100644 index 00000000000..c0fd3eab82b --- /dev/null +++ b/chromium/base/json/json_common.h @@ -0,0 +1,42 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_JSON_JSON_COMMON_H_ +#define BASE_JSON_JSON_COMMON_H_ + +#include <stddef.h> + +#include "base/logging.h" +#include "base/macros.h" + +namespace base { +namespace internal { + +// Chosen to support 99.9% of documents found in the wild late 2016. +// http://crbug.com/673263 +const size_t kAbsoluteMaxDepth = 200; + +// Simple class that checks for maximum recursion/stack overflow. +class StackMarker { + public: + StackMarker(size_t max_depth, size_t* depth) + : max_depth_(max_depth), depth_(depth) { + ++(*depth_); + DCHECK_LE(*depth_, max_depth_); + } + ~StackMarker() { --(*depth_); } + + bool IsTooDeep() const { return *depth_ >= max_depth_; } + + private: + const size_t max_depth_; + size_t* const depth_; + + DISALLOW_COPY_AND_ASSIGN(StackMarker); +}; + +} // namespace internal +} // namespace base + +#endif // BASE_JSON_JSON_COMMON_H_ diff --git a/chromium/base/json/json_parser.cc b/chromium/base/json/json_parser.cc index 3d9d53d120e..65a4b7dd02e 100644 --- a/chromium/base/json/json_parser.cc +++ b/chromium/base/json/json_parser.cc @@ -26,28 +26,6 @@ namespace internal { namespace { const int32_t kExtendedASCIIStart = 0x80; - -// Simple class that checks for maximum recursion/"stack overflow." -class StackMarker { - public: - StackMarker(int max_depth, int* depth) - : max_depth_(max_depth), depth_(depth) { - ++(*depth_); - DCHECK_LE(*depth_, max_depth_); - } - ~StackMarker() { - --(*depth_); - } - - bool IsTooDeep() const { return *depth_ >= max_depth_; } - - private: - const int max_depth_; - int* const depth_; - - DISALLOW_COPY_AND_ASSIGN(StackMarker); -}; - constexpr uint32_t kUnicodeReplacementPoint = 0xFFFD; } // namespace @@ -55,7 +33,7 @@ constexpr uint32_t kUnicodeReplacementPoint = 0xFFFD; // This is U+FFFD. const char kUnicodeReplacementString[] = "\xEF\xBF\xBD"; -JSONParser::JSONParser(int options, int max_depth) +JSONParser::JSONParser(int options, size_t max_depth) : options_(options), max_depth_(max_depth), index_(0), @@ -65,7 +43,7 @@ JSONParser::JSONParser(int options, int max_depth) error_code_(JSONReader::JSON_NO_ERROR), error_line_(0), error_column_(0) { - CHECK_LE(max_depth, JSONReader::kStackMaxDepth); + CHECK_LE(max_depth, kAbsoluteMaxDepth); } JSONParser::~JSONParser() = default; @@ -385,8 +363,10 @@ Optional<Value> JSONParser::ConsumeDictionary() { } ConsumeChar(); // Closing '}'. - - return Value(Value::DictStorage(std::move(dict_storage), KEEP_LAST_OF_DUPES)); + // Reverse |dict_storage| to keep the last of elements with the same key in + // the input. + std::reverse(dict_storage.begin(), dict_storage.end()); + return Value(Value::DictStorage(std::move(dict_storage))); } Optional<Value> JSONParser::ConsumeList() { diff --git a/chromium/base/json/json_parser.h b/chromium/base/json/json_parser.h index 78523363db9..548fb282700 100644 --- a/chromium/base/json/json_parser.h +++ b/chromium/base/json/json_parser.h @@ -14,6 +14,7 @@ #include "base/base_export.h" #include "base/compiler_specific.h" #include "base/gtest_prod_util.h" +#include "base/json/json_common.h" #include "base/json/json_reader.h" #include "base/macros.h" #include "base/optional.h" @@ -43,7 +44,7 @@ class JSONParserTest; // of the next token. class BASE_EXPORT JSONParser { public: - JSONParser(int options, int max_depth = JSONReader::kStackMaxDepth); + JSONParser(int options, size_t max_depth = kAbsoluteMaxDepth); ~JSONParser(); // Parses the input string according to the set options and returns the @@ -215,7 +216,7 @@ class BASE_EXPORT JSONParser { const int options_; // Maximum depth to parse. - const int max_depth_; + const size_t max_depth_; // The input stream being parsed. Note: Not guaranteed to NUL-terminated. StringPiece input_; @@ -224,7 +225,7 @@ class BASE_EXPORT JSONParser { int index_; // The number of times the parser has recursed (current stack depth). - int stack_depth_; + size_t stack_depth_; // The line number that the parser is at currently. int line_number_; diff --git a/chromium/base/json/json_perftest.cc b/chromium/base/json/json_perftest.cc index 1e0ea33d310..c62f9516dbb 100644 --- a/chromium/base/json/json_perftest.cc +++ b/chromium/base/json/json_perftest.cc @@ -25,10 +25,10 @@ DictionaryValue GenerateDict() { root.SetStringKey("String", "Foo"); ListValue list; - list.GetList().emplace_back(2.718); - list.GetList().emplace_back(false); - list.GetList().emplace_back(123); - list.GetList().emplace_back("Bar"); + list.Append(2.718); + list.Append(false); + list.Append(123); + list.Append("Bar"); root.SetKey("List", std::move(list)); return root; diff --git a/chromium/base/json/json_reader.cc b/chromium/base/json/json_reader.cc index d21eac9d180..43f66416624 100644 --- a/chromium/base/json/json_reader.cc +++ b/chromium/base/json/json_reader.cc @@ -13,10 +13,6 @@ namespace base { -// Chosen to support 99.9% of documents found in the wild late 2016. -// http://crbug.com/673263 -const int JSONReader::kStackMaxDepth = 200; - // Values 1000 and above are used by JSONFileValueSerializer::JsonFileError. static_assert(JSONReader::JSON_PARSE_ERROR_COUNT < 1000, "JSONReader error out of bounds"); @@ -49,20 +45,22 @@ JSONReader::ValueWithError::~ValueWithError() = default; JSONReader::ValueWithError& JSONReader::ValueWithError::operator=( ValueWithError&& other) = default; -JSONReader::JSONReader(int options, int max_depth) +JSONReader::JSONReader(int options, size_t max_depth) : parser_(new internal::JSONParser(options, max_depth)) {} JSONReader::~JSONReader() = default; // static -Optional<Value> JSONReader::Read(StringPiece json, int options, int max_depth) { +Optional<Value> JSONReader::Read(StringPiece json, + int options, + size_t max_depth) { internal::JSONParser parser(options, max_depth); return parser.Parse(json); } std::unique_ptr<Value> JSONReader::ReadDeprecated(StringPiece json, int options, - int max_depth) { + size_t max_depth) { Optional<Value> value = Read(json, options, max_depth); return value ? Value::ToUniquePtrValue(std::move(*value)) : nullptr; } diff --git a/chromium/base/json/json_reader.h b/chromium/base/json/json_reader.h index fb458cfe7c8..3eee07ccc3b 100644 --- a/chromium/base/json/json_reader.h +++ b/chromium/base/json/json_reader.h @@ -32,6 +32,7 @@ #include <string> #include "base/base_export.h" +#include "base/json/json_common.h" #include "base/optional.h" #include "base/strings/string_piece.h" #include "base/values.h" @@ -58,8 +59,6 @@ enum JSONParserOptions { class BASE_EXPORT JSONReader { public: - static const int kStackMaxDepth; - // Error codes during parsing. enum JsonParseError { JSON_NO_ERROR = 0, @@ -105,7 +104,8 @@ class BASE_EXPORT JSONReader { static const char kInputTooLarge[]; // Constructs a reader. - JSONReader(int options = JSON_PARSE_RFC, int max_depth = kStackMaxDepth); + JSONReader(int options = JSON_PARSE_RFC, + size_t max_depth = internal::kAbsoluteMaxDepth); ~JSONReader(); @@ -113,16 +113,17 @@ class BASE_EXPORT JSONReader { // If |json| is not a properly formed JSON string, returns base::nullopt. static Optional<Value> Read(StringPiece json, int options = JSON_PARSE_RFC, - int max_depth = kStackMaxDepth); + size_t max_depth = internal::kAbsoluteMaxDepth); // Deprecated. Use the Read() method above. // Reads and parses |json|, returning a Value. // If |json| is not a properly formed JSON string, returns nullptr. // Wrap this in base::FooValue::From() to check the Value is of type Foo and // convert to a FooValue at the same time. - static std::unique_ptr<Value> ReadDeprecated(StringPiece json, - int options = JSON_PARSE_RFC, - int max_depth = kStackMaxDepth); + static std::unique_ptr<Value> ReadDeprecated( + StringPiece json, + int options = JSON_PARSE_RFC, + size_t max_depth = internal::kAbsoluteMaxDepth); // Reads and parses |json| like Read(). Returns a ValueWithError, which on // error, will be populated with a formatted error message, an error code, and diff --git a/chromium/base/json/json_reader_fuzzer.cc b/chromium/base/json/json_reader_fuzzer.cc index 7bff954e9a1..6be408c7a44 100644 --- a/chromium/base/json/json_reader_fuzzer.cc +++ b/chromium/base/json/json_reader_fuzzer.cc @@ -3,8 +3,11 @@ // found in the LICENSE file. #include "base/json/json_reader.h" +#include "base/json/json_writer.h" #include "base/values.h" +namespace base { + // Entry point for LibFuzzer. extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { if (size < 2) @@ -15,11 +18,26 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { std::unique_ptr<char[]> input(new char[size - 1]); memcpy(input.get(), data, size - 1); - base::StringPiece input_string(input.get(), size - 1); + StringPiece input_string(input.get(), size - 1); const int options = data[size - 1]; - base::JSONReader::ReadAndReturnValueWithError(input_string, options); + JSONReader::ValueWithError json_val = + JSONReader::ReadAndReturnValueWithError(input_string, options); + + if (json_val.value) { + // Check that the value can be serialized and deserialized back to an + // equivalent |Value|. + const Value& value = json_val.value.value(); + std::string serialized; + CHECK(JSONWriter::Write(value, &serialized)); + + Optional<Value> deserialized = JSONReader::Read(StringPiece(serialized)); + CHECK(deserialized); + CHECK(value.Equals(&deserialized.value())); + } return 0; } + +} // namespace base diff --git a/chromium/base/json/json_writer.cc b/chromium/base/json/json_writer.cc index 76a93b2db00..f3d2dc14911 100644 --- a/chromium/base/json/json_writer.cc +++ b/chromium/base/json/json_writer.cc @@ -25,19 +25,20 @@ const char kPrettyPrintLineEnding[] = "\n"; #endif // static -bool JSONWriter::Write(const Value& node, std::string* json) { - return WriteWithOptions(node, 0, json); +bool JSONWriter::Write(const Value& node, std::string* json, size_t max_depth) { + return WriteWithOptions(node, 0, json, max_depth); } // static bool JSONWriter::WriteWithOptions(const Value& node, int options, - std::string* json) { + std::string* json, + size_t max_depth) { json->clear(); // Is there a better way to estimate the size of the output? json->reserve(1024); - JSONWriter writer(options, json); + JSONWriter writer(options, json, max_depth); bool result = writer.BuildJSONString(node, 0U); if (options & OPTIONS_PRETTY_PRINT) @@ -46,16 +47,23 @@ bool JSONWriter::WriteWithOptions(const Value& node, return result; } -JSONWriter::JSONWriter(int options, std::string* json) +JSONWriter::JSONWriter(int options, std::string* json, size_t max_depth) : omit_binary_values_((options & OPTIONS_OMIT_BINARY_VALUES) != 0), omit_double_type_preservation_( (options & OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION) != 0), pretty_print_((options & OPTIONS_PRETTY_PRINT) != 0), - json_string_(json) { + json_string_(json), + max_depth_(max_depth), + stack_depth_(0) { DCHECK(json); + CHECK_LE(max_depth, internal::kAbsoluteMaxDepth); } bool JSONWriter::BuildJSONString(const Value& node, size_t depth) { + internal::StackMarker depth_check(max_depth_, &stack_depth_); + if (depth_check.IsTooDeep()) + return false; + switch (node.type()) { case Value::Type::NONE: json_string_->append("null"); diff --git a/chromium/base/json/json_writer.h b/chromium/base/json/json_writer.h index 57cb8c16a29..d66b46f2863 100644 --- a/chromium/base/json/json_writer.h +++ b/chromium/base/json/json_writer.h @@ -10,6 +10,7 @@ #include <string> #include "base/base_export.h" +#include "base/json/json_common.h" #include "base/macros.h" namespace base { @@ -42,16 +43,21 @@ class BASE_EXPORT JSONWriter { // TODO(tc): Should we generate json if it would be invalid json (e.g., // |node| is not a DictionaryValue/ListValue or if there are inf/-inf float // values)? Return true on success and false on failure. - static bool Write(const Value& node, std::string* json); + static bool Write(const Value& node, + std::string* json, + size_t max_depth = internal::kAbsoluteMaxDepth); // Same as above but with |options| which is a bunch of JSONWriter::Options // bitwise ORed together. Return true on success and false on failure. static bool WriteWithOptions(const Value& node, int options, - std::string* json); + std::string* json, + size_t max_depth = internal::kAbsoluteMaxDepth); private: - JSONWriter(int options, std::string* json); + JSONWriter(int options, + std::string* json, + size_t max_depth = internal::kAbsoluteMaxDepth); // Called recursively to build the JSON string. When completed, // |json_string_| will contain the JSON. @@ -67,6 +73,12 @@ class BASE_EXPORT JSONWriter { // Where we write JSON data as we generate it. std::string* json_string_; + // Maximum depth to write. + const size_t max_depth_; + + // The number of times the writer has recursed (current stack depth). + size_t stack_depth_; + DISALLOW_COPY_AND_ASSIGN(JSONWriter); }; diff --git a/chromium/base/json/json_writer_unittest.cc b/chromium/base/json/json_writer_unittest.cc index 291a225f6ff..81d8e3d4be8 100644 --- a/chromium/base/json/json_writer_unittest.cc +++ b/chromium/base/json/json_writer_unittest.cc @@ -61,9 +61,9 @@ TEST(JSONWriterTest, NestedTypes) { ListValue list; DictionaryValue inner_dict; inner_dict.SetIntKey("inner int", 10); - list.GetList().push_back(std::move(inner_dict)); - list.GetList().emplace_back(Value::Type::LIST); - list.GetList().emplace_back(true); + list.Append(std::move(inner_dict)); + list.Append(Value(Value::Type::LIST)); + list.Append(true); root_dict.SetKey("list", std::move(list)); // Test the pretty-printer. @@ -121,11 +121,11 @@ TEST(JSONWriterTest, BinaryValues) { EXPECT_TRUE(output_js.empty()); ListValue binary_list; - binary_list.GetList().emplace_back(kBufferSpan); - binary_list.GetList().emplace_back(5); - binary_list.GetList().emplace_back(kBufferSpan); - binary_list.GetList().emplace_back(2); - binary_list.GetList().emplace_back(kBufferSpan); + binary_list.Append(Value(kBufferSpan)); + binary_list.Append(5); + binary_list.Append(Value(kBufferSpan)); + binary_list.Append(2); + binary_list.Append(Value(kBufferSpan)); EXPECT_FALSE(JSONWriter::Write(binary_list, &output_js)); EXPECT_TRUE(JSONWriter::WriteWithOptions( binary_list, JSONWriter::OPTIONS_OMIT_BINARY_VALUES, &output_js)); @@ -154,4 +154,36 @@ TEST(JSONWriterTest, DoublesAsInts) { EXPECT_EQ("10000000000", output_js); } +TEST(JSONWriterTest, StackOverflow) { + std::string output_js; + ListValue deep_list; + ListValue* next_list = &deep_list; + + const size_t max_depth = 100000; + + for (size_t i = 0; i < max_depth; ++i) { + ListValue inner_list; + next_list->Append(std::move(inner_list)); + next_list->GetList(0, &next_list); + } + + EXPECT_FALSE(JSONWriter::Write(deep_list, &output_js)); + EXPECT_FALSE(JSONWriter::WriteWithOptions( + deep_list, JSONWriter::OPTIONS_PRETTY_PRINT, &output_js)); + + // We cannot just let deep_list tear down since it + // would cause a stack overflow. Therefore, we tear + // down from the inner lists outwards safely. + const size_t step = 200; + for (size_t i = max_depth - step; i > 0; i -= step) { + next_list = &deep_list; + for (size_t curr_depth = 0; curr_depth < i && next_list; ++curr_depth) { + if (!next_list->GetList(0, &next_list)) + next_list = nullptr; + } + if (next_list) + next_list->Remove(0, nullptr); + } +} + } // namespace base diff --git a/chromium/base/linux_util.cc b/chromium/base/linux_util.cc index caf471a39e1..2da24e1d89b 100644 --- a/chromium/base/linux_util.cc +++ b/chromium/base/linux_util.cc @@ -13,6 +13,7 @@ #include <sys/types.h> #include <unistd.h> +#include <iomanip> #include <memory> #include "base/command_line.h" @@ -28,6 +29,8 @@ #include "base/synchronization/lock.h" #include "build/build_config.h" +namespace base { + namespace { // Not needed for OS_CHROMEOS. @@ -43,7 +46,7 @@ class LinuxDistroHelper { public: // Retrieves the Singleton. static LinuxDistroHelper* GetInstance() { - return base::Singleton<LinuxDistroHelper>::get(); + return Singleton<LinuxDistroHelper>::get(); } // The simple state machine goes from: @@ -55,7 +58,7 @@ class LinuxDistroHelper { // we automatically move to STATE_CHECK_STARTED so nobody else will // do the check. LinuxDistroState State() { - base::AutoLock scoped_lock(lock_); + AutoLock scoped_lock(lock_); if (STATE_DID_NOT_CHECK == state_) { state_ = STATE_CHECK_STARTED; return STATE_DID_NOT_CHECK; @@ -65,21 +68,72 @@ class LinuxDistroHelper { // Indicate the check finished, move to STATE_CHECK_FINISHED. void CheckFinished() { - base::AutoLock scoped_lock(lock_); + AutoLock scoped_lock(lock_); DCHECK_EQ(STATE_CHECK_STARTED, state_); state_ = STATE_CHECK_FINISHED; } private: - base::Lock lock_; + Lock lock_; LinuxDistroState state_; }; + +#if !defined(OS_CHROMEOS) +std::string GetKeyValueFromOSReleaseFile(const std::string& input, + const char* key) { + StringPairs key_value_pairs; + SplitStringIntoKeyValuePairs(input, '=', '\n', &key_value_pairs); + for (const auto& pair : key_value_pairs) { + const std::string& key_str = pair.first; + const std::string& value_str = pair.second; + if (key_str == key) { + // It can contain quoted characters. + std::stringstream ss; + std::string pretty_name; + ss << value_str; + // Quoted with a single tick? + if (value_str[0] == '\'') + ss >> std::quoted(pretty_name, '\''); + else + ss >> std::quoted(pretty_name); + + return pretty_name; + } + } + + return ""; +} + +bool ReadDistroFromOSReleaseFile(const char* file) { + static const char kPrettyName[] = "PRETTY_NAME"; + + std::string os_release_content; + if (!ReadFileToString(FilePath(file), &os_release_content)) + return false; + + std::string pretty_name = + GetKeyValueFromOSReleaseFile(os_release_content, kPrettyName); + if (pretty_name.empty()) + return false; + + SetLinuxDistro(pretty_name); + return true; +} + +// https://www.freedesktop.org/software/systemd/man/os-release.html +void GetDistroNameFromOSRelease() { + static const char* const kFilesToCheck[] = {"/etc/os-release", + "/usr/lib/os-release"}; + for (const char* file : kFilesToCheck) { + if (ReadDistroFromOSReleaseFile(file)) + return; + } +} +#endif // if !defined(OS_CHROMEOS) #endif // if defined(OS_LINUX) } // namespace -namespace base { - // Account for the terminating null character. static const int kDistroSize = 128 + 1; @@ -94,6 +148,21 @@ char g_linux_distro[kDistroSize] = "Unknown"; #endif +// This function is only supposed to be used in tests. The declaration in the +// header file is guarded by "#if defined(UNIT_TEST)" so that they can be used +// by tests but not non-test code. However, this .cc file is compiled as part +// of "base" where "UNIT_TEST" is not defined. So we need to specify +// "BASE_EXPORT" here again so that they are visible to tests. +BASE_EXPORT std::string GetKeyValueFromOSReleaseFileForTesting( + const std::string& input, + const char* key) { +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + return GetKeyValueFromOSReleaseFile(input, key); +#else + return ""; +#endif // defined(OS_LINUX) && !defined(OS_CHROMEOS) +} + std::string GetLinuxDistro() { #if defined(OS_CHROMEOS) || defined(OS_ANDROID) return g_linux_distro; @@ -106,20 +175,8 @@ std::string GetLinuxDistro() { return "Unknown"; // Don't wait for other thread to finish. DCHECK_EQ(state, STATE_DID_NOT_CHECK); // We do this check only once per process. If it fails, there's - // little reason to believe it will work if we attempt to run - // lsb_release again. - std::vector<std::string> argv; - argv.push_back("lsb_release"); - argv.push_back("-d"); - std::string output; - GetAppOutput(CommandLine(argv), &output); - if (output.length() > 0) { - // lsb_release -d should return: Description:<tab>Distro Info - const char field[] = "Description:\t"; - if (output.compare(0, strlen(field), field) == 0) { - SetLinuxDistro(output.substr(strlen(field))); - } - } + // little reason to believe it will work if we attempt to run it again. + GetDistroNameFromOSRelease(); distro_state_singleton->CheckFinished(); return g_linux_distro; #else @@ -137,7 +194,7 @@ void SetLinuxDistro(const std::string& distro) { bool GetThreadsForProcess(pid_t pid, std::vector<pid_t>* tids) { // 25 > strlen("/proc//task") + strlen(std::to_string(INT_MAX)) + 1 = 22 char buf[25]; - base::strings::SafeSPrintf(buf, "/proc/%d/task", pid); + strings::SafeSPrintf(buf, "/proc/%d/task", pid); DirReaderPosix dir_reader(buf); if (!dir_reader.IsValid()) { diff --git a/chromium/base/linux_util.h b/chromium/base/linux_util.h index 568b4e86eac..80cd6e40814 100644 --- a/chromium/base/linux_util.h +++ b/chromium/base/linux_util.h @@ -22,6 +22,14 @@ BASE_EXPORT extern char g_linux_distro[]; // Get the Linux Distro if we can, or return "Unknown". BASE_EXPORT std::string GetLinuxDistro(); +#if defined(UNIT_TEST) +// Get the value of given key from the given input (content of the +// /etc/os-release file. Exposed for testing. +BASE_EXPORT std::string GetKeyValueFromOSReleaseFileForTesting( + const std::string& input, + const char* key); +#endif // defined(UNIT_TEST) + // Set the Linux Distro string. BASE_EXPORT void SetLinuxDistro(const std::string& distro); diff --git a/chromium/base/linux_util_unittest.cc b/chromium/base/linux_util_unittest.cc new file mode 100644 index 00000000000..28d162a8912 --- /dev/null +++ b/chromium/base/linux_util_unittest.cc @@ -0,0 +1,76 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/linux_util.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +const char kPrettyName[] = "PRETTY_NAME"; + +TEST(LinuxUtilTest, ParseEtcOsReleaseFile) { + const char kOsRelease[] = R"X( +NAME=Fedora +VERSION="30 (Workstation Edition\)\" +ID=fedora +VERSION_ID=30 +VERSION_CODENAME="" +PLATFORM_ID="platform:f30 +PRETTY_NAME="Fedora 30 (Workstation Edition)" +ANSI_COLOR="0;34" +LOGO=fedora-logo-icon +CPE_NAME="cpe:/o:fedoraproject:fedora:30" +HOME_URL="https://fedoraproject.org/" +DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/f30/system-administrators-guide/" +SUPPORT_URL="https://fedoraproject.org/wiki/Communicating_and_getting_help" +BUG_REPORT_URL="https://bugzilla.redhat.com/" +REDHAT_BUGZILLA_PRODUCT="Fedora" +REDHAT_BUGZILLA_PRODUCT_VERSION=30 +REDHAT_SUPPORT_PRODUCT="Fedora" +REDHAT_SUPPORT_PRODUCT_VERSION=30 +PRIVACY_POLICY_URL="https://fedoraproject.org/wiki/Legal:PrivacyPolicy" +VARIANT="Workstation Edition" +VARIANT_ID=workstation)X"; + + const char kOsReleaseMissingPrettyName[] = R"( +NAME=Fedora +VERSION='30 (Workstation Edition)' +VARIANT_ID=workstation)"; + + std::string value = + base::GetKeyValueFromOSReleaseFileForTesting(kOsRelease, kPrettyName); + EXPECT_EQ(value, "Fedora 30 (Workstation Edition)"); + // Missing key in the file + value = base::GetKeyValueFromOSReleaseFileForTesting( + kOsReleaseMissingPrettyName, kPrettyName); + EXPECT_EQ(value, ""); + // Value quoted with single ticks + value = base::GetKeyValueFromOSReleaseFileForTesting( + kOsReleaseMissingPrettyName, "VERSION"); + EXPECT_EQ(value, "30 (Workstation Edition)"); + // Empty file + value = base::GetKeyValueFromOSReleaseFileForTesting("", kPrettyName); + EXPECT_EQ(value, ""); + // Misspelled key + value = + base::GetKeyValueFromOSReleaseFileForTesting(kOsRelease, "PRETY_NAME"); + EXPECT_EQ(value, ""); + // Broken key=value format + value = base::GetKeyValueFromOSReleaseFileForTesting("A/B", kPrettyName); + EXPECT_EQ(value, ""); + // Empty values + value = + base::GetKeyValueFromOSReleaseFileForTesting("PRETTY_NAME=", kPrettyName); + EXPECT_EQ(value, ""); + value = base::GetKeyValueFromOSReleaseFileForTesting("PRETTY_NAME=\"\"", + kPrettyName); + EXPECT_EQ(value, ""); + // Only one key=value in the whole file + value = base::GetKeyValueFromOSReleaseFileForTesting("PRETTY_NAME=\"Linux\"", + kPrettyName); + EXPECT_EQ(value, "Linux"); +} + +} // namespace diff --git a/chromium/base/logging.cc b/chromium/base/logging.cc index 04aabb67465..282b26ff4cf 100644 --- a/chromium/base/logging.cc +++ b/chromium/base/logging.cc @@ -858,9 +858,13 @@ LogMessage::~LogMessage() { fx_logger_t* logger = fx_log_get_logger(); if (logger) { - // Temporarily pop the trailing newline, since fx_logger will add one. + // Temporarily remove the trailing newline from |str_newline|'s C-string + // representation, since fx_logger will add a newline of its own. str_newline.pop_back(); - fx_logger_log(logger, severity, nullptr, str_newline.c_str()); + std::string message = + base::StringPrintf("%s(%d) %s", file_basename_, line_, + str_newline.c_str() + message_start_); + fx_logger_log(logger, severity, nullptr, message.data()); str_newline.push_back('\n'); } #endif // OS_FUCHSIA @@ -962,6 +966,9 @@ void LogMessage::Init(const char* file, int line) { if (last_slash_pos != base::StringPiece::npos) filename.remove_prefix(last_slash_pos + 1); + // Stores the base name as the null-terminated suffix substring of |filename|. + file_basename_ = filename.data(); + // TODO(darin): It might be nice if the columns were fixed width. stream_ << '['; diff --git a/chromium/base/logging.h b/chromium/base/logging.h index 772c274e2f1..0713dbdab1e 100644 --- a/chromium/base/logging.h +++ b/chromium/base/logging.h @@ -530,9 +530,9 @@ BASE_EXPORT extern std::ostream* g_swallow_stream; class CheckOpResult { public: // |message| must be non-null if and only if the check failed. - CheckOpResult(std::string* message) : message_(message) {} + constexpr CheckOpResult(std::string* message) : message_(message) {} // Returns true if the check succeeded. - operator bool() const { return !message_; } + constexpr operator bool() const { return !message_; } // Returns the message. std::string* message() { return message_; } @@ -685,20 +685,21 @@ std::string* MakeCheckOpString<std::string, std::string>( // The checked condition is wrapped with ANALYZER_ASSUME_TRUE, which under // static analysis builds, blocks analysis of the current path if the // condition is false. -#define DEFINE_CHECK_OP_IMPL(name, op) \ - template <class t1, class t2> \ - inline std::string* Check##name##Impl(const t1& v1, const t2& v2, \ - const char* names) { \ - if (ANALYZER_ASSUME_TRUE(v1 op v2)) \ - return NULL; \ - else \ - return ::logging::MakeCheckOpString(v1, v2, names); \ - } \ - inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \ - if (ANALYZER_ASSUME_TRUE(v1 op v2)) \ - return NULL; \ - else \ - return ::logging::MakeCheckOpString(v1, v2, names); \ +#define DEFINE_CHECK_OP_IMPL(name, op) \ + template <class t1, class t2> \ + constexpr std::string* Check##name##Impl(const t1& v1, const t2& v2, \ + const char* names) { \ + if (ANALYZER_ASSUME_TRUE(v1 op v2)) \ + return nullptr; \ + else \ + return ::logging::MakeCheckOpString(v1, v2, names); \ + } \ + constexpr std::string* Check##name##Impl(int v1, int v2, \ + const char* names) { \ + if (ANALYZER_ASSUME_TRUE(v1 op v2)) \ + return nullptr; \ + else \ + return ::logging::MakeCheckOpString(v1, v2, names); \ } DEFINE_CHECK_OP_IMPL(EQ, ==) DEFINE_CHECK_OP_IMPL(NE, !=) @@ -920,6 +921,7 @@ class BASE_EXPORT LogMessage { // The file and line information passed in to the constructor. const char* file_; const int line_; + const char* file_basename_; // This is useful since the LogMessage class uses a lot of Win32 calls // that will lose the value of GLE and the code that called the log function diff --git a/chromium/base/logging_unittest.cc b/chromium/base/logging_unittest.cc index cef46410320..14adbfc4f8b 100644 --- a/chromium/base/logging_unittest.cc +++ b/chromium/base/logging_unittest.cc @@ -40,6 +40,7 @@ #include <fuchsia/logger/cpp/fidl.h> #include <fuchsia/logger/cpp/fidl_test_base.h> #include <lib/fidl/cpp/binding.h> +#include <lib/sys/cpp/component_context.h> #include <lib/zx/channel.h> #include <lib/zx/event.h> #include <lib/zx/exception.h> @@ -51,8 +52,8 @@ #include <zircon/syscalls/exception.h> #include <zircon/types.h> +#include "base/fuchsia/default_context.h" #include "base/fuchsia/fuchsia_logging.h" -#include "base/fuchsia/service_directory_client.h" #endif // OS_FUCHSIA namespace logging { @@ -1008,8 +1009,9 @@ TEST_F(LoggingTest, FuchsiaSystemLogging) { std::make_unique<fuchsia::logger::LogFilterOptions>(); options->tags = {"base_unittests__exec"}; fuchsia::logger::LogPtr logger = - base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() - ->ConnectToService<fuchsia::logger::Log>(); + base::fuchsia::ComponentContextForCurrentProcess() + ->svc() + ->Connect<fuchsia::logger::Log>(); logger->DumpLogs(binding.NewBinding(), std::move(options)); listener.RunUntilDone(); } while (!listener.DidReceiveString(kLogMessage, &logged_message)); diff --git a/chromium/base/mac/bind_objc_block_unittest.mm b/chromium/base/mac/bind_objc_block_unittest.mm index 8c4a9892298..485f421be67 100644 --- a/chromium/base/mac/bind_objc_block_unittest.mm +++ b/chromium/base/mac/bind_objc_block_unittest.mm @@ -7,7 +7,6 @@ #include "base/bind.h" #include "base/callback.h" #include "base/callback_helpers.h" -#include "base/mac/scoped_nsautorelease_pool.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest_mac.h" @@ -108,8 +107,7 @@ TEST(BindObjcBlockTest, TestSixArguments) { TEST(BindObjcBlockTest, TestBlockMoveable) { base::OnceClosure c; __block BOOL invoked_block = NO; - { - base::mac::ScopedNSAutoreleasePool autorelease_pool; + @autoreleasepool { c = base::BindOnce(base::RetainBlock(^(std::unique_ptr<BOOL> v) { invoked_block = *v; }), @@ -139,8 +137,7 @@ TEST(BindObjcBlockTest, TestBlockDeallocation) { TEST(BindObjcBlockTest, TestBlockReleased) { base::WeakNSObject<NSObject> weak_nsobject; - { - base::mac::ScopedNSAutoreleasePool autorelease_pool; + @autoreleasepool { NSObject* nsobject = [[[NSObject alloc] init] autorelease]; weak_nsobject.reset(nsobject); diff --git a/chromium/base/mac/foundation_util.mm b/chromium/base/mac/foundation_util.mm index 8b20ebc678c..2a83d4d8158 100644 --- a/chromium/base/mac/foundation_util.mm +++ b/chromium/base/mac/foundation_util.mm @@ -15,6 +15,7 @@ #include "base/numerics/safe_conversions.h" #include "base/stl_util.h" #include "base/strings/sys_string_conversions.h" +#include "build/branding_buildflags.h" #include "build/build_config.h" #if !defined(OS_IOS) @@ -239,7 +240,7 @@ const char* BaseBundleID() { return base_bundle_id; } -#if defined(GOOGLE_CHROME_BUILD) +#if BUILDFLAG(GOOGLE_CHROME_BRANDING) return "com.google.Chrome"; #else return "org.chromium.Chromium"; diff --git a/chromium/base/mac/foundation_util_unittest.mm b/chromium/base/mac/foundation_util_unittest.mm index df8ef24b5ae..677264b2b82 100644 --- a/chromium/base/mac/foundation_util_unittest.mm +++ b/chromium/base/mac/foundation_util_unittest.mm @@ -11,7 +11,6 @@ #include "base/files/file_path.h" #include "base/format_macros.h" #include "base/mac/scoped_cftyperef.h" -#include "base/mac/scoped_nsautorelease_pool.h" #include "base/stl_util.h" #include "base/strings/stringprintf.h" #include "build/build_config.h" @@ -162,112 +161,107 @@ TEST(FoundationUtilTest, CFCast) { } TEST(FoundationUtilTest, ObjCCast) { - ScopedNSAutoreleasePool pool; - - id test_array = @[]; - id test_array_mutable = [NSMutableArray array]; - id test_data = [NSData data]; - id test_data_mutable = [NSMutableData dataWithCapacity:10]; - id test_date = [NSDate date]; - id test_dict = @{ @"meaning" : @42 }; - id test_dict_mutable = [NSMutableDictionary dictionaryWithCapacity:10]; - id test_number = @42; - id test_null = [NSNull null]; - id test_set = [NSSet setWithObject:@"string object"]; - id test_set_mutable = [NSMutableSet setWithCapacity:10]; - id test_str = [NSString string]; - id test_str_const = @"bonjour"; - id test_str_mutable = [NSMutableString stringWithCapacity:10]; - - // Make sure the allocations of NS types are good. - EXPECT_TRUE(test_array); - EXPECT_TRUE(test_array_mutable); - EXPECT_TRUE(test_data); - EXPECT_TRUE(test_data_mutable); - EXPECT_TRUE(test_date); - EXPECT_TRUE(test_dict); - EXPECT_TRUE(test_dict_mutable); - EXPECT_TRUE(test_number); - EXPECT_TRUE(test_null); - EXPECT_TRUE(test_set); - EXPECT_TRUE(test_set_mutable); - EXPECT_TRUE(test_str); - EXPECT_TRUE(test_str_const); - EXPECT_TRUE(test_str_mutable); - - // Casting the id correctly provides the same pointer. - EXPECT_EQ(test_array, ObjCCast<NSArray>(test_array)); - EXPECT_EQ(test_array_mutable, ObjCCast<NSArray>(test_array_mutable)); - EXPECT_EQ(test_data, ObjCCast<NSData>(test_data)); - EXPECT_EQ(test_data_mutable, ObjCCast<NSData>(test_data_mutable)); - EXPECT_EQ(test_date, ObjCCast<NSDate>(test_date)); - EXPECT_EQ(test_dict, ObjCCast<NSDictionary>(test_dict)); - EXPECT_EQ(test_dict_mutable, ObjCCast<NSDictionary>(test_dict_mutable)); - EXPECT_EQ(test_number, ObjCCast<NSNumber>(test_number)); - EXPECT_EQ(test_null, ObjCCast<NSNull>(test_null)); - EXPECT_EQ(test_set, ObjCCast<NSSet>(test_set)); - EXPECT_EQ(test_set_mutable, ObjCCast<NSSet>(test_set_mutable)); - EXPECT_EQ(test_str, ObjCCast<NSString>(test_str)); - EXPECT_EQ(test_str_const, ObjCCast<NSString>(test_str_const)); - EXPECT_EQ(test_str_mutable, ObjCCast<NSString>(test_str_mutable)); - - // When given an incorrect ObjC cast, provide nil. - EXPECT_FALSE(ObjCCast<NSString>(test_array)); - EXPECT_FALSE(ObjCCast<NSString>(test_array_mutable)); - EXPECT_FALSE(ObjCCast<NSString>(test_data)); - EXPECT_FALSE(ObjCCast<NSString>(test_data_mutable)); - EXPECT_FALSE(ObjCCast<NSSet>(test_date)); - EXPECT_FALSE(ObjCCast<NSSet>(test_dict)); - EXPECT_FALSE(ObjCCast<NSNumber>(test_dict_mutable)); - EXPECT_FALSE(ObjCCast<NSNull>(test_number)); - EXPECT_FALSE(ObjCCast<NSDictionary>(test_null)); - EXPECT_FALSE(ObjCCast<NSDictionary>(test_set)); - EXPECT_FALSE(ObjCCast<NSDate>(test_set_mutable)); - EXPECT_FALSE(ObjCCast<NSData>(test_str)); - EXPECT_FALSE(ObjCCast<NSData>(test_str_const)); - EXPECT_FALSE(ObjCCast<NSArray>(test_str_mutable)); - - // Giving a nil provides a nil. - EXPECT_FALSE(ObjCCast<NSArray>(nil)); - EXPECT_FALSE(ObjCCast<NSData>(nil)); - EXPECT_FALSE(ObjCCast<NSDate>(nil)); - EXPECT_FALSE(ObjCCast<NSDictionary>(nil)); - EXPECT_FALSE(ObjCCast<NSNull>(nil)); - EXPECT_FALSE(ObjCCast<NSNumber>(nil)); - EXPECT_FALSE(ObjCCast<NSSet>(nil)); - EXPECT_FALSE(ObjCCast<NSString>(nil)); - - // ObjCCastStrict: correct cast results in correct pointer being returned. - EXPECT_EQ(test_array, ObjCCastStrict<NSArray>(test_array)); - EXPECT_EQ(test_array_mutable, - ObjCCastStrict<NSArray>(test_array_mutable)); - EXPECT_EQ(test_data, ObjCCastStrict<NSData>(test_data)); - EXPECT_EQ(test_data_mutable, - ObjCCastStrict<NSData>(test_data_mutable)); - EXPECT_EQ(test_date, ObjCCastStrict<NSDate>(test_date)); - EXPECT_EQ(test_dict, ObjCCastStrict<NSDictionary>(test_dict)); - EXPECT_EQ(test_dict_mutable, - ObjCCastStrict<NSDictionary>(test_dict_mutable)); - EXPECT_EQ(test_number, ObjCCastStrict<NSNumber>(test_number)); - EXPECT_EQ(test_null, ObjCCastStrict<NSNull>(test_null)); - EXPECT_EQ(test_set, ObjCCastStrict<NSSet>(test_set)); - EXPECT_EQ(test_set_mutable, - ObjCCastStrict<NSSet>(test_set_mutable)); - EXPECT_EQ(test_str, ObjCCastStrict<NSString>(test_str)); - EXPECT_EQ(test_str_const, - ObjCCastStrict<NSString>(test_str_const)); - EXPECT_EQ(test_str_mutable, - ObjCCastStrict<NSString>(test_str_mutable)); - - // ObjCCastStrict: Giving a nil provides a nil. - EXPECT_FALSE(ObjCCastStrict<NSArray>(nil)); - EXPECT_FALSE(ObjCCastStrict<NSData>(nil)); - EXPECT_FALSE(ObjCCastStrict<NSDate>(nil)); - EXPECT_FALSE(ObjCCastStrict<NSDictionary>(nil)); - EXPECT_FALSE(ObjCCastStrict<NSNull>(nil)); - EXPECT_FALSE(ObjCCastStrict<NSNumber>(nil)); - EXPECT_FALSE(ObjCCastStrict<NSSet>(nil)); - EXPECT_FALSE(ObjCCastStrict<NSString>(nil)); + @autoreleasepool { + id test_array = @[]; + id test_array_mutable = [NSMutableArray array]; + id test_data = [NSData data]; + id test_data_mutable = [NSMutableData dataWithCapacity:10]; + id test_date = [NSDate date]; + id test_dict = @{@"meaning" : @42}; + id test_dict_mutable = [NSMutableDictionary dictionaryWithCapacity:10]; + id test_number = @42; + id test_null = [NSNull null]; + id test_set = [NSSet setWithObject:@"string object"]; + id test_set_mutable = [NSMutableSet setWithCapacity:10]; + id test_str = [NSString string]; + id test_str_const = @"bonjour"; + id test_str_mutable = [NSMutableString stringWithCapacity:10]; + + // Make sure the allocations of NS types are good. + EXPECT_TRUE(test_array); + EXPECT_TRUE(test_array_mutable); + EXPECT_TRUE(test_data); + EXPECT_TRUE(test_data_mutable); + EXPECT_TRUE(test_date); + EXPECT_TRUE(test_dict); + EXPECT_TRUE(test_dict_mutable); + EXPECT_TRUE(test_number); + EXPECT_TRUE(test_null); + EXPECT_TRUE(test_set); + EXPECT_TRUE(test_set_mutable); + EXPECT_TRUE(test_str); + EXPECT_TRUE(test_str_const); + EXPECT_TRUE(test_str_mutable); + + // Casting the id correctly provides the same pointer. + EXPECT_EQ(test_array, ObjCCast<NSArray>(test_array)); + EXPECT_EQ(test_array_mutable, ObjCCast<NSArray>(test_array_mutable)); + EXPECT_EQ(test_data, ObjCCast<NSData>(test_data)); + EXPECT_EQ(test_data_mutable, ObjCCast<NSData>(test_data_mutable)); + EXPECT_EQ(test_date, ObjCCast<NSDate>(test_date)); + EXPECT_EQ(test_dict, ObjCCast<NSDictionary>(test_dict)); + EXPECT_EQ(test_dict_mutable, ObjCCast<NSDictionary>(test_dict_mutable)); + EXPECT_EQ(test_number, ObjCCast<NSNumber>(test_number)); + EXPECT_EQ(test_null, ObjCCast<NSNull>(test_null)); + EXPECT_EQ(test_set, ObjCCast<NSSet>(test_set)); + EXPECT_EQ(test_set_mutable, ObjCCast<NSSet>(test_set_mutable)); + EXPECT_EQ(test_str, ObjCCast<NSString>(test_str)); + EXPECT_EQ(test_str_const, ObjCCast<NSString>(test_str_const)); + EXPECT_EQ(test_str_mutable, ObjCCast<NSString>(test_str_mutable)); + + // When given an incorrect ObjC cast, provide nil. + EXPECT_FALSE(ObjCCast<NSString>(test_array)); + EXPECT_FALSE(ObjCCast<NSString>(test_array_mutable)); + EXPECT_FALSE(ObjCCast<NSString>(test_data)); + EXPECT_FALSE(ObjCCast<NSString>(test_data_mutable)); + EXPECT_FALSE(ObjCCast<NSSet>(test_date)); + EXPECT_FALSE(ObjCCast<NSSet>(test_dict)); + EXPECT_FALSE(ObjCCast<NSNumber>(test_dict_mutable)); + EXPECT_FALSE(ObjCCast<NSNull>(test_number)); + EXPECT_FALSE(ObjCCast<NSDictionary>(test_null)); + EXPECT_FALSE(ObjCCast<NSDictionary>(test_set)); + EXPECT_FALSE(ObjCCast<NSDate>(test_set_mutable)); + EXPECT_FALSE(ObjCCast<NSData>(test_str)); + EXPECT_FALSE(ObjCCast<NSData>(test_str_const)); + EXPECT_FALSE(ObjCCast<NSArray>(test_str_mutable)); + + // Giving a nil provides a nil. + EXPECT_FALSE(ObjCCast<NSArray>(nil)); + EXPECT_FALSE(ObjCCast<NSData>(nil)); + EXPECT_FALSE(ObjCCast<NSDate>(nil)); + EXPECT_FALSE(ObjCCast<NSDictionary>(nil)); + EXPECT_FALSE(ObjCCast<NSNull>(nil)); + EXPECT_FALSE(ObjCCast<NSNumber>(nil)); + EXPECT_FALSE(ObjCCast<NSSet>(nil)); + EXPECT_FALSE(ObjCCast<NSString>(nil)); + + // ObjCCastStrict: correct cast results in correct pointer being returned. + EXPECT_EQ(test_array, ObjCCastStrict<NSArray>(test_array)); + EXPECT_EQ(test_array_mutable, ObjCCastStrict<NSArray>(test_array_mutable)); + EXPECT_EQ(test_data, ObjCCastStrict<NSData>(test_data)); + EXPECT_EQ(test_data_mutable, ObjCCastStrict<NSData>(test_data_mutable)); + EXPECT_EQ(test_date, ObjCCastStrict<NSDate>(test_date)); + EXPECT_EQ(test_dict, ObjCCastStrict<NSDictionary>(test_dict)); + EXPECT_EQ(test_dict_mutable, + ObjCCastStrict<NSDictionary>(test_dict_mutable)); + EXPECT_EQ(test_number, ObjCCastStrict<NSNumber>(test_number)); + EXPECT_EQ(test_null, ObjCCastStrict<NSNull>(test_null)); + EXPECT_EQ(test_set, ObjCCastStrict<NSSet>(test_set)); + EXPECT_EQ(test_set_mutable, ObjCCastStrict<NSSet>(test_set_mutable)); + EXPECT_EQ(test_str, ObjCCastStrict<NSString>(test_str)); + EXPECT_EQ(test_str_const, ObjCCastStrict<NSString>(test_str_const)); + EXPECT_EQ(test_str_mutable, ObjCCastStrict<NSString>(test_str_mutable)); + + // ObjCCastStrict: Giving a nil provides a nil. + EXPECT_FALSE(ObjCCastStrict<NSArray>(nil)); + EXPECT_FALSE(ObjCCastStrict<NSData>(nil)); + EXPECT_FALSE(ObjCCastStrict<NSDate>(nil)); + EXPECT_FALSE(ObjCCastStrict<NSDictionary>(nil)); + EXPECT_FALSE(ObjCCastStrict<NSNull>(nil)); + EXPECT_FALSE(ObjCCastStrict<NSNumber>(nil)); + EXPECT_FALSE(ObjCCastStrict<NSSet>(nil)); + EXPECT_FALSE(ObjCCastStrict<NSString>(nil)); + } } TEST(FoundationUtilTest, GetValueFromDictionary) { diff --git a/chromium/base/mac/objc_release_properties_unittest.mm b/chromium/base/mac/objc_release_properties_unittest.mm index 4b51e42b2ec..6804669d15c 100644 --- a/chromium/base/mac/objc_release_properties_unittest.mm +++ b/chromium/base/mac/objc_release_properties_unittest.mm @@ -5,7 +5,6 @@ #include "base/mac/objc_release_properties.h" #include "base/stl_util.h" -#import "base/mac/scoped_nsautorelease_pool.h" #include "testing/gtest/include/gtest/gtest.h" #import <objc/runtime.h> @@ -268,9 +267,7 @@ TEST(ObjCReleasePropertiesTest, SesameStreet) { // Make sure that worked before things get more involved. EXPECT_EQ(3, ah_ah_ah); - { - base::mac::ScopedNSAutoreleasePool pool; - + @autoreleasepool { test_object.baseCvcRetain = [CountVonCount countVonCount]; test_object.baseCvcCopy = [CountVonCount countVonCount]; test_object.baseCvcAssign = baseAssign; @@ -324,9 +321,7 @@ TEST(ObjCReleasePropertiesTest, SesameStreet) { // readonly. EXPECT_EQ(6, ah_ah_ah); - { - base::mac::ScopedNSAutoreleasePool pool; - + @autoreleasepool { // Put things back to how they were. test_object.baseCvcRetain = [CountVonCount countVonCount]; test_object.baseCvcCopy = [CountVonCount countVonCount]; diff --git a/chromium/base/mac/scoped_nsautorelease_pool.h b/chromium/base/mac/scoped_nsautorelease_pool.h index 4d15e6da4cf..6f58f30a8d9 100644 --- a/chromium/base/mac/scoped_nsautorelease_pool.h +++ b/chromium/base/mac/scoped_nsautorelease_pool.h @@ -21,6 +21,8 @@ namespace mac { // sends it a -drain message when destroyed. This allows an autorelease pool to // be maintained in ordinary C++ code without bringing in any direct Objective-C // dependency. +// +// Use only in C++ code; use @autoreleasepool in Obj-C(++) code. class BASE_EXPORT ScopedNSAutoreleasePool { public: diff --git a/chromium/base/mac/scoped_nsobject.h b/chromium/base/mac/scoped_nsobject.h index d970d03e8bc..b7d119546fc 100644 --- a/chromium/base/mac/scoped_nsobject.h +++ b/chromium/base/mac/scoped_nsobject.h @@ -36,11 +36,10 @@ namespace base { // scoped_nsprotocol<> has the same behavior as scoped_nsobject, but can be used // with protocols. // -// scoped_nsobject<> is not to be used for NSAutoreleasePools. For -// NSAutoreleasePools use ScopedNSAutoreleasePool from -// scoped_nsautorelease_pool.h instead. -// We check for bad uses of scoped_nsobject and NSAutoreleasePool at compile -// time with a template specialization (see below). +// scoped_nsobject<> is not to be used for NSAutoreleasePools. For C++ code use +// NSAutoreleasePool; for Objective-C(++) code use @autoreleasepool instead. We +// check for bad uses of scoped_nsobject and NSAutoreleasePool at compile time +// with a template specialization (see below). // // If Automatic Reference Counting (aka ARC) is enabled then the ownership // policy is not controllable by the user as ARC make it really difficult to @@ -187,7 +186,7 @@ class scoped_nsobject : public scoped_nsprotocol<NST*> { #if !defined(__has_feature) || !__has_feature(objc_arc) static_assert(std::is_same<NST, NSAutoreleasePool>::value == false, - "Use ScopedNSAutoreleasePool instead"); + "Use @autoreleasepool instead"); #endif }; diff --git a/chromium/base/mac/scoped_nsobject_unittest.mm b/chromium/base/mac/scoped_nsobject_unittest.mm index 72d52422582..d33e96e5750 100644 --- a/chromium/base/mac/scoped_nsobject_unittest.mm +++ b/chromium/base/mac/scoped_nsobject_unittest.mm @@ -4,7 +4,6 @@ #include <vector> -#include "base/mac/scoped_nsautorelease_pool.h" #include "base/mac/scoped_nsobject.h" #include "testing/gtest/include/gtest/gtest.h" @@ -24,8 +23,7 @@ TEST(ScopedNSObjectTest, ScopedNSObject) { base::scoped_nsobject<NSObject> p3 = p1; ASSERT_EQ(p1.get(), p3.get()); ASSERT_EQ(2u, [p1 retainCount]); - { - base::mac::ScopedNSAutoreleasePool pool; + @autoreleasepool { p3 = p1; } ASSERT_EQ(p1.get(), p3.get()); @@ -46,8 +44,7 @@ TEST(ScopedNSObjectTest, ScopedNSObject) { base::scoped_nsobject<NSObject> p6 = p1; ASSERT_EQ(3u, [p6 retainCount]); - { - base::mac::ScopedNSAutoreleasePool pool; + @autoreleasepool { p6.autorelease(); ASSERT_EQ(nil, p6.get()); ASSERT_EQ(3u, [p1 retainCount]); diff --git a/chromium/base/memory/scoped_refptr.h b/chromium/base/memory/scoped_refptr.h index 23dec28c4a4..ee8b75b3f88 100644 --- a/chromium/base/memory/scoped_refptr.h +++ b/chromium/base/memory/scoped_refptr.h @@ -25,10 +25,17 @@ class RefCounted; template <class, typename> class RefCountedThreadSafe; class SequencedTaskRunner; +class WrappedPromise; template <typename T> scoped_refptr<T> AdoptRef(T* t); +namespace internal { + +class BasePromise; + +} // namespace internal + namespace subtle { enum AdoptRefTag { kAdoptRefTag }; @@ -260,6 +267,11 @@ class scoped_refptr { friend scoped_refptr<U> base::AdoptRef(U*); friend class ::base::SequencedTaskRunner; + // Friend access so these classes can use the constructor below as part of a + // binary size optimization. + friend class ::base::internal::BasePromise; + friend class ::base::WrappedPromise; + // Returns the owned pointer (if any), releasing ownership to the caller. The // caller is responsible for managing the lifetime of the reference. T* release(); diff --git a/chromium/base/message_loop/message_loop.cc b/chromium/base/message_loop/message_loop.cc index a8a66308a1c..7e0b9c5520d 100644 --- a/chromium/base/message_loop/message_loop.cc +++ b/chromium/base/message_loop/message_loop.cc @@ -161,45 +161,4 @@ void MessageLoop::SetTaskRunner( sequence_manager_->SetTaskRunner(task_runner); } -#if !defined(OS_NACL) - -//------------------------------------------------------------------------------ -// MessageLoopForUI - -MessageLoopForUI::MessageLoopForUI(MessagePumpType type) : MessageLoop(type) { -#if defined(OS_ANDROID) - DCHECK(type == MessagePumpType::UI || type == MessagePumpType::JAVA); -#else - DCHECK_EQ(type, MessagePumpType::UI); -#endif -} - -#if defined(OS_IOS) -void MessageLoopForUI::Attach() { - sequence_manager_->AttachToMessagePump(); -} -#endif // defined(OS_IOS) - -#if defined(OS_ANDROID) -void MessageLoopForUI::Abort() { - static_cast<MessagePumpForUI*>(pump_)->Abort(); -} - -bool MessageLoopForUI::IsAborted() { - return static_cast<MessagePumpForUI*>(pump_)->IsAborted(); -} - -void MessageLoopForUI::QuitWhenIdle(base::OnceClosure callback) { - static_cast<MessagePumpForUI*>(pump_)->QuitWhenIdle(std::move(callback)); -} -#endif // defined(OS_ANDROID) - -#if defined(OS_WIN) -void MessageLoopForUI::EnableWmQuit() { - static_cast<MessagePumpForUI*>(pump_)->EnableWmQuit(); -} -#endif // defined(OS_WIN) - -#endif // !defined(OS_NACL) - } // namespace base diff --git a/chromium/base/message_loop/message_loop.h b/chromium/base/message_loop/message_loop.h index cc760ffab17..fa5f214c588 100644 --- a/chromium/base/message_loop/message_loop.h +++ b/chromium/base/message_loop/message_loop.h @@ -23,10 +23,6 @@ namespace base { -namespace internal { -class MessageLoopThreadDelegate; -} // namespace internal - class MessageLoopImpl; class MessagePump; class TaskObserver; @@ -160,7 +156,6 @@ class BASE_EXPORT MessageLoop { friend class MessageLoopTypedTest; friend class ScheduleWorkTest; friend class Thread; - friend class internal::MessageLoopThreadDelegate; friend class sequence_manager::internal::SequenceManagerImpl; FRIEND_TEST_ALL_PREFIXES(MessageLoopTest, DeleteUnboundLoop); @@ -202,58 +197,6 @@ class BASE_EXPORT MessageLoop { DISALLOW_COPY_AND_ASSIGN(MessageLoop); }; -#if !defined(OS_NACL) - -//----------------------------------------------------------------------------- -// MessageLoopForUI extends MessageLoop with methods that are particular to a -// MessageLoop instantiated with TYPE_UI. -// -// By instantiating a MessageLoopForUI on the current thread, the owner enables -// native UI message pumping. -// -// MessageLoopCurrentForUI is exposed statically on its thread via -// MessageLoopCurrentForUI::Get() to provide additional functionality. -// -class BASE_EXPORT MessageLoopForUI : public MessageLoop { - public: - explicit MessageLoopForUI(MessagePumpType type = MessagePumpType::UI); - -#if defined(OS_IOS) - // On iOS, the main message loop cannot be Run(). Instead call Attach(), - // which connects this MessageLoop to the UI thread's CFRunLoop and allows - // PostTask() to work. - void Attach(); -#endif - -#if defined(OS_ANDROID) - // On Android there are cases where we want to abort immediately without - // calling Quit(), in these cases we call Abort(). - void Abort(); - - // True if this message pump has been aborted. - bool IsAborted(); - - // Since Run() is never called on Android, and the message loop is run by the - // java Looper, quitting the RunLoop won't join the thread, so we need a - // callback to run when the RunLoop goes idle to let the Java thread know when - // it can safely quit. - void QuitWhenIdle(base::OnceClosure callback); -#endif - -#if defined(OS_WIN) - // See method of the same name in the Windows MessagePumpForUI implementation. - void EnableWmQuit(); -#endif -}; - -// Do not add any member variables to MessageLoopForUI! This is important b/c -// MessageLoopForUI is often allocated via MessageLoop(TYPE_UI). Any extra -// data that you need should be stored on the MessageLoop's pump_ instance. -static_assert(sizeof(MessageLoop) == sizeof(MessageLoopForUI), - "MessageLoopForUI should not have extra member variables"); - -#endif // !defined(OS_NACL) - //----------------------------------------------------------------------------- // MessageLoopForIO extends MessageLoop with methods that are particular to a // MessageLoop instantiated with TYPE_IO. diff --git a/chromium/base/message_loop/message_loop_unittest.cc b/chromium/base/message_loop/message_loop_unittest.cc index 9d5e92d50bb..02cb94b5b47 100644 --- a/chromium/base/message_loop/message_loop_unittest.cc +++ b/chromium/base/message_loop/message_loop_unittest.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/message_loop/message_loop.h" + #include <stddef.h> #include <stdint.h> @@ -1507,29 +1509,6 @@ TEST_F(MessageLoopTest, WmQuitIsIgnored) { EXPECT_TRUE(task_was_run); } -TEST_F(MessageLoopTest, WmQuitIsNotIgnoredWithEnableWmQuit) { - MessageLoop loop(MessagePumpType::UI); - static_cast<MessageLoopForUI*>(&loop)->EnableWmQuit(); - - // Post a WM_QUIT message to the current thread. - ::PostQuitMessage(0); - - // Post a task to the current thread, with a small delay to make it less - // likely that we process the posted task before looking for WM_* messages. - RunLoop run_loop; - loop.task_runner()->PostDelayedTask(FROM_HERE, - BindOnce( - [](OnceClosure closure) { - ADD_FAILURE(); - std::move(closure).Run(); - }, - run_loop.QuitClosure()), - TestTimeouts::tiny_timeout()); - - // Run the loop. It should not result in ADD_FAILURE() getting called. - run_loop.Run(); -} - TEST_F(MessageLoopTest, PostDelayedTask_SharedTimer_SubPump) { MessageLoop message_loop(MessagePumpType::UI); diff --git a/chromium/base/message_loop/message_pump_io_ios_unittest.cc b/chromium/base/message_loop/message_pump_io_ios_unittest.cc index 4d15d44db32..aec10012a7c 100644 --- a/chromium/base/message_loop/message_pump_io_ios_unittest.cc +++ b/chromium/base/message_loop/message_pump_io_ios_unittest.cc @@ -7,6 +7,7 @@ #include <unistd.h> #include "base/macros.h" +#include "base/message_loop/message_pump_for_io.h" #include "base/posix/eintr_wrapper.h" #include "base/test/gtest_util.h" #include "base/threading/thread.h" diff --git a/chromium/base/message_loop/message_pump_mac_unittest.mm b/chromium/base/message_loop/message_pump_mac_unittest.mm index d5ff5c9fd59..85f4779868a 100644 --- a/chromium/base/message_loop/message_pump_mac_unittest.mm +++ b/chromium/base/message_loop/message_pump_mac_unittest.mm @@ -9,9 +9,9 @@ #include "base/mac/scoped_cftyperef.h" #import "base/mac/scoped_nsobject.h" #include "base/macros.h" -#include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop_current.h" #include "base/test/bind_test_util.h" +#include "base/test/task_environment.h" #include "base/threading/thread_task_runner_handle.h" #include "testing/gtest/include/gtest/gtest.h" @@ -145,7 +145,8 @@ void RunTaskInMode(CFRunLoopMode mode, OnceClosure task) { // Tests the correct behavior of ScopedPumpMessagesInPrivateModes. TEST(MessagePumpMacTest, ScopedPumpMessagesInPrivateModes) { - MessageLoopForUI message_loop; + test::SingleThreadTaskEnvironment task_environment( + test::SingleThreadTaskEnvironment::MainThreadType::UI); CFRunLoopMode kRegular = kCFRunLoopDefaultMode; CFRunLoopMode kPrivate = CFSTR("NSUnhighlightMenuRunLoopMode"); @@ -193,7 +194,8 @@ TEST(MessagePumpMacTest, ScopedPumpMessagesInPrivateModes) { // Tests that private message loop modes are not pumped while a modal dialog is // present. TEST(MessagePumpMacTest, ScopedPumpMessagesAttemptWithModalDialog) { - MessageLoopForUI message_loop; + test::SingleThreadTaskEnvironment task_environment( + test::SingleThreadTaskEnvironment::MainThreadType::UI); { base::ScopedPumpMessagesInPrivateModes allow_private; @@ -239,7 +241,8 @@ TEST(MessagePumpMacTest, ScopedPumpMessagesAttemptWithModalDialog) { // terminal such as SSH (as opposed to Chromoting) to investigate the issue. // TEST(MessagePumpMacTest, DontInvalidateTimerInNativeRunLoop) { - MessageLoopForUI message_loop; + test::SingleThreadTaskEnvironment task_environment( + test::SingleThreadTaskEnvironment::MainThreadType::UI); NSWindow* window = [[[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100) styleMask:NSBorderlessWindowMask @@ -291,7 +294,8 @@ TEST(MessagePumpMacTest, DontInvalidateTimerInNativeRunLoop) { } TEST(MessagePumpMacTest, QuitWithModalWindow) { - MessageLoopForUI message_loop; + test::SingleThreadTaskEnvironment task_environment( + test::SingleThreadTaskEnvironment::MainThreadType::UI); NSWindow* window = [[[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100) styleMask:NSBorderlessWindowMask diff --git a/chromium/base/message_loop/message_pump_perftest.cc b/chromium/base/message_loop/message_pump_perftest.cc index 1af026f7cd2..7c7cdd4decd 100644 --- a/chromium/base/message_loop/message_pump_perftest.cc +++ b/chromium/base/message_loop/message_pump_perftest.cc @@ -9,7 +9,6 @@ #include "base/bind_helpers.h" #include "base/format_macros.h" #include "base/memory/ptr_util.h" -#include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop_current.h" #include "base/message_loop/message_pump_type.h" #include "base/single_thread_task_runner.h" @@ -98,12 +97,7 @@ class ScheduleWorkTest : public testing::Test { target_.reset(new Thread("test")); Thread::Options options(target_type, 0u); - - std::unique_ptr<MessageLoop> message_loop = - MessageLoop::CreateUnbound(target_type); - message_loop_ = message_loop.get(); - options.delegate = - new internal::MessageLoopThreadDelegate(std::move(message_loop)); + options.message_pump_type = target_type; target_->StartWithOptions(options); // Without this, it's possible for the scheduling threads to start and run @@ -203,7 +197,6 @@ class ScheduleWorkTest : public testing::Test { private: std::unique_ptr<Thread> target_; - MessageLoop* message_loop_; #if defined(OS_ANDROID) std::unique_ptr<JavaHandlerThreadForTest> java_thread_; #endif diff --git a/chromium/base/metrics/histogram_macros.h b/chromium/base/metrics/histogram_macros.h index 9423fc07ad7..6edb65a93fd 100644 --- a/chromium/base/metrics/histogram_macros.h +++ b/chromium/base/metrics/histogram_macros.h @@ -187,7 +187,7 @@ // underflow bucket. // Sample usage: -// UMA_HISTOGRAM_CUSTOM_COUNTS("My.Histogram", 1, 100000000, 100); +// UMA_HISTOGRAM_CUSTOM_COUNTS("My.Histogram", sample, 1, 100000000, 50); #define UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \ INTERNAL_HISTOGRAM_CUSTOM_COUNTS_WITH_FLAG( \ name, sample, min, max, bucket_count, \ diff --git a/chromium/base/native_library_win.cc b/chromium/base/native_library_win.cc index 08d520d061b..74413b6c323 100644 --- a/chromium/base/native_library_win.cc +++ b/chromium/base/native_library_win.cc @@ -8,7 +8,6 @@ #include "base/files/file_util.h" #include "base/metrics/histogram_macros.h" -#include "base/optional.h" #include "base/path_service.h" #include "base/scoped_native_library.h" #include "base/strings/string_util.h" @@ -170,12 +169,12 @@ NativeLibrary LoadSystemLibraryHelper(const FilePath& library_path, return module; } -Optional<FilePath> GetSystemLibraryName(FilePath::StringPieceType name) { +FilePath GetSystemLibraryName(FilePath::StringPieceType name) { FilePath library_path; // Use an absolute path to load the DLL to avoid DLL preloading attacks. - if (!base::PathService::Get(base::DIR_SYSTEM, &library_path)) - return base::nullopt; - return make_optional(library_path.Append(name)); + if (PathService::Get(DIR_SYSTEM, &library_path)) + library_path = library_path.Append(name); + return library_path; } } // namespace @@ -210,18 +209,19 @@ std::string GetLoadableModuleName(StringPiece name) { NativeLibrary LoadSystemLibrary(FilePath::StringPieceType name, NativeLibraryLoadError* error) { - Optional<FilePath> library_path = GetSystemLibraryName(name); - if (library_path) - return LoadSystemLibraryHelper(library_path.value(), error); - if (error) - error->code = ERROR_NOT_FOUND; - return nullptr; + FilePath library_path = GetSystemLibraryName(name); + if (library_path.empty()) { + if (error) + error->code = ERROR_NOT_FOUND; + return nullptr; + } + return LoadSystemLibraryHelper(library_path, error); } NativeLibrary PinSystemLibrary(FilePath::StringPieceType name, NativeLibraryLoadError* error) { - Optional<FilePath> library_path = GetSystemLibraryName(name); - if (!library_path) { + FilePath library_path = GetSystemLibraryName(name); + if (library_path.empty()) { if (error) error->code = ERROR_NOT_FOUND; return nullptr; @@ -231,25 +231,28 @@ NativeLibrary PinSystemLibrary(FilePath::StringPieceType name, // Dllmain. ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK); ScopedNativeLibrary module; - if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, - as_wcstr(library_path.value().value()), - ScopedNativeLibrary::Receiver(module).get())) { - // Load and pin the library since it wasn't already loaded. - module = ScopedNativeLibrary( - LoadSystemLibraryHelper(library_path.value(), error)); - if (module.is_valid()) { - ScopedNativeLibrary temp; - if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, - as_wcstr(library_path.value().value()), - ScopedNativeLibrary::Receiver(temp).get())) { - if (error) - error->code = ::GetLastError(); - // Return nullptr since we failed to pin the module. - return nullptr; - } - } + if (::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, + as_wcstr(library_path.value()), + ScopedNativeLibrary::Receiver(module).get())) { + return module.release(); } - return module.release(); + + // Load and pin the library since it wasn't already loaded. + module = ScopedNativeLibrary(LoadSystemLibraryHelper(library_path, error)); + if (!module.is_valid()) + return nullptr; + + ScopedNativeLibrary temp; + if (::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, + as_wcstr(library_path.value()), + ScopedNativeLibrary::Receiver(temp).get())) { + return module.release(); + } + + if (error) + error->code = ::GetLastError(); + // Return nullptr since we failed to pin the module. + return nullptr; } } // namespace base diff --git a/chromium/base/numerics/safe_math_arm_impl.h b/chromium/base/numerics/safe_math_arm_impl.h index a7cda1bb238..ff86bd0b73b 100644 --- a/chromium/base/numerics/safe_math_arm_impl.h +++ b/chromium/base/numerics/safe_math_arm_impl.h @@ -57,8 +57,8 @@ struct ClampedAddFastAsmOp { return saturated_cast<V>(x + y); int32_t result; - int32_t x_i32 = x; - int32_t y_i32 = y; + int32_t x_i32 = checked_cast<int32_t>(x); + int32_t y_i32 = checked_cast<int32_t>(y); asm("qadd %[result], %[first], %[second]" : [result] "=r"(result) @@ -83,8 +83,8 @@ struct ClampedSubFastAsmOp { return saturated_cast<V>(x - y); int32_t result; - int32_t x_i32 = x; - int32_t y_i32 = y; + int32_t x_i32 = checked_cast<int32_t>(x); + int32_t y_i32 = checked_cast<int32_t>(y); asm("qsub %[result], %[first], %[second]" : [result] "=r"(result) diff --git a/chromium/base/process/kill.h b/chromium/base/process/kill.h index 9acfb8a7388..70a04d97e5a 100644 --- a/chromium/base/process/kill.h +++ b/chromium/base/process/kill.h @@ -29,6 +29,7 @@ const DWORD kNormalTerminationExitCode = 0; const DWORD kDebuggerInactiveExitCode = 0xC0000354; const DWORD kKeyboardInterruptExitCode = 0xC000013A; const DWORD kDebuggerTerminatedExitCode = 0x40010004; +const DWORD kStatusInvalidImageHashExitCode = 0xC0000428; // This exit code is used by the Windows task manager when it kills a // process. It's value is obviously not that unique, and it's @@ -46,6 +47,7 @@ const DWORD kProcessKilledExitCode = 1; // exit code arguments to KillProcess*(), use platform/application // specific values instead. enum TerminationStatus { + // clang-format off TERMINATION_STATUS_NORMAL_TERMINATION, // zero exit status TERMINATION_STATUS_ABNORMAL_TERMINATION, // non-zero exit status TERMINATION_STATUS_PROCESS_WAS_KILLED, // e.g. SIGKILL or task manager kill @@ -64,7 +66,12 @@ enum TerminationStatus { #endif TERMINATION_STATUS_LAUNCH_FAILED, // child process never launched TERMINATION_STATUS_OOM, // Process died due to oom +#if defined(OS_WIN) + // On Windows, the OS terminated process due to code integrity failure. + TERMINATION_STATUS_INTEGRITY_FAILURE, +#endif TERMINATION_STATUS_MAX_ENUM + // clang-format on }; // Attempts to kill all the processes on the current machine that were launched diff --git a/chromium/base/process/kill_win.cc b/chromium/base/process/kill_win.cc index 7a664429bcd..3b85dea1cd4 100644 --- a/chromium/base/process/kill_win.cc +++ b/chromium/base/process/kill_win.cc @@ -61,6 +61,7 @@ TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) { *exit_code = tmp_exit_code; + // clang-format off switch (tmp_exit_code) { case win::kNormalTerminationExitCode: return TERMINATION_STATUS_NORMAL_TERMINATION; @@ -74,10 +75,15 @@ TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) { // object memory limits. case win::kOomExceptionCode: // Ran out of memory. return TERMINATION_STATUS_OOM; + // This exit code means the process failed an OS integrity check. + // This is tested in ProcessMitigationsTest.* in sandbox. + case win::kStatusInvalidImageHashExitCode: + return TERMINATION_STATUS_INTEGRITY_FAILURE; default: // All other exit codes indicate crashes. return TERMINATION_STATUS_PROCESS_CRASHED; } + // clang-format on } bool WaitForProcessesToExit(const FilePath::StringType& executable_name, diff --git a/chromium/base/process/launch_win.cc b/chromium/base/process/launch_win.cc index 6bf9e188d66..59c4e8f7e27 100644 --- a/chromium/base/process/launch_win.cc +++ b/chromium/base/process/launch_win.cc @@ -287,9 +287,9 @@ Process LaunchProcess(const string16& cmdline, << "job. https://crbug.com/820996"; if (options.as_user) { flags |= CREATE_UNICODE_ENVIRONMENT; - void* enviroment_block = nullptr; + void* environment_block = nullptr; - if (!CreateEnvironmentBlock(&enviroment_block, options.as_user, FALSE)) { + if (!CreateEnvironmentBlock(&environment_block, options.as_user, FALSE)) { DPLOG(ERROR); return Process(); } @@ -300,9 +300,9 @@ Process LaunchProcess(const string16& cmdline, BOOL launched = CreateProcessAsUser( options.as_user, nullptr, as_writable_wcstr(writable_cmdline), nullptr, - nullptr, inherit_handles, flags, enviroment_block, current_directory, + nullptr, inherit_handles, flags, environment_block, current_directory, startup_info, &temp_process_info); - DestroyEnvironmentBlock(enviroment_block); + DestroyEnvironmentBlock(environment_block); if (!launched) { DPLOG(ERROR) << "Command line:" << std::endl << UTF16ToUTF8(cmdline) << std::endl; diff --git a/chromium/base/process/process_unittest.cc b/chromium/base/process/process_unittest.cc index 68126460557..18db6892399 100644 --- a/chromium/base/process/process_unittest.cc +++ b/chromium/base/process/process_unittest.cc @@ -271,13 +271,7 @@ TEST_F(ProcessTest, WaitForExitWithTimeout) { // backgrounding and restoring. // Note: a platform may not be willing or able to lower the priority of // a process. The calls to SetProcessBackground should be noops then. -// Flaky on Windows: https://crbug.com/931721. -#if defined(OS_WIN) -#define MAYBE_SetProcessBackgrounded DISABLED_SetProcessBackgrounded -#else -#define MAYBE_SetProcessBackgrounded SetProcessBackgrounded -#endif -TEST_F(ProcessTest, MAYBE_SetProcessBackgrounded) { +TEST_F(ProcessTest, SetProcessBackgrounded) { if (!Process::CanBackgroundProcesses()) return; Process process(SpawnChild("SimpleChildProcess")); @@ -305,15 +299,9 @@ TEST_F(ProcessTest, MAYBE_SetProcessBackgrounded) { EXPECT_EQ(old_priority, new_priority); } -// Flaky on Windows: https://crbug.com/931721. -#if defined(OS_WIN) -#define MAYBE_SetProcessBackgroundedSelf DISABLED_SetProcessBackgroundedSelf -#else -#define MAYBE_SetProcessBackgroundedSelf SetProcessBackgroundedSelf -#endif // Same as SetProcessBackgrounded but to this very process. It uses // a different code path at least for Windows. -TEST_F(ProcessTest, MAYBE_SetProcessBackgroundedSelf) { +TEST_F(ProcessTest, SetProcessBackgroundedSelf) { if (!Process::CanBackgroundProcesses()) return; Process process = Process::Current(); diff --git a/chromium/base/profiler/register_context.h b/chromium/base/profiler/register_context.h index 9c6eaf1b1b2..556b34c27aa 100644 --- a/chromium/base/profiler/register_context.h +++ b/chromium/base/profiler/register_context.h @@ -17,6 +17,8 @@ #include <windows.h> #elif defined(OS_MACOSX) #include <mach/machine/thread_status.h> +#elif defined(OS_ANDROID) && !defined(ARCH_CPU_64_BITS) +#include <sys/ucontext.h> #endif namespace base { @@ -83,6 +85,23 @@ inline uintptr_t& RegisterContextInstructionPointer( return AsUintPtr(&context->__rip); } +#elif defined(OS_ANDROID) && defined(ARCH_CPU_ARM_FAMILY) && \ + defined(ARCH_CPU_32_BITS) // #if defined(OS_WIN) + +using RegisterContext = mcontext_t; + +inline uintptr_t& RegisterContextStackPointer(mcontext_t* context) { + return AsUintPtr(&context->arm_sp); +} + +inline uintptr_t& RegisterContextFramePointer(mcontext_t* context) { + return AsUintPtr(&context->arm_fp); +} + +inline uintptr_t& RegisterContextInstructionPointer(mcontext_t* context) { + return AsUintPtr(&context->arm_ip); +} + #else // #if defined(OS_WIN) // Placeholders for other platforms. diff --git a/chromium/base/profiler/stack_copier.h b/chromium/base/profiler/stack_copier.h index cea4574362f..921e1324e1d 100644 --- a/chromium/base/profiler/stack_copier.h +++ b/chromium/base/profiler/stack_copier.h @@ -33,6 +33,7 @@ class BASE_EXPORT StackCopier { ProfileBuilder* profile_builder, RegisterContext* thread_context) = 0; + protected: // If the value at |pointer| points to the original stack, rewrite it to point // to the corresponding location in the copied stack. // diff --git a/chromium/base/profiler/stack_copier_signal.cc b/chromium/base/profiler/stack_copier_signal.cc new file mode 100644 index 00000000000..ae858701252 --- /dev/null +++ b/chromium/base/profiler/stack_copier_signal.cc @@ -0,0 +1,28 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/profiler/stack_copier_signal.h" + +#include "base/profiler/metadata_recorder.h" +#include "base/profiler/sample_metadata.h" +#include "base/profiler/stack_buffer.h" +#include "base/profiler/suspendable_thread_delegate.h" + +namespace base { + +StackCopierSignal::StackCopierSignal( + std::unique_ptr<ThreadDelegate> thread_delegate) + : thread_delegate_(std::move(thread_delegate)) {} + +StackCopierSignal::~StackCopierSignal() = default; + +bool StackCopierSignal::CopyStack(StackBuffer* stack_buffer, + uintptr_t* stack_top, + ProfileBuilder* profile_builder, + RegisterContext* thread_context) { + // TODO(wittman): Implement signal-based stack copying. + return false; +} + +} // namespace base diff --git a/chromium/base/profiler/stack_copier_signal.h b/chromium/base/profiler/stack_copier_signal.h new file mode 100644 index 00000000000..14f48969200 --- /dev/null +++ b/chromium/base/profiler/stack_copier_signal.h @@ -0,0 +1,36 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_PROFILER_STACK_COPIER_SIGNAL_H_ +#define BASE_PROFILER_STACK_COPIER_SIGNAL_H_ + +#include <memory> + +#include "base/base_export.h" +#include "base/profiler/stack_copier.h" + +namespace base { + +class ThreadDelegate; + +// Supports stack copying on platforms where a signal must be delivered to the +// profiled thread and the stack is copied from the signal handler. +class BASE_EXPORT StackCopierSignal : public StackCopier { + public: + StackCopierSignal(std::unique_ptr<ThreadDelegate> thread_delegate); + ~StackCopierSignal() override; + + // StackCopier: + bool CopyStack(StackBuffer* stack_buffer, + uintptr_t* stack_top, + ProfileBuilder* profile_builder, + RegisterContext* thread_context) override; + + private: + std::unique_ptr<ThreadDelegate> thread_delegate_; +}; + +} // namespace base + +#endif // BASE_PROFILER_STACK_COPIER_SIGNAL_H_ diff --git a/chromium/base/profiler/stack_copier_suspend.cc b/chromium/base/profiler/stack_copier_suspend.cc new file mode 100644 index 00000000000..a12aba595d4 --- /dev/null +++ b/chromium/base/profiler/stack_copier_suspend.cc @@ -0,0 +1,82 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/profiler/stack_copier_suspend.h" + +#include "base/profiler/metadata_recorder.h" +#include "base/profiler/sample_metadata.h" +#include "base/profiler/stack_buffer.h" +#include "base/profiler/suspendable_thread_delegate.h" + +namespace base { + +StackCopierSuspend::StackCopierSuspend( + std::unique_ptr<SuspendableThreadDelegate> thread_delegate) + : thread_delegate_(std::move(thread_delegate)) {} + +StackCopierSuspend::~StackCopierSuspend() = default; + +// Suspends the thread, copies the stack state, and resumes the thread. The +// copied stack state includes the stack itself, the top address of the stack +// copy, the register context, and the current metadata state. Returns true on +// success, and returns the copied state via the params. +// +// NO HEAP ALLOCATIONS within the ScopedSuspendThread scope. +bool StackCopierSuspend::CopyStack(StackBuffer* stack_buffer, + uintptr_t* stack_top, + ProfileBuilder* profile_builder, + RegisterContext* thread_context) { + const uintptr_t top = thread_delegate_->GetStackBaseAddress(); + uintptr_t bottom = 0; + const uint8_t* stack_copy_bottom = nullptr; + { + // The MetadataProvider must be created before the ScopedSuspendThread + // because it acquires a lock in its constructor that might otherwise be + // held by the target thread, resulting in deadlock. + std::unique_ptr<base::ProfileBuilder::MetadataProvider> get_metadata_items = + base::GetSampleMetadataRecorder()->CreateMetadataProvider(); + + // Allocation of the ScopedSuspendThread object itself is OK since it + // necessarily occurs before the thread is suspended by the object. + std::unique_ptr<SuspendableThreadDelegate::ScopedSuspendThread> + suspend_thread = thread_delegate_->CreateScopedSuspendThread(); + + if (!suspend_thread->WasSuccessful()) + return false; + + if (!thread_delegate_->GetThreadContext(thread_context)) + return false; + + bottom = RegisterContextStackPointer(thread_context); + + // The StackBuffer allocation is expected to be at least as large as the + // largest stack region allocation on the platform, but check just in case + // it isn't *and* the actual stack itself exceeds the buffer allocation + // size. + if ((top - bottom) > stack_buffer->size()) + return false; + + if (!thread_delegate_->CanCopyStack(bottom)) + return false; + + profile_builder->RecordMetadata(get_metadata_items.get()); + + stack_copy_bottom = CopyStackContentsAndRewritePointers( + reinterpret_cast<uint8_t*>(bottom), reinterpret_cast<uintptr_t*>(top), + StackBuffer::kPlatformStackAlignment, stack_buffer->buffer()); + } + + *stack_top = reinterpret_cast<uintptr_t>(stack_copy_bottom) + (top - bottom); + + for (uintptr_t* reg : + thread_delegate_->GetRegistersToRewrite(thread_context)) { + *reg = RewritePointerIfInOriginalStack(reinterpret_cast<uint8_t*>(bottom), + reinterpret_cast<uintptr_t*>(top), + stack_copy_bottom, *reg); + } + + return true; +} + +} // namespace base diff --git a/chromium/base/profiler/stack_copier_suspend.h b/chromium/base/profiler/stack_copier_suspend.h new file mode 100644 index 00000000000..67976251b51 --- /dev/null +++ b/chromium/base/profiler/stack_copier_suspend.h @@ -0,0 +1,38 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_PROFILER_STACK_COPIER_SUSPEND_H_ +#define BASE_PROFILER_STACK_COPIER_SUSPEND_H_ + +#include <memory> + +#include "base/base_export.h" +#include "base/profiler/stack_copier.h" + +namespace base { + +class SuspendableThreadDelegate; + +// Supports stack copying on platforms where the profiled thread must be +// explicitly suspended from the profiler thread and the stack is copied from +// the profiler thread. +class BASE_EXPORT StackCopierSuspend : public StackCopier { + public: + StackCopierSuspend( + std::unique_ptr<SuspendableThreadDelegate> thread_delegate); + ~StackCopierSuspend() override; + + // StackCopier: + bool CopyStack(StackBuffer* stack_buffer, + uintptr_t* stack_top, + ProfileBuilder* profile_builder, + RegisterContext* thread_context) override; + + private: + std::unique_ptr<SuspendableThreadDelegate> thread_delegate_; +}; + +} // namespace base + +#endif // BASE_PROFILER_STACK_COPIER_SUSPEND_H_ diff --git a/chromium/base/profiler/stack_copier_suspend_unittest.cc b/chromium/base/profiler/stack_copier_suspend_unittest.cc new file mode 100644 index 00000000000..a496ade1602 --- /dev/null +++ b/chromium/base/profiler/stack_copier_suspend_unittest.cc @@ -0,0 +1,201 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <algorithm> +#include <cstring> +#include <memory> +#include <numeric> +#include <utility> + +#include "base/profiler/profile_builder.h" +#include "base/profiler/stack_buffer.h" +#include "base/profiler/stack_copier_suspend.h" +#include "base/profiler/suspendable_thread_delegate.h" +#include "base/stl_util.h" +#include "build/build_config.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +namespace { + +using ::testing::ElementsAre; + +// A thread delegate for use in tests that provides the expected behavior when +// operating on the supplied fake stack. +class TestSuspendableThreadDelegate : public SuspendableThreadDelegate { + public: + class TestScopedSuspendThread + : public SuspendableThreadDelegate::ScopedSuspendThread { + public: + TestScopedSuspendThread() = default; + + TestScopedSuspendThread(const TestScopedSuspendThread&) = delete; + TestScopedSuspendThread& operator=(const TestScopedSuspendThread&) = delete; + + bool WasSuccessful() const override { return true; } + }; + + TestSuspendableThreadDelegate(const std::vector<uintptr_t>& fake_stack, + // The register context will be initialized to + // *|thread_context| if non-null. + RegisterContext* thread_context = nullptr) + : fake_stack_(fake_stack), thread_context_(thread_context) {} + + TestSuspendableThreadDelegate(const TestSuspendableThreadDelegate&) = delete; + TestSuspendableThreadDelegate& operator=( + const TestSuspendableThreadDelegate&) = delete; + + std::unique_ptr<ScopedSuspendThread> CreateScopedSuspendThread() override { + return std::make_unique<TestScopedSuspendThread>(); + } + + bool GetThreadContext(RegisterContext* thread_context) override { + if (thread_context_) + *thread_context = *thread_context_; + // Set the stack pointer to be consistent with the provided fake stack. + RegisterContextStackPointer(thread_context) = + reinterpret_cast<uintptr_t>(&fake_stack_[0]); + RegisterContextInstructionPointer(thread_context) = + reinterpret_cast<uintptr_t>(fake_stack_[0]); + return true; + } + + uintptr_t GetStackBaseAddress() const override { + return reinterpret_cast<uintptr_t>(&fake_stack_[0] + fake_stack_.size()); + } + + bool CanCopyStack(uintptr_t stack_pointer) override { return true; } + + std::vector<uintptr_t*> GetRegistersToRewrite( + RegisterContext* thread_context) override { + return {&RegisterContextFramePointer(thread_context)}; + } + + private: + // Must be a reference to retain the underlying allocation from the vector + // passed to the constructor. + const std::vector<uintptr_t>& fake_stack_; + RegisterContext* thread_context_; +}; + +class TestProfileBuilder : public ProfileBuilder { + public: + TestProfileBuilder() = default; + + TestProfileBuilder(const TestProfileBuilder&) = delete; + TestProfileBuilder& operator=(const TestProfileBuilder&) = delete; + + // ProfileBuilder + ModuleCache* GetModuleCache() override { return nullptr; } + + void RecordMetadata( + base::ProfileBuilder::MetadataProvider* metadata_provider) override { + recorded_metadata_ = true; + } + + void OnSampleCompleted(std::vector<Frame> frames) override {} + void OnProfileCompleted(TimeDelta profile_duration, + TimeDelta sampling_period) override {} + + private: + bool recorded_metadata_ = false; +}; + +} // namespace + +TEST(StackCopierSuspendTest, CopyStack) { + const std::vector<uintptr_t> stack = {0, 1, 2, 3, 4}; + StackCopierSuspend stack_copier_suspend( + std::make_unique<TestSuspendableThreadDelegate>(stack)); + + std::unique_ptr<StackBuffer> stack_buffer = + std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t)); + uintptr_t stack_top = 0; + TestProfileBuilder profile_builder; + RegisterContext register_context = {0}; + stack_copier_suspend.CopyStack(stack_buffer.get(), &stack_top, + &profile_builder, ®ister_context); + + uintptr_t* stack_copy_bottom = + reinterpret_cast<uintptr_t*>(stack_buffer.get()->buffer()); + std::vector<uintptr_t> stack_copy(stack_copy_bottom, + stack_copy_bottom + stack.size()); + EXPECT_EQ(stack, stack_copy); +} + +TEST(StackCopierSuspendTest, CopyStackBufferTooSmall) { + std::vector<uintptr_t> stack = {0, 1, 2, 3, 4}; + StackCopierSuspend stack_copier_suspend( + std::make_unique<TestSuspendableThreadDelegate>(stack)); + + std::unique_ptr<StackBuffer> stack_buffer = + std::make_unique<StackBuffer>((stack.size() - 1) * sizeof(uintptr_t)); + // Make the buffer different than the input stack. + stack_buffer->buffer()[0] = 100; + uintptr_t stack_top = 0; + TestProfileBuilder profile_builder; + RegisterContext register_context = {0}; + stack_copier_suspend.CopyStack(stack_buffer.get(), &stack_top, + &profile_builder, ®ister_context); + + uintptr_t* stack_copy_bottom = + reinterpret_cast<uintptr_t*>(stack_buffer.get()->buffer()); + std::vector<uintptr_t> stack_copy(stack_copy_bottom, + stack_copy_bottom + stack.size()); + // Use the buffer not being overwritten as a proxy for the unwind being + // aborted. + EXPECT_NE(stack, stack_copy); +} + +TEST(StackCopierSuspendTest, CopyStackAndRewritePointers) { + // Allocate space for the stack, then make its elements point to themselves. + std::vector<uintptr_t> stack(2); + stack[0] = reinterpret_cast<uintptr_t>(&stack[0]); + stack[1] = reinterpret_cast<uintptr_t>(&stack[1]); + StackCopierSuspend stack_copier_suspend( + std::make_unique<TestSuspendableThreadDelegate>(stack)); + + std::unique_ptr<StackBuffer> stack_buffer = + std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t)); + uintptr_t stack_top = 0; + TestProfileBuilder profile_builder; + RegisterContext register_context = {0}; + stack_copier_suspend.CopyStack(stack_buffer.get(), &stack_top, + &profile_builder, ®ister_context); + + uintptr_t* stack_copy_bottom = + reinterpret_cast<uintptr_t*>(stack_buffer.get()->buffer()); + std::vector<uintptr_t> stack_copy(stack_copy_bottom, + stack_copy_bottom + stack.size()); + EXPECT_THAT(stack_copy, + ElementsAre(reinterpret_cast<uintptr_t>(stack_copy_bottom), + reinterpret_cast<uintptr_t>(stack_copy_bottom) + + sizeof(uintptr_t))); +} + +TEST(StackCopierSuspendTest, RewriteRegisters) { + std::vector<uintptr_t> stack = {0, 1, 2}; + RegisterContext register_context = {0}; + RegisterContextFramePointer(®ister_context) = + reinterpret_cast<uintptr_t>(&stack[1]); + StackCopierSuspend stack_copier_suspend( + std::make_unique<TestSuspendableThreadDelegate>(stack, + ®ister_context)); + + std::unique_ptr<StackBuffer> stack_buffer = + std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t)); + uintptr_t stack_top = 0; + TestProfileBuilder profile_builder; + stack_copier_suspend.CopyStack(stack_buffer.get(), &stack_top, + &profile_builder, ®ister_context); + + uintptr_t stack_copy_bottom = + reinterpret_cast<uintptr_t>(stack_buffer.get()->buffer()); + EXPECT_EQ(stack_copy_bottom + sizeof(uintptr_t), + RegisterContextFramePointer(®ister_context)); +} + +} // namespace base diff --git a/chromium/base/profiler/stack_copier_unittest.cc b/chromium/base/profiler/stack_copier_unittest.cc index a065c9f9a72..b4a81ef4b8b 100644 --- a/chromium/base/profiler/stack_copier_unittest.cc +++ b/chromium/base/profiler/stack_copier_unittest.cc @@ -15,6 +15,12 @@ namespace base { namespace { +class CopyFunctions : public StackCopier { + public: + using StackCopier::CopyStackContentsAndRewritePointers; + using StackCopier::RewritePointerIfInOriginalStack; +}; + static constexpr size_t kTestStackBufferSize = sizeof(uintptr_t) * 4; union alignas(StackBuffer::kPlatformStackAlignment) TestStackBuffer { @@ -29,7 +35,7 @@ TEST(StackCopierTest, RewritePointerIfInOriginalStack_InStack) { uintptr_t original_stack[4]; uintptr_t stack_copy[4]; EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack_copy[2]), - StackCopier::RewritePointerIfInOriginalStack( + CopyFunctions::RewritePointerIfInOriginalStack( reinterpret_cast<uint8_t*>(&original_stack[0]), &original_stack[0] + base::size(original_stack), reinterpret_cast<uint8_t*>(&stack_copy[0]), @@ -44,7 +50,7 @@ TEST(StackCopierTest, RewritePointerIfInOriginalStack_NotInStack) { uintptr_t stack_copy[4]; EXPECT_EQ(reinterpret_cast<uintptr_t>(&non_stack_location), - StackCopier::RewritePointerIfInOriginalStack( + CopyFunctions::RewritePointerIfInOriginalStack( reinterpret_cast<uint8_t*>(&original_stack[0]), &original_stack[0] + size(original_stack), reinterpret_cast<uint8_t*>(&stack_copy[0]), @@ -62,7 +68,7 @@ TEST(StackCopierTest, StackCopy) { reinterpret_cast<uintptr_t>(&original_stack.as_uintptr[1]); TestStackBuffer stack_copy; - StackCopier::CopyStackContentsAndRewritePointers( + CopyFunctions::CopyStackContentsAndRewritePointers( &original_stack.as_uint8[0], &original_stack.as_uintptr[0] + size(original_stack.as_uintptr), StackBuffer::kPlatformStackAlignment, &stack_copy.as_uintptr[0]); @@ -97,7 +103,7 @@ TEST(StackCopierTest, StackCopy_NonAlignedStackPointerCopy) { TestStackBuffer stack_copy_buffer = {{0}}; const uint8_t* stack_copy_bottom = - StackCopier::CopyStackContentsAndRewritePointers( + CopyFunctions::CopyStackContentsAndRewritePointers( unaligned_stack_bottom, stack_top, StackBuffer::kPlatformStackAlignment, &stack_copy_buffer.as_uintptr[0]); @@ -144,7 +150,7 @@ TEST(StackCopierTest, StackCopy_NonAlignedStackPointerUnalignedRewriteAtStart) { TestStackBuffer stack_copy_buffer = {{0}}; const uint8_t* stack_copy_bottom = - StackCopier::CopyStackContentsAndRewritePointers( + CopyFunctions::CopyStackContentsAndRewritePointers( unaligned_stack_bottom, &stack_buffer.as_uintptr[0] + size(stack_buffer.as_uintptr), StackBuffer::kPlatformStackAlignment, @@ -181,7 +187,7 @@ TEST(StackCopierTest, TestStackBuffer stack_copy_buffer = {{0}}; const uint8_t* stack_copy_bottom = - StackCopier::CopyStackContentsAndRewritePointers( + CopyFunctions::CopyStackContentsAndRewritePointers( unaligned_stack_bottom, &stack_buffer.as_uintptr[0] + size(stack_buffer.as_uintptr), StackBuffer::kPlatformStackAlignment, @@ -212,7 +218,7 @@ TEST(StackCopierTest, StackCopy_NonAlignedStackPointerAlignedRewrite) { TestStackBuffer stack_copy_buffer = {{0}}; - StackCopier::CopyStackContentsAndRewritePointers( + CopyFunctions::CopyStackContentsAndRewritePointers( unaligned_stack_bottom, &stack_buffer.as_uintptr[0] + size(stack_buffer.as_uintptr), StackBuffer::kPlatformStackAlignment, &stack_copy_buffer.as_uintptr[0]); diff --git a/chromium/base/profiler/stack_sampler_android.cc b/chromium/base/profiler/stack_sampler_android.cc index e3190e3a69d..85b3626b53e 100644 --- a/chromium/base/profiler/stack_sampler_android.cc +++ b/chromium/base/profiler/stack_sampler_android.cc @@ -7,6 +7,7 @@ #include <pthread.h> #include "base/profiler/native_unwinder_android.h" +#include "base/profiler/stack_copier_signal.h" #include "base/profiler/stack_sampler_impl.h" #include "base/profiler/thread_delegate_android.h" #include "base/threading/platform_thread.h" @@ -18,7 +19,8 @@ std::unique_ptr<StackSampler> StackSampler::Create( ModuleCache* module_cache, StackSamplerTestDelegate* test_delegate) { return std::make_unique<StackSamplerImpl>( - std::make_unique<ThreadDelegateAndroid>(), + std::make_unique<StackCopierSignal>( + std::make_unique<ThreadDelegateAndroid>(thread_id)), std::make_unique<NativeUnwinderAndroid>(), module_cache, test_delegate); } diff --git a/chromium/base/profiler/stack_sampler_impl.cc b/chromium/base/profiler/stack_sampler_impl.cc index 1e63b54ed2f..f80e9c91fcf 100644 --- a/chromium/base/profiler/stack_sampler_impl.cc +++ b/chromium/base/profiler/stack_sampler_impl.cc @@ -11,7 +11,7 @@ #include "base/profiler/sample_metadata.h" #include "base/profiler/stack_buffer.h" #include "base/profiler/stack_copier.h" -#include "base/profiler/thread_delegate.h" +#include "base/profiler/suspendable_thread_delegate.h" #include "base/profiler/unwinder.h" // IMPORTANT NOTE: Some functions within this implementation are invoked while @@ -23,12 +23,11 @@ namespace base { -StackSamplerImpl::StackSamplerImpl( - std::unique_ptr<ThreadDelegate> thread_delegate, - std::unique_ptr<Unwinder> native_unwinder, - ModuleCache* module_cache, - StackSamplerTestDelegate* test_delegate) - : thread_delegate_(std::move(thread_delegate)), +StackSamplerImpl::StackSamplerImpl(std::unique_ptr<StackCopier> stack_copier, + std::unique_ptr<Unwinder> native_unwinder, + ModuleCache* module_cache, + StackSamplerTestDelegate* test_delegate) + : stack_copier_(std::move(stack_copier)), native_unwinder_(std::move(native_unwinder)), module_cache_(module_cache), test_delegate_(test_delegate) {} @@ -46,8 +45,8 @@ void StackSamplerImpl::RecordStackFrames(StackBuffer* stack_buffer, RegisterContext thread_context; uintptr_t stack_top; - bool success = - CopyStack(stack_buffer, &stack_top, profile_builder, &thread_context); + bool success = stack_copier_->CopyStack(stack_buffer, &stack_top, + profile_builder, &thread_context); if (!success) return; @@ -70,66 +69,6 @@ std::vector<Frame> StackSamplerImpl::WalkStackForTesting( aux_unwinder); } -// Suspends the thread, copies its stack, top address of the stack copy, and -// register context, records the current metadata, then resumes the thread. -// Returns true on success, and returns the copied state via the params. NO HEAP -// ALLOCATIONS within the ScopedSuspendThread scope. -bool StackSamplerImpl::CopyStack(StackBuffer* stack_buffer, - uintptr_t* stack_top, - ProfileBuilder* profile_builder, - RegisterContext* thread_context) { - const uintptr_t top = thread_delegate_->GetStackBaseAddress(); - uintptr_t bottom = 0; - const uint8_t* stack_copy_bottom = nullptr; - { - // The MetadataProvider must be created before the ScopedSuspendThread - // because it acquires a lock in its constructor that might otherwise be - // held by the target thread, resulting in deadlock. - std::unique_ptr<base::ProfileBuilder::MetadataProvider> get_metadata_items = - base::GetSampleMetadataRecorder()->CreateMetadataProvider(); - - // Allocation of the ScopedSuspendThread object itself is OK since it - // necessarily occurs before the thread is suspended by the object. - std::unique_ptr<ThreadDelegate::ScopedSuspendThread> suspend_thread = - thread_delegate_->CreateScopedSuspendThread(); - - if (!suspend_thread->WasSuccessful()) - return false; - - if (!thread_delegate_->GetThreadContext(thread_context)) - return false; - - bottom = RegisterContextStackPointer(thread_context); - - // The StackBuffer allocation is expected to be at least as large as the - // largest stack region allocation on the platform, but check just in case - // it isn't *and* the actual stack itself exceeds the buffer allocation - // size. - if ((top - bottom) > stack_buffer->size()) - return false; - - if (!thread_delegate_->CanCopyStack(bottom)) - return false; - - profile_builder->RecordMetadata(get_metadata_items.get()); - - stack_copy_bottom = StackCopier::CopyStackContentsAndRewritePointers( - reinterpret_cast<uint8_t*>(bottom), reinterpret_cast<uintptr_t*>(top), - StackBuffer::kPlatformStackAlignment, stack_buffer->buffer()); - } - - *stack_top = reinterpret_cast<uintptr_t>(stack_copy_bottom) + (top - bottom); - - for (uintptr_t* reg : - thread_delegate_->GetRegistersToRewrite(thread_context)) { - *reg = StackCopier::RewritePointerIfInOriginalStack( - reinterpret_cast<uint8_t*>(bottom), reinterpret_cast<uintptr_t*>(top), - stack_copy_bottom, *reg); - } - - return true; -} - // static std::vector<Frame> StackSamplerImpl::WalkStack(ModuleCache* module_cache, RegisterContext* thread_context, diff --git a/chromium/base/profiler/stack_sampler_impl.h b/chromium/base/profiler/stack_sampler_impl.h index b28d90c1d04..226ccdb9435 100644 --- a/chromium/base/profiler/stack_sampler_impl.h +++ b/chromium/base/profiler/stack_sampler_impl.h @@ -14,14 +14,14 @@ namespace base { -class ThreadDelegate; +class StackCopier; class Unwinder; -// Cross-platform stack sampler implementation. Delegates to ThreadDelegate for -// platform-specific implementation. +// Cross-platform stack sampler implementation. Delegates to StackCopier for the +// platform-specific stack copying implementation. class BASE_EXPORT StackSamplerImpl : public StackSampler { public: - StackSamplerImpl(std::unique_ptr<ThreadDelegate> delegate, + StackSamplerImpl(std::unique_ptr<StackCopier> stack_copier, std::unique_ptr<Unwinder> native_unwinder, ModuleCache* module_cache, StackSamplerTestDelegate* test_delegate = nullptr); @@ -43,18 +43,13 @@ class BASE_EXPORT StackSamplerImpl : public StackSampler { Unwinder* aux_unwinder); private: - bool CopyStack(StackBuffer* stack_buffer, - uintptr_t* stack_top, - ProfileBuilder* profile_builder, - RegisterContext* thread_context); - static std::vector<Frame> WalkStack(ModuleCache* module_cache, RegisterContext* thread_context, uintptr_t stack_top, Unwinder* native_unwinder, Unwinder* aux_unwinder); - const std::unique_ptr<ThreadDelegate> thread_delegate_; + const std::unique_ptr<StackCopier> stack_copier_; const std::unique_ptr<Unwinder> native_unwinder_; std::unique_ptr<Unwinder> aux_unwinder_; ModuleCache* const module_cache_; diff --git a/chromium/base/profiler/stack_sampler_impl_unittest.cc b/chromium/base/profiler/stack_sampler_impl_unittest.cc index f9d56c3a239..5e3389db705 100644 --- a/chromium/base/profiler/stack_sampler_impl_unittest.cc +++ b/chromium/base/profiler/stack_sampler_impl_unittest.cc @@ -10,8 +10,9 @@ #include "base/profiler/profile_builder.h" #include "base/profiler/stack_buffer.h" +#include "base/profiler/stack_copier.h" #include "base/profiler/stack_sampler_impl.h" -#include "base/profiler/thread_delegate.h" +#include "base/profiler/suspendable_thread_delegate.h" #include "base/profiler/unwinder.h" #include "base/sampling_heap_profiler/module_cache.h" #include "base/stl_util.h" @@ -44,60 +45,31 @@ class TestProfileBuilder : public ProfileBuilder { ModuleCache* module_cache_; }; -// A thread delegate for use in tests that provides the expected behavior when +// A stack copier for use in tests that provides the expected behavior when // operating on the supplied fake stack. -class TestThreadDelegate : public ThreadDelegate { +class TestStackCopier : public StackCopier { public: - class TestScopedSuspendThread : public ThreadDelegate::ScopedSuspendThread { - public: - TestScopedSuspendThread() = default; - - TestScopedSuspendThread(const TestScopedSuspendThread&) = delete; - TestScopedSuspendThread& operator=(const TestScopedSuspendThread&) = delete; - - bool WasSuccessful() const override { return true; } - }; - - TestThreadDelegate(const std::vector<uintptr_t>& fake_stack, - // The register context will be initialized to - // *|thread_context| if non-null. - RegisterContext* thread_context = nullptr) - : fake_stack_(fake_stack), thread_context_(thread_context) {} - - TestThreadDelegate(const TestThreadDelegate&) = delete; - TestThreadDelegate& operator=(const TestThreadDelegate&) = delete; - - std::unique_ptr<ScopedSuspendThread> CreateScopedSuspendThread() override { - return std::make_unique<TestScopedSuspendThread>(); - } - - bool GetThreadContext(RegisterContext* thread_context) override { - if (thread_context_) - *thread_context = *thread_context_; + TestStackCopier(const std::vector<uintptr_t>& fake_stack) + : fake_stack_(fake_stack) {} + + bool CopyStack(StackBuffer* stack_buffer, + uintptr_t* stack_top, + ProfileBuilder* profile_builder, + RegisterContext* thread_context) override { + std::memcpy(stack_buffer->buffer(), &fake_stack_[0], fake_stack_.size()); + *stack_top = + reinterpret_cast<uintptr_t>(&fake_stack_[0] + fake_stack_.size()); // Set the stack pointer to be consistent with the provided fake stack. RegisterContextStackPointer(thread_context) = reinterpret_cast<uintptr_t>(&fake_stack_[0]); - RegisterContextInstructionPointer(thread_context) = - reinterpret_cast<uintptr_t>(fake_stack_[0]); - return true; - } - - uintptr_t GetStackBaseAddress() const override { - return reinterpret_cast<uintptr_t>(&fake_stack_[0] + fake_stack_.size()); - } - - bool CanCopyStack(uintptr_t stack_pointer) override { return true; } - std::vector<uintptr_t*> GetRegistersToRewrite( - RegisterContext* thread_context) override { - return {&RegisterContextFramePointer(thread_context)}; + return true; } private: // Must be a reference to retain the underlying allocation from the vector // passed to the constructor. const std::vector<uintptr_t>& fake_stack_; - RegisterContext* thread_context_; }; // Trivial unwinder implementation for testing. @@ -230,13 +202,19 @@ class FakeTestUnwinder : public Unwinder { } // namespace -TEST(StackSamplerImplTest, CopyStack) { +// TODO(crbug.com/1001923): Fails on Linux MSan. +#if defined(OS_LINUX) +#define MAYBE_CopyStack DISABLED_MAYBE_CopyStack +#else +#define MAYBE_CopyStack CopyStack +#endif +TEST(StackSamplerImplTest, MAYBE_CopyStack) { ModuleCache module_cache; const std::vector<uintptr_t> stack = {0, 1, 2, 3, 4}; InjectModuleForContextInstructionPointer(stack, &module_cache); std::vector<uintptr_t> stack_copy; StackSamplerImpl stack_sampler_impl( - std::make_unique<TestThreadDelegate>(stack), + std::make_unique<TestStackCopier>(stack), std::make_unique<TestUnwinder>(stack.size(), &stack_copy), &module_cache); std::unique_ptr<StackBuffer> stack_buffer = @@ -247,75 +225,6 @@ TEST(StackSamplerImplTest, CopyStack) { EXPECT_EQ(stack, stack_copy); } -TEST(StackSamplerImplTest, CopyStackBufferTooSmall) { - ModuleCache module_cache; - std::vector<uintptr_t> stack = {0, 1, 2, 3, 4}; - InjectModuleForContextInstructionPointer(stack, &module_cache); - std::vector<uintptr_t> stack_copy; - StackSamplerImpl stack_sampler_impl( - std::make_unique<TestThreadDelegate>(stack), - std::make_unique<TestUnwinder>(stack.size(), &stack_copy), &module_cache); - - std::unique_ptr<StackBuffer> stack_buffer = - std::make_unique<StackBuffer>((stack.size() - 1) * sizeof(uintptr_t)); - // Make the buffer different than the input stack. - stack_buffer->buffer()[0] = 100; - TestProfileBuilder profile_builder(&module_cache); - - stack_sampler_impl.RecordStackFrames(stack_buffer.get(), &profile_builder); - - // Use the buffer not being overwritten as a proxy for the unwind being - // aborted. - EXPECT_NE(stack, stack_copy); -} - -TEST(StackSamplerImplTest, CopyStackAndRewritePointers) { - ModuleCache module_cache; - // Allocate space for the stack, then make its elements point to themselves. - std::vector<uintptr_t> stack(2); - stack[0] = reinterpret_cast<uintptr_t>(&stack[0]); - stack[1] = reinterpret_cast<uintptr_t>(&stack[1]); - InjectModuleForContextInstructionPointer(stack, &module_cache); - std::vector<uintptr_t> stack_copy; - uintptr_t stack_copy_bottom; - StackSamplerImpl stack_sampler_impl( - std::make_unique<TestThreadDelegate>(stack), - std::make_unique<TestUnwinder>(stack.size(), &stack_copy, - &stack_copy_bottom), - &module_cache); - - std::unique_ptr<StackBuffer> stack_buffer = - std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t)); - TestProfileBuilder profile_builder(&module_cache); - - stack_sampler_impl.RecordStackFrames(stack_buffer.get(), &profile_builder); - - EXPECT_THAT(stack_copy, ElementsAre(stack_copy_bottom, - stack_copy_bottom + sizeof(uintptr_t))); -} - -TEST(StackSamplerImplTest, RewriteRegisters) { - ModuleCache module_cache; - std::vector<uintptr_t> stack = {0, 1, 2}; - InjectModuleForContextInstructionPointer(stack, &module_cache); - uintptr_t stack_copy_bottom; - RegisterContext thread_context; - RegisterContextFramePointer(&thread_context) = - reinterpret_cast<uintptr_t>(&stack[1]); - StackSamplerImpl stack_sampler_impl( - std::make_unique<TestThreadDelegate>(stack, &thread_context), - std::make_unique<TestUnwinder>(stack.size(), nullptr, &stack_copy_bottom), - &module_cache); - - std::unique_ptr<StackBuffer> stack_buffer = - std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t)); - TestProfileBuilder profile_builder(&module_cache); - stack_sampler_impl.RecordStackFrames(stack_buffer.get(), &profile_builder); - - EXPECT_EQ(stack_copy_bottom + sizeof(uintptr_t), - RegisterContextFramePointer(&thread_context)); -} - TEST(StackSamplerImplTest, WalkStack_Completed) { ModuleCache module_cache; RegisterContext thread_context; diff --git a/chromium/base/profiler/stack_sampler_mac.cc b/chromium/base/profiler/stack_sampler_mac.cc index 54ca505e216..1ab0eaf7a7f 100644 --- a/chromium/base/profiler/stack_sampler_mac.cc +++ b/chromium/base/profiler/stack_sampler_mac.cc @@ -5,8 +5,9 @@ #include "base/profiler/stack_sampler.h" #include "base/profiler/native_unwinder_mac.h" +#include "base/profiler/stack_copier_suspend.h" #include "base/profiler/stack_sampler_impl.h" -#include "base/profiler/thread_delegate_mac.h" +#include "base/profiler/suspendable_thread_delegate_mac.h" namespace base { @@ -16,7 +17,8 @@ std::unique_ptr<StackSampler> StackSampler::Create( ModuleCache* module_cache, StackSamplerTestDelegate* test_delegate) { return std::make_unique<StackSamplerImpl>( - std::make_unique<ThreadDelegateMac>(thread_id), + std::make_unique<StackCopierSuspend>( + std::make_unique<SuspendableThreadDelegateMac>(thread_id)), std::make_unique<NativeUnwinderMac>(module_cache), module_cache, test_delegate); } diff --git a/chromium/base/profiler/stack_sampler_win.cc b/chromium/base/profiler/stack_sampler_win.cc index fb09fb79709..83307609aa1 100644 --- a/chromium/base/profiler/stack_sampler_win.cc +++ b/chromium/base/profiler/stack_sampler_win.cc @@ -5,8 +5,9 @@ #include "base/profiler/stack_sampler.h" #include "base/profiler/native_unwinder_win.h" +#include "base/profiler/stack_copier_suspend.h" #include "base/profiler/stack_sampler_impl.h" -#include "base/profiler/thread_delegate_win.h" +#include "base/profiler/suspendable_thread_delegate_win.h" #include "build/build_config.h" namespace base { @@ -18,7 +19,8 @@ std::unique_ptr<StackSampler> StackSampler::Create( StackSamplerTestDelegate* test_delegate) { #if defined(ARCH_CPU_X86_64) || defined(ARCH_CPU_ARM64) return std::make_unique<StackSamplerImpl>( - std::make_unique<ThreadDelegateWin>(thread_id), + std::make_unique<StackCopierSuspend>( + std::make_unique<SuspendableThreadDelegateWin>(thread_id)), std::make_unique<NativeUnwinderWin>(), module_cache, test_delegate); #else return nullptr; diff --git a/chromium/base/profiler/suspendable_thread_delegate.h b/chromium/base/profiler/suspendable_thread_delegate.h new file mode 100644 index 00000000000..b5dfcd7bf12 --- /dev/null +++ b/chromium/base/profiler/suspendable_thread_delegate.h @@ -0,0 +1,59 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_PROFILER_SUSPENDABLE_THREAD_DELEGATE_H_ +#define BASE_PROFILER_SUSPENDABLE_THREAD_DELEGATE_H_ + +#include <vector> + +#include "base/base_export.h" +#include "base/profiler/register_context.h" +#include "base/profiler/thread_delegate.h" + +namespace base { + +// Platform-specific thread and stack manipulation delegate, for use by the +// platform-independent stack copying/walking implementation in +// StackSamplerImpl for suspension-based stack copying. +// +// IMPORTANT NOTE: Most methods in this interface are invoked while the target +// thread is suspended so must not do any allocation from the heap, including +// indirectly via use of DCHECK/CHECK or other logging statements. Otherwise the +// implementation can deadlock on heap locks acquired by the target thread +// before it was suspended. These functions are commented with "NO HEAP +// ALLOCATIONS". +class BASE_EXPORT SuspendableThreadDelegate : public ThreadDelegate { + public: + // Implementations of this interface should suspend the thread for the + // object's lifetime. NO HEAP ALLOCATIONS between the time the thread is + // suspended and resumed. + class BASE_EXPORT ScopedSuspendThread { + public: + ScopedSuspendThread() = default; + virtual ~ScopedSuspendThread() = default; + + ScopedSuspendThread(const ScopedSuspendThread&) = delete; + ScopedSuspendThread& operator=(const ScopedSuspendThread&) = delete; + + virtual bool WasSuccessful() const = 0; + }; + + SuspendableThreadDelegate() = default; + + // Creates an object that holds the thread suspended for its lifetime. + virtual std::unique_ptr<ScopedSuspendThread> CreateScopedSuspendThread() = 0; + + // Gets the register context for the thread. + // NO HEAP ALLOCATIONS. + virtual bool GetThreadContext(RegisterContext* thread_context) = 0; + + // Returns true if the thread's stack can be copied, where the bottom address + // of the thread is at |stack_pointer|. + // NO HEAP ALLOCATIONS. + virtual bool CanCopyStack(uintptr_t stack_pointer) = 0; +}; + +} // namespace base + +#endif // BASE_PROFILER_SUSPENDABLE_THREAD_DELEGATE_H_ diff --git a/chromium/base/profiler/thread_delegate_mac.cc b/chromium/base/profiler/suspendable_thread_delegate_mac.cc index 7632c4efed5..81323969d74 100644 --- a/chromium/base/profiler/thread_delegate_mac.cc +++ b/chromium/base/profiler/suspendable_thread_delegate_mac.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/profiler/thread_delegate_mac.h" +#include "base/profiler/suspendable_thread_delegate_mac.h" #include <mach/mach.h> #include <mach/thread_act.h> @@ -36,7 +36,7 @@ bool GetThreadState(thread_act_t target_thread, x86_thread_state64_t* state) { // ScopedSuspendThread -------------------------------------------------------- // NO HEAP ALLOCATIONS after thread_suspend. -ThreadDelegateMac::ScopedSuspendThread::ScopedSuspendThread( +SuspendableThreadDelegateMac::ScopedSuspendThread::ScopedSuspendThread( mach_port_t thread_port) : thread_port_(thread_suspend(thread_port) == KERN_SUCCESS ? thread_port @@ -44,7 +44,7 @@ ThreadDelegateMac::ScopedSuspendThread::ScopedSuspendThread( // NO HEAP ALLOCATIONS. The MACH_CHECK is OK because it provides a more noisy // failure mode than deadlocking. -ThreadDelegateMac::ScopedSuspendThread::~ScopedSuspendThread() { +SuspendableThreadDelegateMac::ScopedSuspendThread::~ScopedSuspendThread() { if (!WasSuccessful()) return; @@ -52,13 +52,14 @@ ThreadDelegateMac::ScopedSuspendThread::~ScopedSuspendThread() { MACH_CHECK(kr == KERN_SUCCESS, kr) << "thread_resume"; } -bool ThreadDelegateMac::ScopedSuspendThread::WasSuccessful() const { +bool SuspendableThreadDelegateMac::ScopedSuspendThread::WasSuccessful() const { return thread_port_ != MACH_PORT_NULL; } -// ThreadDelegateMac ---------------------------------------------------------- +// SuspendableThreadDelegateMac ----------------------------------------------- -ThreadDelegateMac::ThreadDelegateMac(mach_port_t thread_port) +SuspendableThreadDelegateMac::SuspendableThreadDelegateMac( + mach_port_t thread_port) : thread_port_(thread_port), thread_stack_base_address_(reinterpret_cast<uintptr_t>( pthread_get_stackaddr_np(pthread_from_mach_thread_np(thread_port)))) { @@ -70,29 +71,30 @@ ThreadDelegateMac::ThreadDelegateMac(mach_port_t thread_port) GetThreadState(thread_port_, &thread_state); } -ThreadDelegateMac::~ThreadDelegateMac() = default; +SuspendableThreadDelegateMac::~SuspendableThreadDelegateMac() = default; -std::unique_ptr<ThreadDelegate::ScopedSuspendThread> -ThreadDelegateMac::CreateScopedSuspendThread() { +std::unique_ptr<SuspendableThreadDelegate::ScopedSuspendThread> +SuspendableThreadDelegateMac::CreateScopedSuspendThread() { return std::make_unique<ScopedSuspendThread>(thread_port_); } // NO HEAP ALLOCATIONS. -bool ThreadDelegateMac::GetThreadContext(x86_thread_state64_t* thread_context) { +bool SuspendableThreadDelegateMac::GetThreadContext( + x86_thread_state64_t* thread_context) { return GetThreadState(thread_port_, thread_context); } // NO HEAP ALLOCATIONS. -uintptr_t ThreadDelegateMac::GetStackBaseAddress() const { +uintptr_t SuspendableThreadDelegateMac::GetStackBaseAddress() const { return thread_stack_base_address_; } // NO HEAP ALLOCATIONS. -bool ThreadDelegateMac::CanCopyStack(uintptr_t stack_pointer) { +bool SuspendableThreadDelegateMac::CanCopyStack(uintptr_t stack_pointer) { return true; } -std::vector<uintptr_t*> ThreadDelegateMac::GetRegistersToRewrite( +std::vector<uintptr_t*> SuspendableThreadDelegateMac::GetRegistersToRewrite( x86_thread_state64_t* thread_context) { return { &AsUintPtr(&thread_context->__rbx), &AsUintPtr(&thread_context->__rbp), diff --git a/chromium/base/profiler/thread_delegate_mac.h b/chromium/base/profiler/suspendable_thread_delegate_mac.h index 1ff2497fe1e..de3f30668ed 100644 --- a/chromium/base/profiler/thread_delegate_mac.h +++ b/chromium/base/profiler/suspendable_thread_delegate_mac.h @@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_PROFILER_THREAD_DELEGATE_MAC_H_ -#define BASE_PROFILER_THREAD_DELEGATE_MAC_H_ +#ifndef BASE_PROFILER_SUSPENDABLE_THREAD_DELEGATE_MAC_H_ +#define BASE_PROFILER_SUSPENDABLE_THREAD_DELEGATE_MAC_H_ #include <mach/mach.h> #include "base/base_export.h" #include "base/profiler/native_unwinder_mac.h" -#include "base/profiler/thread_delegate.h" +#include "base/profiler/suspendable_thread_delegate.h" #include "base/sampling_heap_profiler/module_cache.h" #include "base/threading/platform_thread.h" @@ -17,9 +17,11 @@ namespace base { // Platform- and thread-specific implementation in support of stack sampling on // Mac. -class BASE_EXPORT ThreadDelegateMac : public ThreadDelegate { +class BASE_EXPORT SuspendableThreadDelegateMac + : public SuspendableThreadDelegate { public: - class ScopedSuspendThread : public ThreadDelegate::ScopedSuspendThread { + class ScopedSuspendThread + : public SuspendableThreadDelegate::ScopedSuspendThread { public: explicit ScopedSuspendThread(mach_port_t thread_port); ~ScopedSuspendThread() override; @@ -33,14 +35,15 @@ class BASE_EXPORT ThreadDelegateMac : public ThreadDelegate { mach_port_t thread_port_; }; - ThreadDelegateMac(mach_port_t thread_port); - ~ThreadDelegateMac() override; + SuspendableThreadDelegateMac(mach_port_t thread_port); + ~SuspendableThreadDelegateMac() override; - ThreadDelegateMac(const ThreadDelegateMac&) = delete; - ThreadDelegateMac& operator=(const ThreadDelegateMac&) = delete; + SuspendableThreadDelegateMac(const SuspendableThreadDelegateMac&) = delete; + SuspendableThreadDelegateMac& operator=(const SuspendableThreadDelegateMac&) = + delete; - // ThreadDelegate - std::unique_ptr<ThreadDelegate::ScopedSuspendThread> + // SuspendableThreadDelegate + std::unique_ptr<SuspendableThreadDelegate::ScopedSuspendThread> CreateScopedSuspendThread() override; bool GetThreadContext(x86_thread_state64_t* thread_context) override; uintptr_t GetStackBaseAddress() const override; @@ -58,4 +61,4 @@ class BASE_EXPORT ThreadDelegateMac : public ThreadDelegate { } // namespace base -#endif // BASE_PROFILER_THREAD_DELEGATE_MAC_H_ +#endif // BASE_PROFILER_SUSPENDABLE_THREAD_DELEGATE_MAC_H_ diff --git a/chromium/base/profiler/thread_delegate_win.cc b/chromium/base/profiler/suspendable_thread_delegate_win.cc index 71ba5aaee47..d7369e50b68 100644 --- a/chromium/base/profiler/thread_delegate_win.cc +++ b/chromium/base/profiler/suspendable_thread_delegate_win.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/profiler/thread_delegate_win.h" +#include "base/profiler/suspendable_thread_delegate_win.h" #include <windows.h> #include <winternl.h> @@ -136,7 +136,7 @@ ScopedDisablePriorityBoost::~ScopedDisablePriorityBoost() { // ScopedSuspendThread -------------------------------------------------------- // NO HEAP ALLOCATIONS after ::SuspendThread. -ThreadDelegateWin::ScopedSuspendThread::ScopedSuspendThread( +SuspendableThreadDelegateWin::ScopedSuspendThread::ScopedSuspendThread( HANDLE thread_handle) : thread_handle_(thread_handle), was_successful_(::SuspendThread(thread_handle) != @@ -144,7 +144,7 @@ ThreadDelegateWin::ScopedSuspendThread::ScopedSuspendThread( // NO HEAP ALLOCATIONS. The CHECK is OK because it provides a more noisy failure // mode than deadlocking. -ThreadDelegateWin::ScopedSuspendThread::~ScopedSuspendThread() { +SuspendableThreadDelegateWin::ScopedSuspendThread::~ScopedSuspendThread() { if (!was_successful_) return; @@ -164,46 +164,48 @@ ThreadDelegateWin::ScopedSuspendThread::~ScopedSuspendThread() { CHECK(resume_thread_succeeded) << "ResumeThread failed: " << GetLastError(); } -bool ThreadDelegateWin::ScopedSuspendThread::WasSuccessful() const { +bool SuspendableThreadDelegateWin::ScopedSuspendThread::WasSuccessful() const { return was_successful_; } -// ThreadDelegateWin ---------------------------------------------------------- +// SuspendableThreadDelegateWin +// ---------------------------------------------------------- -ThreadDelegateWin::ThreadDelegateWin(PlatformThreadId thread_id) +SuspendableThreadDelegateWin::SuspendableThreadDelegateWin( + PlatformThreadId thread_id) : thread_handle_(GetThreadHandle(thread_id)), thread_stack_base_address_(reinterpret_cast<uintptr_t>( GetThreadEnvironmentBlock(thread_handle_.Get())->Tib.StackBase)) {} -ThreadDelegateWin::~ThreadDelegateWin() = default; +SuspendableThreadDelegateWin::~SuspendableThreadDelegateWin() = default; -std::unique_ptr<ThreadDelegate::ScopedSuspendThread> -ThreadDelegateWin::CreateScopedSuspendThread() { +std::unique_ptr<SuspendableThreadDelegate::ScopedSuspendThread> +SuspendableThreadDelegateWin::CreateScopedSuspendThread() { return std::make_unique<ScopedSuspendThread>(thread_handle_.Get()); } // NO HEAP ALLOCATIONS. -bool ThreadDelegateWin::GetThreadContext(CONTEXT* thread_context) { +bool SuspendableThreadDelegateWin::GetThreadContext(CONTEXT* thread_context) { *thread_context = {0}; thread_context->ContextFlags = CONTEXT_FULL; return ::GetThreadContext(thread_handle_.Get(), thread_context) != 0; } // NO HEAP ALLOCATIONS. -uintptr_t ThreadDelegateWin::GetStackBaseAddress() const { +uintptr_t SuspendableThreadDelegateWin::GetStackBaseAddress() const { return thread_stack_base_address_; } // Tests whether |stack_pointer| points to a location in the guard page. NO HEAP // ALLOCATIONS. -bool ThreadDelegateWin::CanCopyStack(uintptr_t stack_pointer) { +bool SuspendableThreadDelegateWin::CanCopyStack(uintptr_t stack_pointer) { // Dereferencing a pointer in the guard page in a thread that doesn't own the // stack results in a STATUS_GUARD_PAGE_VIOLATION exception and a crash. This // occurs very rarely, but reliably over the population. return !PointsToGuardPage(stack_pointer); } -std::vector<uintptr_t*> ThreadDelegateWin::GetRegistersToRewrite( +std::vector<uintptr_t*> SuspendableThreadDelegateWin::GetRegistersToRewrite( CONTEXT* thread_context) { // Return the set of non-volatile registers. return { @@ -215,7 +217,8 @@ std::vector<uintptr_t*> ThreadDelegateWin::GetRegistersToRewrite( &thread_context->X19, &thread_context->X20, &thread_context->X21, &thread_context->X22, &thread_context->X23, &thread_context->X24, &thread_context->X25, &thread_context->X26, &thread_context->X27, - &thread_context->X28, &thread_context->Fp, &thread_context->Lr + &thread_context->X28, &thread_context->Fp, &thread_context->Lr, + &thread_context->Sp #endif }; } diff --git a/chromium/base/profiler/thread_delegate_win.h b/chromium/base/profiler/suspendable_thread_delegate_win.h index 9e1d781e67b..2452dbe47db 100644 --- a/chromium/base/profiler/thread_delegate_win.h +++ b/chromium/base/profiler/suspendable_thread_delegate_win.h @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_PROFILER_THREAD_DELEGATE_WIN_H_ -#define BASE_PROFILER_THREAD_DELEGATE_WIN_H_ +#ifndef BASE_PROFILER_SUSPENDABLE_THREAD_DELEGATE_WIN_H_ +#define BASE_PROFILER_SUSPENDABLE_THREAD_DELEGATE_WIN_H_ #include <windows.h> #include "base/base_export.h" -#include "base/profiler/thread_delegate.h" +#include "base/profiler/suspendable_thread_delegate.h" #include "base/threading/platform_thread.h" #include "base/win/scoped_handle.h" @@ -16,9 +16,11 @@ namespace base { // Platform- and thread-specific implementation in support of stack sampling on // Windows. -class BASE_EXPORT ThreadDelegateWin : public ThreadDelegate { +class BASE_EXPORT SuspendableThreadDelegateWin + : public SuspendableThreadDelegate { public: - class ScopedSuspendThread : public ThreadDelegate::ScopedSuspendThread { + class ScopedSuspendThread + : public SuspendableThreadDelegate::ScopedSuspendThread { public: explicit ScopedSuspendThread(HANDLE thread_handle); ~ScopedSuspendThread() override; @@ -32,14 +34,15 @@ class BASE_EXPORT ThreadDelegateWin : public ThreadDelegate { DISALLOW_COPY_AND_ASSIGN(ScopedSuspendThread); }; - explicit ThreadDelegateWin(PlatformThreadId thread_id); - ~ThreadDelegateWin() override; + explicit SuspendableThreadDelegateWin(PlatformThreadId thread_id); + ~SuspendableThreadDelegateWin() override; - ThreadDelegateWin(const ThreadDelegateWin&) = delete; - ThreadDelegateWin& operator=(const ThreadDelegateWin&) = delete; + SuspendableThreadDelegateWin(const SuspendableThreadDelegateWin&) = delete; + SuspendableThreadDelegateWin& operator=(const SuspendableThreadDelegateWin&) = + delete; - // ThreadDelegate - std::unique_ptr<ThreadDelegate::ScopedSuspendThread> + // SuspendableThreadDelegate + std::unique_ptr<SuspendableThreadDelegate::ScopedSuspendThread> CreateScopedSuspendThread() override; bool GetThreadContext(CONTEXT* thread_context) override; uintptr_t GetStackBaseAddress() const override; @@ -54,4 +57,4 @@ class BASE_EXPORT ThreadDelegateWin : public ThreadDelegate { } // namespace base -#endif // BASE_PROFILER_THREAD_DELEGATE_WIN_H_ +#endif // BASE_PROFILER_SUSPENDABLE_THREAD_DELEGATE_WIN_H_ diff --git a/chromium/base/profiler/thread_delegate.h b/chromium/base/profiler/thread_delegate.h index d0f0f028405..bb8b50b6354 100644 --- a/chromium/base/profiler/thread_delegate.h +++ b/chromium/base/profiler/thread_delegate.h @@ -8,58 +8,25 @@ #include <vector> #include "base/base_export.h" -#include "base/profiler/frame.h" #include "base/profiler/register_context.h" namespace base { // Platform-specific thread and stack manipulation delegate, for use by the // platform-independent stack copying/walking implementation in -// StackSamplerImpl. -// -// IMPORTANT NOTE: Most methods in this interface are invoked while the target -// thread is suspended so must not do any allocation from the heap, including -// indirectly via use of DCHECK/CHECK or other logging statements. Otherwise the -// implementation can deadlock on heap locks acquired by the target thread -// before it was suspended. These functions are commented with "NO HEAP -// ALLOCATIONS". +// StackSamplerImpl. Provides the common interface across signal- and +// suspend-based stack copy implementations. class BASE_EXPORT ThreadDelegate { public: - // Implementations of this interface should suspend the thread for the - // object's lifetime. NO HEAP ALLOCATIONS between the time the thread is - // suspended and resumed. - class BASE_EXPORT ScopedSuspendThread { - public: - ScopedSuspendThread() = default; - virtual ~ScopedSuspendThread() = default; - - ScopedSuspendThread(const ScopedSuspendThread&) = delete; - ScopedSuspendThread& operator=(const ScopedSuspendThread&) = delete; - - virtual bool WasSuccessful() const = 0; - }; - ThreadDelegate() = default; virtual ~ThreadDelegate() = default; ThreadDelegate(const ThreadDelegate&) = delete; ThreadDelegate& operator=(const ThreadDelegate&) = delete; - // Creates an object that holds the thread suspended for its lifetime. - virtual std::unique_ptr<ScopedSuspendThread> CreateScopedSuspendThread() = 0; - - // Gets the register context for the thread. - // NO HEAP ALLOCATIONS. - virtual bool GetThreadContext(RegisterContext* thread_context) = 0; - // Gets the base address of the thread's stack. virtual uintptr_t GetStackBaseAddress() const = 0; - // Returns true if the thread's stack can be copied, where the bottom address - // of the thread is at |stack_pointer|. - // NO HEAP ALLOCATIONS. - virtual bool CanCopyStack(uintptr_t stack_pointer) = 0; - // Returns a list of registers that should be rewritten to point into the // stack copy, if they originally pointed into the original stack. // May heap allocate. diff --git a/chromium/base/profiler/thread_delegate_android.cc b/chromium/base/profiler/thread_delegate_android.cc index f61f51f0224..4711cf6589c 100644 --- a/chromium/base/profiler/thread_delegate_android.cc +++ b/chromium/base/profiler/thread_delegate_android.cc @@ -2,8 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <pthread.h> + #include "base/profiler/thread_delegate_android.h" +#include "build/build_config.h" + // IMPORTANT NOTE: Some functions within this implementation are invoked while // the target thread is suspended so it must not do any allocation from the // heap, including indirectly via use of DCHECK/CHECK or other logging @@ -13,39 +17,50 @@ namespace base { -// ScopedSuspendThread -------------------------------------------------------- +namespace { -bool ThreadDelegateAndroid::ScopedSuspendThread::WasSuccessful() const { - return false; +uintptr_t GetThreadStackBaseAddress(PlatformThreadId thread_id) { + pthread_attr_t attr; + pthread_getattr_np(thread_id, &attr); + void* base_address; + size_t size; + pthread_attr_getstack(&attr, &base_address, &size); + return reinterpret_cast<uintptr_t>(base_address); } -// ThreadDelegateAndroid ------------------------------------------------------ +} // namespace -std::unique_ptr<ThreadDelegate::ScopedSuspendThread> -ThreadDelegateAndroid::CreateScopedSuspendThread() { - return std::make_unique<ScopedSuspendThread>(); -} +ThreadDelegateAndroid::ThreadDelegateAndroid(PlatformThreadId thread_id) + : thread_stack_base_address_(GetThreadStackBaseAddress(thread_id)) {} -// NO HEAP ALLOCATIONS. -bool ThreadDelegateAndroid::GetThreadContext(RegisterContext* thread_context) { - return false; -} - -// NO HEAP ALLOCATIONS. uintptr_t ThreadDelegateAndroid::GetStackBaseAddress() const { - // It's okay for the stub to return zero here: GetStackBaseAddress() if - // ScopedSuspendThread fails, which it always will in the stub. - return 0; -} - -// NO HEAP ALLOCATIONS. -bool ThreadDelegateAndroid::CanCopyStack(uintptr_t stack_pointer) { - return false; + return thread_stack_base_address_; } std::vector<uintptr_t*> ThreadDelegateAndroid::GetRegistersToRewrite( RegisterContext* thread_context) { +#if defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_32_BITS) + return { + reinterpret_cast<uintptr_t*>(&thread_context->arm_r0), + reinterpret_cast<uintptr_t*>(&thread_context->arm_r1), + reinterpret_cast<uintptr_t*>(&thread_context->arm_r2), + reinterpret_cast<uintptr_t*>(&thread_context->arm_r3), + reinterpret_cast<uintptr_t*>(&thread_context->arm_r4), + reinterpret_cast<uintptr_t*>(&thread_context->arm_r5), + reinterpret_cast<uintptr_t*>(&thread_context->arm_r6), + reinterpret_cast<uintptr_t*>(&thread_context->arm_r7), + reinterpret_cast<uintptr_t*>(&thread_context->arm_r8), + reinterpret_cast<uintptr_t*>(&thread_context->arm_r9), + reinterpret_cast<uintptr_t*>(&thread_context->arm_r10), + reinterpret_cast<uintptr_t*>(&thread_context->arm_fp), + reinterpret_cast<uintptr_t*>(&thread_context->arm_ip), + reinterpret_cast<uintptr_t*>(&thread_context->arm_sp), + // arm_lr and arm_pc do not require rewriting because they contain + // addresses of executable code, not addresses in the stack. + }; +#else // #if defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_32_BITS) return {}; +#endif } } // namespace base diff --git a/chromium/base/profiler/thread_delegate_android.h b/chromium/base/profiler/thread_delegate_android.h index 59ff674ac6d..96e1e3c45c1 100644 --- a/chromium/base/profiler/thread_delegate_android.h +++ b/chromium/base/profiler/thread_delegate_android.h @@ -7,41 +7,28 @@ #include "base/base_export.h" #include "base/profiler/thread_delegate.h" +#include "base/threading/platform_thread.h" namespace base { // Platform- and thread-specific implementation in support of stack sampling on // Android. // -// TODO(charliea): Implement this class. -// See: https://crbug.com/988574 +// TODO(https://crbug.com/988579): Implement this class. class BASE_EXPORT ThreadDelegateAndroid : public ThreadDelegate { public: - class ScopedSuspendThread : public ThreadDelegate::ScopedSuspendThread { - public: - ScopedSuspendThread() = default; - ~ScopedSuspendThread() override = default; - - ScopedSuspendThread(const ScopedSuspendThread&) = delete; - ScopedSuspendThread& operator=(const ScopedSuspendThread&) = delete; - - bool WasSuccessful() const override; - }; - - ThreadDelegateAndroid() = default; - ~ThreadDelegateAndroid() override = default; + ThreadDelegateAndroid(PlatformThreadId thread_id); ThreadDelegateAndroid(const ThreadDelegateAndroid&) = delete; ThreadDelegateAndroid& operator=(const ThreadDelegateAndroid&) = delete; // ThreadDelegate - std::unique_ptr<ThreadDelegate::ScopedSuspendThread> - CreateScopedSuspendThread() override; - bool GetThreadContext(RegisterContext* thread_context) override; uintptr_t GetStackBaseAddress() const override; - bool CanCopyStack(uintptr_t stack_pointer) override; std::vector<uintptr_t*> GetRegistersToRewrite( RegisterContext* thread_context) override; + + private: + const uintptr_t thread_stack_base_address_; }; } // namespace base diff --git a/chromium/base/sampling_heap_profiler/poisson_allocation_sampler.cc b/chromium/base/sampling_heap_profiler/poisson_allocation_sampler.cc index 6bee5c0e442..3d9d928f341 100644 --- a/chromium/base/sampling_heap_profiler/poisson_allocation_sampler.cc +++ b/chromium/base/sampling_heap_profiler/poisson_allocation_sampler.cc @@ -412,11 +412,11 @@ void PoissonAllocationSampler::RecordAlloc(void* address, return; if (UNLIKELY(!g_running.load(std::memory_order_relaxed))) { - // Sampling is in fact disabled. Put a large negative value into - // the accumulator. It needs to be large enough to have this code - // not trigger frequently, and small enough to eventually start collecting - // samples when the sampling is enabled. - g_accumulated_bytes_tls = -static_cast<intptr_t>(kWarmupInterval); + // Sampling is in fact disabled. Reset the state of the sampler. + // We do this check off the fast-path, because it's quite a rare state when + // allocation hooks are installed but the sampler is not running. + g_sampling_interval_initialized_tls = false; + g_accumulated_bytes_tls = 0; return; } @@ -433,6 +433,21 @@ void PoissonAllocationSampler::DoRecordAlloc(intptr_t accumulated_bytes, return; size_t mean_interval = g_sampling_interval.load(std::memory_order_relaxed); + + if (UNLIKELY(!g_sampling_interval_initialized_tls)) { + g_sampling_interval_initialized_tls = true; + // This is the very first allocation on the thread. It always makes it + // passing the condition at |RecordAlloc|, because g_accumulated_bytes_tls + // is initialized with zero due to TLS semantics. + // Generate proper sampling interval instance and make sure the allocation + // has indeed crossed the threshold before counting it as a sample. + accumulated_bytes -= GetNextSampleInterval(mean_interval); + if (accumulated_bytes < 0) { + g_accumulated_bytes_tls = accumulated_bytes; + return; + } + } + size_t samples = accumulated_bytes / mean_interval; accumulated_bytes %= mean_interval; @@ -443,16 +458,6 @@ void PoissonAllocationSampler::DoRecordAlloc(intptr_t accumulated_bytes, g_accumulated_bytes_tls = accumulated_bytes; - if (UNLIKELY(!g_sampling_interval_initialized_tls)) { - g_sampling_interval_initialized_tls = true; - // This is the very first allocation on the thread. It always produces an - // extra sample because g_accumulated_bytes_tls is initialized with zero - // due to TLS semantics. - // Make sure we don't count this extra sample. - if (!--samples) - return; - } - if (UNLIKELY(ScopedMuteThreadSamples::IsMuted())) return; diff --git a/chromium/base/sampling_heap_profiler/poisson_allocation_sampler.h b/chromium/base/sampling_heap_profiler/poisson_allocation_sampler.h index 6006bff19b6..116123940a6 100644 --- a/chromium/base/sampling_heap_profiler/poisson_allocation_sampler.h +++ b/chromium/base/sampling_heap_profiler/poisson_allocation_sampler.h @@ -35,10 +35,6 @@ class BASE_EXPORT PoissonAllocationSampler { public: enum AllocatorType : uint32_t { kMalloc, kPartitionAlloc, kBlinkGC }; - // When the sampler is just enabled it needs to see up to that amount - // of allocation sizes before it starts recording samples. - static constexpr size_t kWarmupInterval = 1 << 20; // 1MB. - class SamplesObserver { public: virtual ~SamplesObserver() = default; diff --git a/chromium/base/strings/string_number_conversions.cc b/chromium/base/strings/string_number_conversions.cc index 461502bb438..67bd3d0fd69 100644 --- a/chromium/base/strings/string_number_conversions.cc +++ b/chromium/base/strings/string_number_conversions.cc @@ -15,6 +15,7 @@ #include "base/logging.h" #include "base/no_destructor.h" #include "base/numerics/safe_math.h" +#include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/third_party/double_conversion/double-conversion/double-conversion.h" @@ -425,14 +426,15 @@ bool StringToSizeT(StringPiece16 input, size_t* output) { return String16ToIntImpl(input, output); } -bool StringToDouble(const std::string& input, double* output) { +template <typename STRING, typename CHAR> +bool StringToDoubleImpl(STRING input, const CHAR* data, double* output) { static NoDestructor<double_conversion::StringToDoubleConverter> converter( double_conversion::StringToDoubleConverter::ALLOW_LEADING_SPACES | double_conversion::StringToDoubleConverter::ALLOW_TRAILING_JUNK, 0.0, 0, nullptr, nullptr); int processed_characters_count; - *output = converter->StringToDouble(input.c_str(), input.size(), + *output = converter->StringToDouble(data, input.size(), &processed_characters_count); // Cases to return false: @@ -444,16 +446,17 @@ bool StringToDouble(const std::string& input, double* output) { // - If the first character is a space, there was leading whitespace return !input.empty() && *output != HUGE_VAL && *output != -HUGE_VAL && static_cast<size_t>(processed_characters_count) == input.size() && - !isspace(input[0]); + !IsUnicodeWhitespace(input[0]); } -// Note: if you need to add String16ToDouble, first ask yourself if it's -// really necessary. If it is, probably the best implementation here is to -// convert to 8-bit and then use the 8-bit version. +bool StringToDouble(StringPiece input, double* output) { + return StringToDoubleImpl(input, input.data(), output); +} -// Note: if you need to add an iterator range version of StringToDouble, first -// ask yourself if it's really necessary. If it is, probably the best -// implementation here is to instantiate a string and use the string version. +bool StringToDouble(StringPiece16 input, double* output) { + return StringToDoubleImpl( + input, reinterpret_cast<const uint16_t*>(input.data()), output); +} std::string HexEncode(const void* bytes, size_t size) { static const char kHexChars[] = "0123456789ABCDEF"; diff --git a/chromium/base/strings/string_number_conversions.h b/chromium/base/strings/string_number_conversions.h index 55f80a6d863..bc4edf289d6 100644 --- a/chromium/base/strings/string_number_conversions.h +++ b/chromium/base/strings/string_number_conversions.h @@ -98,7 +98,8 @@ BASE_EXPORT bool StringToSizeT(StringPiece16 input, size_t* output); // If your input is locale specific, use ICU to read the number. // WARNING: Will write to |output| even when returning false. // Read the comments here and above StringToInt() carefully. -BASE_EXPORT bool StringToDouble(const std::string& input, double* output); +BASE_EXPORT bool StringToDouble(StringPiece input, double* output); +BASE_EXPORT bool StringToDouble(StringPiece16 input, double* output); // Hex encoding ---------------------------------------------------------------- diff --git a/chromium/base/strings/string_piece.cc b/chromium/base/strings/string_piece.cc index 278849fac9d..19e132f1e36 100644 --- a/chromium/base/strings/string_piece.cc +++ b/chromium/base/strings/string_piece.cc @@ -219,8 +219,11 @@ size_t find_first_of(const StringPiece& self, size_t find_first_of(const StringPiece16& self, const StringPiece16& s, size_t pos) { + // Use the faster std::find() if searching for a single character. StringPiece16::const_iterator found = - std::find_first_of(self.begin() + pos, self.end(), s.begin(), s.end()); + s.size() == 1 ? std::find(self.begin() + pos, self.end(), s[0]) + : std::find_first_of(self.begin() + pos, self.end(), + s.begin(), s.end()); if (found == self.end()) return StringPiece16::npos; return found - self.begin(); diff --git a/chromium/base/strings/string_split.cc b/chromium/base/strings/string_split.cc index 1456c3df940..9a99925ca8a 100644 --- a/chromium/base/strings/string_split.cc +++ b/chromium/base/strings/string_split.cc @@ -14,27 +14,15 @@ namespace base { namespace { -// PieceToOutputType converts a StringPiece as needed to a given output type, -// which is either the same type of StringPiece (a NOP) or the corresponding -// non-piece string type. -// -// The default converter is a NOP, it works when the OutputType is the -// correct StringPiece. -template<typename Str, typename OutputType> -OutputType PieceToOutputType(BasicStringPiece<Str> piece) { - return piece; -} -template<> // Convert StringPiece to std::string -std::string PieceToOutputType<std::string, std::string>(StringPiece piece) { - return piece.as_string(); -} -template<> // Convert StringPiece16 to string16. -string16 PieceToOutputType<string16, string16>(StringPiece16 piece) { - return piece.as_string(); -} - // Returns either the ASCII or UTF-16 whitespace. template<typename Str> BasicStringPiece<Str> WhitespaceForType(); +#if defined(OS_WIN) && defined(BASE_STRING16_IS_STD_U16STRING) +template <> +WStringPiece WhitespaceForType<std::wstring>() { + return kWhitespaceWide; +} +#endif + template<> StringPiece16 WhitespaceForType<string16>() { return kWhitespaceUTF16; } @@ -42,37 +30,12 @@ template<> StringPiece WhitespaceForType<std::string>() { return kWhitespaceASCII; } -// Optimize the single-character case to call find() on the string instead, -// since this is the common case and can be made faster. This could have been -// done with template specialization too, but would have been less clear. -// -// There is no corresponding FindFirstNotOf because StringPiece already -// implements these different versions that do the optimized searching. -size_t FindFirstOf(StringPiece piece, char c, size_t pos) { - return piece.find(c, pos); -} -size_t FindFirstOf(StringPiece16 piece, char16 c, size_t pos) { - return piece.find(c, pos); -} -size_t FindFirstOf(StringPiece piece, StringPiece one_of, size_t pos) { - return piece.find_first_of(one_of, pos); -} -size_t FindFirstOf(StringPiece16 piece, StringPiece16 one_of, size_t pos) { - return piece.find_first_of(one_of, pos); -} - // General string splitter template. Can take 8- or 16-bit input, can produce -// the corresponding string or StringPiece output, and can take single- or -// multiple-character delimiters. -// -// DelimiterType is either a character (Str::value_type) or a string piece of -// multiple characters (BasicStringPiece<Str>). StringPiece has a version of -// find for both of these cases, and the single-character version is the most -// common and can be implemented faster, which is why this is a template. -template<typename Str, typename OutputStringType, typename DelimiterType> +// the corresponding string or StringPiece output. +template <typename OutputStringType, typename Str> static std::vector<OutputStringType> SplitStringT( BasicStringPiece<Str> str, - DelimiterType delimiter, + BasicStringPiece<Str> delimiter, WhitespaceHandling whitespace, SplitResult result_type) { std::vector<OutputStringType> result; @@ -81,7 +44,7 @@ static std::vector<OutputStringType> SplitStringT( size_t start = 0; while (start != Str::npos) { - size_t end = FindFirstOf(str, delimiter, start); + size_t end = str.find_first_of(delimiter, start); BasicStringPiece<Str> piece; if (end == Str::npos) { @@ -96,7 +59,7 @@ static std::vector<OutputStringType> SplitStringT( piece = TrimString(piece, WhitespaceForType<Str>(), TRIM_ALL); if (result_type == SPLIT_WANT_ALL || !piece.empty()) - result.push_back(PieceToOutputType<Str, OutputStringType>(piece)); + result.emplace_back(piece); } return result; } @@ -130,16 +93,16 @@ bool AppendStringKeyValue(StringPiece input, return true; } -template <typename Str, typename OutputStringType> -void SplitStringUsingSubstrT(BasicStringPiece<Str> input, - BasicStringPiece<Str> delimiter, - WhitespaceHandling whitespace, - SplitResult result_type, - std::vector<OutputStringType>* result) { +template <typename OutputStringType, typename Str> +std::vector<OutputStringType> SplitStringUsingSubstrT( + BasicStringPiece<Str> input, + BasicStringPiece<Str> delimiter, + WhitespaceHandling whitespace, + SplitResult result_type) { using Piece = BasicStringPiece<Str>; using size_type = typename Piece::size_type; - result->clear(); + std::vector<OutputStringType> result; for (size_type begin_index = 0, end_index = 0; end_index != Piece::npos; begin_index = end_index + delimiter.size()) { end_index = input.find(delimiter, begin_index); @@ -151,8 +114,10 @@ void SplitStringUsingSubstrT(BasicStringPiece<Str> input, term = TrimString(term, WhitespaceForType<Str>(), TRIM_ALL); if (result_type == SPLIT_WANT_ALL || !term.empty()) - result->push_back(PieceToOutputType<Str, OutputStringType>(term)); + result.emplace_back(term); } + + return result; } } // namespace @@ -161,48 +126,29 @@ std::vector<std::string> SplitString(StringPiece input, StringPiece separators, WhitespaceHandling whitespace, SplitResult result_type) { - if (separators.size() == 1) { - return SplitStringT<std::string, std::string, char>( - input, separators[0], whitespace, result_type); - } - return SplitStringT<std::string, std::string, StringPiece>( - input, separators, whitespace, result_type); + return SplitStringT<std::string>(input, separators, whitespace, result_type); } std::vector<string16> SplitString(StringPiece16 input, StringPiece16 separators, WhitespaceHandling whitespace, SplitResult result_type) { - if (separators.size() == 1) { - return SplitStringT<string16, string16, char16>( - input, separators[0], whitespace, result_type); - } - return SplitStringT<string16, string16, StringPiece16>( - input, separators, whitespace, result_type); + return SplitStringT<string16>(input, separators, whitespace, result_type); } std::vector<StringPiece> SplitStringPiece(StringPiece input, StringPiece separators, WhitespaceHandling whitespace, SplitResult result_type) { - if (separators.size() == 1) { - return SplitStringT<std::string, StringPiece, char>( - input, separators[0], whitespace, result_type); - } - return SplitStringT<std::string, StringPiece, StringPiece>( - input, separators, whitespace, result_type); + return SplitStringT<StringPiece>(input, separators, whitespace, result_type); } std::vector<StringPiece16> SplitStringPiece(StringPiece16 input, StringPiece16 separators, WhitespaceHandling whitespace, SplitResult result_type) { - if (separators.size() == 1) { - return SplitStringT<string16, StringPiece16, char16>( - input, separators[0], whitespace, result_type); - } - return SplitStringT<string16, StringPiece16, StringPiece16>( - input, separators, whitespace, result_type); + return SplitStringT<StringPiece16>(input, separators, whitespace, + result_type); } bool SplitStringIntoKeyValuePairs(StringPiece input, @@ -240,18 +186,16 @@ std::vector<string16> SplitStringUsingSubstr(StringPiece16 input, StringPiece16 delimiter, WhitespaceHandling whitespace, SplitResult result_type) { - std::vector<string16> result; - SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result); - return result; + return SplitStringUsingSubstrT<string16>(input, delimiter, whitespace, + result_type); } std::vector<std::string> SplitStringUsingSubstr(StringPiece input, StringPiece delimiter, WhitespaceHandling whitespace, SplitResult result_type) { - std::vector<std::string> result; - SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result); - return result; + return SplitStringUsingSubstrT<std::string>(input, delimiter, whitespace, + result_type); } std::vector<StringPiece16> SplitStringPieceUsingSubstr( @@ -260,8 +204,8 @@ std::vector<StringPiece16> SplitStringPieceUsingSubstr( WhitespaceHandling whitespace, SplitResult result_type) { std::vector<StringPiece16> result; - SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result); - return result; + return SplitStringUsingSubstrT<StringPiece16>(input, delimiter, whitespace, + result_type); } std::vector<StringPiece> SplitStringPieceUsingSubstr( @@ -269,9 +213,41 @@ std::vector<StringPiece> SplitStringPieceUsingSubstr( StringPiece delimiter, WhitespaceHandling whitespace, SplitResult result_type) { - std::vector<StringPiece> result; - SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result); - return result; + return SplitStringUsingSubstrT<StringPiece>(input, delimiter, whitespace, + result_type); +} + +#if defined(OS_WIN) && defined(BASE_STRING16_IS_STD_U16STRING) +std::vector<std::wstring> SplitString(WStringPiece input, + WStringPiece separators, + WhitespaceHandling whitespace, + SplitResult result_type) { + return SplitStringT<std::wstring>(input, separators, whitespace, result_type); +} + +std::vector<WStringPiece> SplitStringPiece(WStringPiece input, + WStringPiece separators, + WhitespaceHandling whitespace, + SplitResult result_type) { + return SplitStringT<WStringPiece>(input, separators, whitespace, result_type); +} + +std::vector<std::wstring> SplitStringUsingSubstr(WStringPiece input, + WStringPiece delimiter, + WhitespaceHandling whitespace, + SplitResult result_type) { + return SplitStringUsingSubstrT<std::wstring>(input, delimiter, whitespace, + result_type); +} + +std::vector<WStringPiece> SplitStringPieceUsingSubstr( + WStringPiece input, + WStringPiece delimiter, + WhitespaceHandling whitespace, + SplitResult result_type) { + return SplitStringUsingSubstrT<WStringPiece>(input, delimiter, whitespace, + result_type); } +#endif } // namespace base diff --git a/chromium/base/strings/string_split.h b/chromium/base/strings/string_split.h index 406dd2abe79..d3181a78f4e 100644 --- a/chromium/base/strings/string_split.h +++ b/chromium/base/strings/string_split.h @@ -12,6 +12,7 @@ #include "base/base_export.h" #include "base/strings/string16.h" #include "base/strings/string_piece.h" +#include "build/build_config.h" namespace base { @@ -132,6 +133,31 @@ BASE_EXPORT std::vector<StringPiece> SplitStringPieceUsingSubstr( WhitespaceHandling whitespace, SplitResult result_type); +#if defined(OS_WIN) && defined(BASE_STRING16_IS_STD_U16STRING) +BASE_EXPORT std::vector<std::wstring> SplitString(WStringPiece input, + WStringPiece separators, + WhitespaceHandling whitespace, + SplitResult result_type); + +BASE_EXPORT std::vector<WStringPiece> SplitStringPiece( + WStringPiece input, + WStringPiece separators, + WhitespaceHandling whitespace, + SplitResult result_type); + +BASE_EXPORT std::vector<std::wstring> SplitStringUsingSubstr( + WStringPiece input, + WStringPiece delimiter, + WhitespaceHandling whitespace, + SplitResult result_type); + +BASE_EXPORT std::vector<WStringPiece> SplitStringPieceUsingSubstr( + WStringPiece input, + WStringPiece delimiter, + WhitespaceHandling whitespace, + SplitResult result_type); +#endif + } // namespace base #endif // BASE_STRINGS_STRING_SPLIT_H_ diff --git a/chromium/base/strings/string_util.cc b/chromium/base/strings/string_util.cc index a8fcb8d2e20..03ea3972a86 100644 --- a/chromium/base/strings/string_util.cc +++ b/chromium/base/strings/string_util.cc @@ -262,8 +262,8 @@ bool RemoveChars(const std::string& input, return ReplaceCharsT(input, remove_chars, StringPiece(), output); } -template<typename Str> -TrimPositions TrimStringT(const Str& input, +template <typename Str> +TrimPositions TrimStringT(BasicStringPiece<Str> input, BasicStringPiece<Str> trim_chars, TrimPositions positions, Str* output) { @@ -271,40 +271,40 @@ TrimPositions TrimStringT(const Str& input, // a StringPiece version of input to be able to call find* on it with the // StringPiece version of trim_chars (normally the trim_chars will be a // constant so avoid making a copy). - BasicStringPiece<Str> input_piece(input); const size_t last_char = input.length() - 1; - const size_t first_good_char = (positions & TRIM_LEADING) ? - input_piece.find_first_not_of(trim_chars) : 0; - const size_t last_good_char = (positions & TRIM_TRAILING) ? - input_piece.find_last_not_of(trim_chars) : last_char; + const size_t first_good_char = + (positions & TRIM_LEADING) ? input.find_first_not_of(trim_chars) : 0; + const size_t last_good_char = (positions & TRIM_TRAILING) + ? input.find_last_not_of(trim_chars) + : last_char; // When the string was all trimmed, report that we stripped off characters // from whichever position the caller was interested in. For empty input, we // stripped no characters, but we still need to clear |output|. - if (input.empty() || - (first_good_char == Str::npos) || (last_good_char == Str::npos)) { + if (input.empty() || first_good_char == Str::npos || + last_good_char == Str::npos) { bool input_was_empty = input.empty(); // in case output == &input output->clear(); return input_was_empty ? TRIM_NONE : positions; } // Trim. - *output = - input.substr(first_good_char, last_good_char - first_good_char + 1); + output->assign(input.data() + first_good_char, + last_good_char - first_good_char + 1); // Return where we trimmed from. return static_cast<TrimPositions>( - ((first_good_char == 0) ? TRIM_NONE : TRIM_LEADING) | - ((last_good_char == last_char) ? TRIM_NONE : TRIM_TRAILING)); + (first_good_char == 0 ? TRIM_NONE : TRIM_LEADING) | + (last_good_char == last_char ? TRIM_NONE : TRIM_TRAILING)); } -bool TrimString(const string16& input, +bool TrimString(StringPiece16 input, StringPiece16 trim_chars, string16* output) { return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE; } -bool TrimString(const std::string& input, +bool TrimString(StringPiece input, StringPiece trim_chars, std::string* output) { return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE; @@ -370,7 +370,7 @@ void TruncateUTF8ToByteSize(const std::string& input, output->clear(); } -TrimPositions TrimWhitespace(const string16& input, +TrimPositions TrimWhitespace(StringPiece16 input, TrimPositions positions, string16* output) { return TrimStringT(input, StringPiece16(kWhitespaceUTF16), positions, output); @@ -381,7 +381,7 @@ StringPiece16 TrimWhitespace(StringPiece16 input, return TrimStringPieceT(input, StringPiece16(kWhitespaceUTF16), positions); } -TrimPositions TrimWhitespaceASCII(const std::string& input, +TrimPositions TrimWhitespaceASCII(StringPiece input, TrimPositions positions, std::string* output) { return TrimStringT(input, StringPiece(kWhitespaceASCII), positions, output); @@ -913,7 +913,7 @@ void ReplaceSubstringsAfterOffset(std::string* str, template <class string_type> inline typename string_type::value_type* WriteIntoT(string_type* str, size_t length_with_null) { - DCHECK_GT(length_with_null, 1u); + DCHECK_GE(length_with_null, 1u); str->reserve(length_with_null); str->resize(length_with_null - 1); return &((*str)[0]); @@ -1085,6 +1085,32 @@ string16 ReplaceStringPlaceholders(const string16& format_string, return result; } +#if defined(OS_WIN) && defined(BASE_STRING16_IS_STD_U16STRING) + +TrimPositions TrimWhitespace(WStringPiece input, + TrimPositions positions, + std::wstring* output) { + return TrimStringT(input, WStringPiece(kWhitespaceWide), positions, output); +} + +WStringPiece TrimWhitespace(WStringPiece input, TrimPositions positions) { + return TrimStringPieceT(input, WStringPiece(kWhitespaceWide), positions); +} + +bool TrimString(WStringPiece input, + WStringPiece trim_chars, + std::wstring* output) { + return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE; +} + +WStringPiece TrimString(WStringPiece input, + WStringPiece trim_chars, + TrimPositions positions) { + return TrimStringPieceT(input, trim_chars, positions); +} + +#endif + // The following code is compatible with the OpenBSD lcpy interface. See: // http://www.gratisoft.us/todd/papers/strlcpy.html // ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/{wcs,str}lcpy.c diff --git a/chromium/base/strings/string_util.h b/chromium/base/strings/string_util.h index 1398a9ba542..ec0bee3b824 100644 --- a/chromium/base/strings/string_util.h +++ b/chromium/base/strings/string_util.h @@ -204,10 +204,10 @@ enum TrimPositions { // // It is safe to use the same variable for both |input| and |output| (this is // the normal usage to trim in-place). -BASE_EXPORT bool TrimString(const string16& input, +BASE_EXPORT bool TrimString(StringPiece16 input, StringPiece16 trim_chars, string16* output); -BASE_EXPORT bool TrimString(const std::string& input, +BASE_EXPORT bool TrimString(StringPiece input, StringPiece trim_chars, std::string* output); @@ -269,6 +269,24 @@ inline const char16* as_u16cstr(const wchar_t* str) { inline const char16* as_u16cstr(WStringPiece str) { return reinterpret_cast<const char16*>(str.data()); } + +// Utility functions to convert between base::WStringPiece and +// base::StringPiece16. +inline WStringPiece AsWStringPiece(StringPiece16 str) { + return WStringPiece(as_wcstr(str.data()), str.size()); +} + +inline StringPiece16 AsStringPiece16(WStringPiece str) { + return StringPiece16(as_u16cstr(str.data()), str.size()); +} + +inline std::wstring AsWString(StringPiece16 str) { + return std::wstring(as_wcstr(str.data()), str.size()); +} + +inline string16 AsString16(WStringPiece str) { + return string16(as_u16cstr(str.data()), str.size()); +} #endif // defined(WCHAR_T_IS_UTF16) // Trims any whitespace from either end of the input string. @@ -278,12 +296,12 @@ inline const char16* as_u16cstr(WStringPiece str) { // // The std::string versions return where whitespace was found. // NOTE: Safe to use the same variable for both input and output. -BASE_EXPORT TrimPositions TrimWhitespace(const string16& input, +BASE_EXPORT TrimPositions TrimWhitespace(StringPiece16 input, TrimPositions positions, string16* output); BASE_EXPORT StringPiece16 TrimWhitespace(StringPiece16 input, TrimPositions positions); -BASE_EXPORT TrimPositions TrimWhitespaceASCII(const std::string& input, +BASE_EXPORT TrimPositions TrimWhitespaceASCII(StringPiece input, TrimPositions positions, std::string* output); BASE_EXPORT StringPiece TrimWhitespaceASCII(StringPiece input, @@ -457,10 +475,6 @@ BASE_EXPORT void ReplaceSubstringsAfterOffset( // convenient in that is can be used inline in the call, and fast in that it // avoids copying the results of the call from a char* into a string. // -// |length_with_null| must be at least 2, since otherwise the underlying string -// would have size 0, and trying to access &((*str)[0]) in that case can result -// in a number of problems. -// // Internally, this takes linear time because the resize() call 0-fills the // underlying array for potentially all // (|length_with_null - 1| * sizeof(string_type::value_type)) bytes. Ideally we @@ -518,6 +532,23 @@ BASE_EXPORT string16 ReplaceStringPlaceholders(const string16& format_string, const string16& a, size_t* offset); +#if defined(OS_WIN) && defined(BASE_STRING16_IS_STD_U16STRING) +BASE_EXPORT TrimPositions TrimWhitespace(WStringPiece input, + TrimPositions positions, + std::wstring* output); + +BASE_EXPORT WStringPiece TrimWhitespace(WStringPiece input, + TrimPositions positions); + +BASE_EXPORT bool TrimString(WStringPiece input, + WStringPiece trim_chars, + std::wstring* output); + +BASE_EXPORT WStringPiece TrimString(WStringPiece input, + WStringPiece trim_chars, + TrimPositions positions); +#endif + } // namespace base #if defined(OS_WIN) diff --git a/chromium/base/strings/string_util_unittest.cc b/chromium/base/strings/string_util_unittest.cc index 2e0e35e5596..58eda91f0a1 100644 --- a/chromium/base/strings/string_util_unittest.cc +++ b/chromium/base/strings/string_util_unittest.cc @@ -1414,6 +1414,13 @@ TEST_F(WriteIntoTest, WriteInto) { WritesCorrectly(2); WritesCorrectly(5000); + // Validate that WriteInto handles 0-length strings + std::string empty; + const char kOriginal[] = "original"; + strncpy(WriteInto(&empty, 1), kOriginal, 0); + EXPECT_STREQ("", empty.c_str()); + EXPECT_EQ(0u, empty.size()); + // Validate that WriteInto doesn't modify other strings // when using a Copy-on-Write implementation. const char kLive[] = "live"; diff --git a/chromium/base/strings/string_util_win.h b/chromium/base/strings/string_util_win.h index 7f260bfc8b4..791f60e3e4c 100644 --- a/chromium/base/strings/string_util_win.h +++ b/chromium/base/strings/string_util_win.h @@ -39,6 +39,19 @@ inline int vswprintf(wchar_t* buffer, size_t size, return length; } +// Windows only overload of base::WriteInto for std::wstring. See the comment +// above the cross-platform version in //base/strings/string_util.h for details. +// TODO(crbug.com/911896): Rename this to WriteInto once base::string16 is +// std::u16string on all platforms and using the name WriteInto here no longer +// causes redefinition errors. +inline wchar_t* WriteIntoW(std::wstring* str, size_t length_with_null) { + // Note: As of C++11 std::strings are guaranteed to be 0-terminated. Thus it + // is enough to reserve space for one char less. + DCHECK_GE(length_with_null, 1u); + str->resize(length_with_null - 1); + return &((*str)[0]); +} + } // namespace base #endif // BASE_STRINGS_STRING_UTIL_WIN_H_ diff --git a/chromium/base/strings/stringprintf.cc b/chromium/base/strings/stringprintf.cc index 72160699203..738cc63bbe8 100644 --- a/chromium/base/strings/stringprintf.cc +++ b/chromium/base/strings/stringprintf.cc @@ -39,18 +39,25 @@ inline int vsnprintfT(wchar_t* buffer, va_list argptr) { return base::vswprintf(buffer, buf_size, format, argptr); } +inline int vsnprintfT(char16_t* buffer, + size_t buf_size, + const char16_t* format, + va_list argptr) { + return base::vswprintf(reinterpret_cast<wchar_t*>(buffer), buf_size, + reinterpret_cast<const wchar_t*>(format), argptr); +} #endif // Templatized backend for StringPrintF/StringAppendF. This does not finalize // the va_list, the caller is expected to do that. -template <class StringType> -static void StringAppendVT(StringType* dst, - const typename StringType::value_type* format, +template <class CharT> +static void StringAppendVT(std::basic_string<CharT>* dst, + const CharT* format, va_list ap) { // First try with a small fixed size buffer. // This buffer size should be kept in sync with StringUtilTest.GrowBoundary // and StringUtilTest.StringPrintfBounds. - typename StringType::value_type stack_buf[1024]; + CharT stack_buf[1024]; va_list ap_copy; va_copy(ap_copy, ap); @@ -93,7 +100,7 @@ static void StringAppendVT(StringType* dst, return; } - std::vector<typename StringType::value_type> mem_buf(mem_length); + std::vector<CharT> mem_buf(mem_length); // NOTE: You can only use a va_list once. Since we're in a while loop, we // need to make a new copy each time so we don't use up the original. @@ -129,6 +136,15 @@ std::wstring StringPrintf(const wchar_t* format, ...) { va_end(ap); return result; } + +std::u16string StringPrintf(const char16_t* format, ...) { + va_list ap; + va_start(ap, format); + std::u16string result; + StringAppendV(&result, format, ap); + va_end(ap); + return result; +} #endif std::string StringPrintV(const char* format, va_list ap) { @@ -156,6 +172,17 @@ const std::wstring& SStringPrintf(std::wstring* dst, va_end(ap); return *dst; } + +const std::u16string& SStringPrintf(std::u16string* dst, + const char16_t* format, + ...) { + va_list ap; + va_start(ap, format); + dst->clear(); + StringAppendV(dst, format, ap); + va_end(ap); + return *dst; +} #endif void StringAppendF(std::string* dst, const char* format, ...) { @@ -172,6 +199,13 @@ void StringAppendF(std::wstring* dst, const wchar_t* format, ...) { StringAppendV(dst, format, ap); va_end(ap); } + +void StringAppendF(std::u16string* dst, const char16_t* format, ...) { + va_list ap; + va_start(ap, format); + StringAppendV(dst, format, ap); + va_end(ap); +} #endif void StringAppendV(std::string* dst, const char* format, va_list ap) { @@ -182,6 +216,10 @@ void StringAppendV(std::string* dst, const char* format, va_list ap) { void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap) { StringAppendVT(dst, format, ap); } + +void StringAppendV(std::u16string* dst, const char16_t* format, va_list ap) { + StringAppendVT(dst, format, ap); +} #endif } // namespace base diff --git a/chromium/base/strings/stringprintf.h b/chromium/base/strings/stringprintf.h index 341c2ef8a64..a8d5bc84e9a 100644 --- a/chromium/base/strings/stringprintf.h +++ b/chromium/base/strings/stringprintf.h @@ -19,8 +19,14 @@ namespace base { BASE_EXPORT std::string StringPrintf(const char* format, ...) PRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT; #if defined(OS_WIN) +// Note: Unfortunately compile time checking of the format string for UTF-16 +// strings is not supported by any compiler, thus these functions should be used +// carefully and sparingly. Also applies to SStringPrintf and StringAppendV +// below. BASE_EXPORT std::wstring StringPrintf(const wchar_t* format, ...) WPRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT; +BASE_EXPORT std::u16string StringPrintf(const char16_t* format, ...) + WPRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT; #endif // Return a C++ string given vprintf-like input. @@ -35,6 +41,9 @@ BASE_EXPORT const std::string& SStringPrintf(std::string* dst, BASE_EXPORT const std::wstring& SStringPrintf(std::wstring* dst, const wchar_t* format, ...) WPRINTF_FORMAT(2, 3); +BASE_EXPORT const std::u16string& SStringPrintf(std::u16string* dst, + const char16_t* format, + ...) WPRINTF_FORMAT(2, 3); #endif // Append result to a supplied string. @@ -43,6 +52,8 @@ BASE_EXPORT void StringAppendF(std::string* dst, const char* format, ...) #if defined(OS_WIN) BASE_EXPORT void StringAppendF(std::wstring* dst, const wchar_t* format, ...) WPRINTF_FORMAT(2, 3); +BASE_EXPORT void StringAppendF(std::u16string* dst, const char16_t* format, ...) + WPRINTF_FORMAT(2, 3); #endif // Lower-level routine that takes a va_list and appends to a specified @@ -53,6 +64,9 @@ BASE_EXPORT void StringAppendV(std::string* dst, const char* format, va_list ap) BASE_EXPORT void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap) WPRINTF_FORMAT(2, 0); +BASE_EXPORT void StringAppendV(std::u16string* dst, + const char16_t* format, + va_list ap) WPRINTF_FORMAT(2, 0); #endif } // namespace base diff --git a/chromium/base/strings/stringprintf_unittest.cc b/chromium/base/strings/stringprintf_unittest.cc index dbc9b74cc2e..557ed54d1df 100644 --- a/chromium/base/strings/stringprintf_unittest.cc +++ b/chromium/base/strings/stringprintf_unittest.cc @@ -18,7 +18,10 @@ namespace { // A helper for the StringAppendV test that follows. // // Just forwards its args to StringAppendV. -static void StringAppendVTestHelper(std::string* out, const char* format, ...) { +template <class CharT> +static void StringAppendVTestHelper(std::basic_string<CharT>* out, + const CharT* format, + ...) { va_list ap; va_start(ap, format); StringAppendV(out, format, ap); @@ -35,6 +38,7 @@ TEST(StringPrintfTest, StringPrintfMisc) { EXPECT_EQ("123hello w", StringPrintf("%3d%2s %1c", 123, "hello", 'w')); #if defined(OS_WIN) EXPECT_EQ(L"123hello w", StringPrintf(L"%3d%2ls %1lc", 123, L"hello", 'w')); + EXPECT_EQ(u"123hello w", StringPrintf(u"%3d%2ls %1lc", 123, u"hello", 'w')); #endif } @@ -47,6 +51,10 @@ TEST(StringPrintfTest, StringAppendfEmptyString) { std::wstring valuew(L"Hello"); StringAppendF(&valuew, L"%ls", L""); EXPECT_EQ(L"Hello", valuew); + + std::u16string value16(u"Hello"); + StringAppendF(&value16, u"%ls", u""); + EXPECT_EQ(u"Hello", value16); #endif } @@ -59,6 +67,10 @@ TEST(StringPrintfTest, StringAppendfString) { std::wstring valuew(L"Hello"); StringAppendF(&valuew, L" %ls", L"World"); EXPECT_EQ(L"Hello World", valuew); + + std::u16string value16(u"Hello"); + StringAppendF(&value16, u" %ls", u"World"); + EXPECT_EQ(u"Hello World", value16); #endif } @@ -71,6 +83,10 @@ TEST(StringPrintfTest, StringAppendfInt) { std::wstring valuew(L"Hello"); StringAppendF(&valuew, L" %d", 123); EXPECT_EQ(L"Hello 123", valuew); + + std::u16string value16(u"Hello"); + StringAppendF(&value16, u" %d", 123); + EXPECT_EQ(u"Hello 123", value16); #endif } @@ -79,12 +95,13 @@ TEST(StringPrintfTest, StringAppendfInt) { TEST(StringPrintfTest, StringPrintfBounds) { const int kSrcLen = 1026; char src[kSrcLen]; - for (auto& i : src) - i = 'A'; + std::fill_n(src, kSrcLen, 'A'); wchar_t srcw[kSrcLen]; - for (auto& i : srcw) - i = 'A'; + std::fill_n(srcw, kSrcLen, 'A'); + + char16_t src16[kSrcLen]; + std::fill_n(src16, kSrcLen, 'A'); for (int i = 1; i < 3; i++) { src[kSrcLen - i] = 0; @@ -97,6 +114,14 @@ TEST(StringPrintfTest, StringPrintfBounds) { std::wstring outw; SStringPrintf(&outw, L"%ls", srcw); EXPECT_STREQ(srcw, outw.c_str()); + + src16[kSrcLen - i] = 0; + std::u16string out16; + SStringPrintf(&out16, u"%ls", src16); + // EXPECT_STREQ does not support const char16_t* strings yet. + // Dispatch to the const wchar_t* overload instead. + EXPECT_STREQ(reinterpret_cast<const wchar_t*>(src16), + reinterpret_cast<const wchar_t*>(out16.c_str())); #endif } } @@ -129,6 +154,16 @@ TEST(StringPrintfTest, StringAppendV) { std::string out; StringAppendVTestHelper(&out, "%d foo %s", 1, "bar"); EXPECT_EQ("1 foo bar", out); + +#if defined(OS_WIN) + std::wstring outw; + StringAppendVTestHelper(&outw, L"%d foo %ls", 1, L"bar"); + EXPECT_EQ(L"1 foo bar", outw); + + std::u16string out16; + StringAppendVTestHelper(&out16, u"%d foo %ls", 1, u"bar"); + EXPECT_EQ(u"1 foo bar", out16); +#endif } // Test the boundary condition for the size of the string_util's diff --git a/chromium/base/syslog_logging.cc b/chromium/base/syslog_logging.cc index 68483efccd5..6b7280dbc41 100644 --- a/chromium/base/syslog_logging.cc +++ b/chromium/base/syslog_logging.cc @@ -34,7 +34,7 @@ namespace { std::string* g_event_source_name = nullptr; uint16_t g_category = 0; uint32_t g_event_id = 0; -base::string16* g_user_sid = nullptr; +std::wstring* g_user_sid = nullptr; } // namespace @@ -46,7 +46,7 @@ void SetEventSource(const std::string& name, g_category = category; g_event_id = event_id; DCHECK_EQ(nullptr, g_user_sid); - g_user_sid = new base::string16(); + g_user_sid = new std::wstring(); base::win::GetUserSidString(g_user_sid); } @@ -102,7 +102,7 @@ EventLogMessage::~EventLogMessage() { } LPCSTR strings[1] = {message.data()}; PSID user_sid = nullptr; - if (!::ConvertStringSidToSid(base::as_wcstr(*g_user_sid), &user_sid)) { + if (!::ConvertStringSidToSid(g_user_sid->c_str(), &user_sid)) { stream() << " !!ERROR GETTING USER SID!!"; } diff --git a/chromium/base/system/sys_info.h b/chromium/base/system/sys_info.h index 80577b1b937..e5ffb2aeff5 100644 --- a/chromium/base/system/sys_info.h +++ b/chromium/base/system/sys_info.h @@ -83,6 +83,11 @@ class BASE_EXPORT SysInfo { // Note: validate any new usage with the privacy team. // TODO(crbug.com/907518): Implement support on other platforms. std::string serial_number; + + bool operator==(const HardwareInfo& rhs) const { + return manufacturer == rhs.manufacturer && model == rhs.model && + serial_number == rhs.serial_number; + } }; // Returns via |callback| a struct containing descriptive UTF-8 strings for // the current machine manufacturer and model, or empty strings if the diff --git a/chromium/base/system/sys_info_ios.mm b/chromium/base/system/sys_info_ios.mm index e8ae8fb41d1..1dd9dc18ad8 100644 --- a/chromium/base/system/sys_info_ios.mm +++ b/chromium/base/system/sys_info_ios.mm @@ -13,7 +13,6 @@ #include "base/logging.h" #include "base/mac/scoped_mach_port.h" -#include "base/mac/scoped_nsautorelease_pool.h" #include "base/process/process_metrics.h" #include "base/stl_util.h" #include "base/strings/string_util.h" @@ -43,9 +42,10 @@ std::string SysInfo::OperatingSystemName() { static dispatch_once_t get_system_name_once; static std::string* system_name; dispatch_once(&get_system_name_once, ^{ - base::mac::ScopedNSAutoreleasePool pool; - system_name = new std::string( - SysNSStringToUTF8([[UIDevice currentDevice] systemName])); + @autoreleasepool { + system_name = new std::string( + SysNSStringToUTF8([[UIDevice currentDevice] systemName])); + } }); // Examples of returned value: 'iPhone OS' on iPad 5.1.1 // and iPhone 5.1.1. @@ -57,9 +57,10 @@ std::string SysInfo::OperatingSystemVersion() { static dispatch_once_t get_system_version_once; static std::string* system_version; dispatch_once(&get_system_version_once, ^{ - base::mac::ScopedNSAutoreleasePool pool; - system_version = new std::string( - SysNSStringToUTF8([[UIDevice currentDevice] systemVersion])); + @autoreleasepool { + system_version = new std::string( + SysNSStringToUTF8([[UIDevice currentDevice] systemVersion])); + } }); return *system_version; } @@ -68,18 +69,19 @@ std::string SysInfo::OperatingSystemVersion() { void SysInfo::OperatingSystemVersionNumbers(int32_t* major_version, int32_t* minor_version, int32_t* bugfix_version) { - base::mac::ScopedNSAutoreleasePool pool; - std::string system_version = OperatingSystemVersion(); - if (!system_version.empty()) { - // Try to parse out the version numbers from the string. - int num_read = sscanf(system_version.c_str(), "%d.%d.%d", major_version, - minor_version, bugfix_version); - if (num_read < 1) - *major_version = 0; - if (num_read < 2) - *minor_version = 0; - if (num_read < 3) - *bugfix_version = 0; + @autoreleasepool { + std::string system_version = OperatingSystemVersion(); + if (!system_version.empty()) { + // Try to parse out the version numbers from the string. + int num_read = sscanf(system_version.c_str(), "%d.%d.%d", major_version, + minor_version, bugfix_version); + if (num_read < 1) + *major_version = 0; + if (num_read < 2) + *minor_version = 0; + if (num_read < 3) + *bugfix_version = 0; + } } } diff --git a/chromium/base/system/sys_info_win.cc b/chromium/base/system/sys_info_win.cc index 87650d8892f..a600296695e 100644 --- a/chromium/base/system/sys_info_win.cc +++ b/chromium/base/system/sys_info_win.cc @@ -171,9 +171,9 @@ SysInfo::HardwareInfo SysInfo::GetHardwareInfoSync() { win::WmiComputerSystemInfo wmi_info = win::WmiComputerSystemInfo::Get(); HardwareInfo info; - info.manufacturer = UTF16ToUTF8(wmi_info.manufacturer()); - info.model = UTF16ToUTF8(wmi_info.model()); - info.serial_number = UTF16ToUTF8(wmi_info.serial_number()); + info.manufacturer = WideToUTF8(wmi_info.manufacturer()); + info.model = WideToUTF8(wmi_info.model()); + info.serial_number = WideToUTF8(wmi_info.serial_number()); DCHECK(IsStringUTF8(info.manufacturer)); DCHECK(IsStringUTF8(info.model)); DCHECK(IsStringUTF8(info.serial_number)); diff --git a/chromium/base/task/post_job.cc b/chromium/base/task/post_job.cc index e57b32e4b40..3c83068995a 100644 --- a/chromium/base/task/post_job.cc +++ b/chromium/base/task/post_job.cc @@ -4,8 +4,11 @@ #include "base/task/post_job.h" +#include "base/task/scoped_set_task_priority_for_current_thread.h" #include "base/task/thread_pool/job_task_source.h" #include "base/task/thread_pool/pooled_task_runner_delegate.h" +#include "base/task/thread_pool/thread_pool_impl.h" +#include "base/task/thread_pool/thread_pool_instance.h" namespace base { namespace experimental { @@ -16,7 +19,6 @@ JobDelegate::JobDelegate( : task_source_(task_source), pooled_task_runner_delegate_(pooled_task_runner_delegate) { DCHECK(task_source_); - DCHECK(pooled_task_runner_delegate_); #if DCHECK_IS_ON() recorded_increase_version_ = task_source_->GetConcurrencyIncreaseVersion(); // Record max concurrency before running the worker task. @@ -42,7 +44,9 @@ bool JobDelegate::ShouldYield() { AssertExpectedConcurrency(recorded_max_concurrency_); #endif // DCHECK_IS_ON() const bool should_yield = - pooled_task_runner_delegate_->ShouldYield(task_source_); + task_source_->ShouldYield() || + (pooled_task_runner_delegate_ && + pooled_task_runner_delegate_->ShouldYield(task_source_)); #if DCHECK_IS_ON() last_should_yield_ = should_yield; @@ -98,5 +102,91 @@ void JobDelegate::AssertExpectedConcurrency(size_t expected_max_concurrency) { #endif // DCHECK_IS_ON() } +JobHandle::JobHandle() = default; + +JobHandle::JobHandle(scoped_refptr<internal::JobTaskSource> task_source) + : task_source_(std::move(task_source)) {} + +JobHandle::~JobHandle() { + DCHECK(!task_source_) + << "The Job must be cancelled, detached or joined before its " + "JobHandle is destroyed."; +} + +JobHandle::JobHandle(JobHandle&&) = default; + +JobHandle& JobHandle::operator=(JobHandle&& other) { + DCHECK(!task_source_) + << "The Job must be cancelled, detached or joined before its " + "JobHandle is re-assigned."; + task_source_ = std::move(other.task_source_); + return *this; +} + +void JobHandle::UpdatePriority(TaskPriority new_priority) { + task_source_->delegate()->UpdatePriority(task_source_, new_priority); +} + +void JobHandle::NotifyConcurrencyIncrease() { + task_source_->NotifyConcurrencyIncrease(); +} + +void JobHandle::Join() { + DCHECK_GE(internal::GetTaskPriorityForCurrentThread(), + task_source_->priority_racy()) + << "Join may not be called on Job with higher priority than the current " + "one."; + UpdatePriority(internal::GetTaskPriorityForCurrentThread()); + bool must_run = task_source_->WillJoin(); + while (must_run) + must_run = task_source_->RunJoinTask(); + // Remove |task_source_| from the ThreadPool to prevent access to + // |max_concurrency_callback| after Join(). + task_source_->delegate()->RemoveJobTaskSource(task_source_); + task_source_ = nullptr; +} + +void JobHandle::Cancel() { + task_source_->Cancel(); + Join(); +} + +void JobHandle::CancelAndDetach() { + task_source_->Cancel(); + Detach(); +} + +void JobHandle::Detach() { + DCHECK(task_source_); + task_source_ = nullptr; +} + +JobHandle PostJob(const Location& from_here, + const TaskTraits& traits, + RepeatingCallback<void(JobDelegate*)> worker_task, + RepeatingCallback<size_t()> max_concurrency_callback) { + DCHECK(ThreadPoolInstance::Get()) + << "Ref. Prerequisite section of post_task.h.\n\n" + "Hint: if this is in a unit test, you're likely merely missing a " + "base::test::TaskEnvironment member in your fixture.\n"; + DCHECK(traits.use_thread_pool()) + << "The base::ThreadPool() trait is mandatory with PostJob()."; + DCHECK_EQ(traits.extension_id(), + TaskTraitsExtensionStorage::kInvalidExtensionId) + << "Extension traits cannot be used with PostJob()."; + TaskTraits adjusted_traits = traits; + adjusted_traits.InheritPriority(internal::GetTaskPriorityForCurrentThread()); + auto task_source = base::MakeRefCounted<internal::JobTaskSource>( + from_here, adjusted_traits, std::move(worker_task), + std::move(max_concurrency_callback), + static_cast<internal::ThreadPoolImpl*>(ThreadPoolInstance::Get())); + const bool queued = + static_cast<internal::ThreadPoolImpl*>(ThreadPoolInstance::Get()) + ->EnqueueJobTaskSource(task_source); + if (queued) + return internal::JobTaskSource::CreateJobHandle(std::move(task_source)); + return JobHandle(); +} + } // namespace experimental } // namespace base
\ No newline at end of file diff --git a/chromium/base/task/post_job.h b/chromium/base/task/post_job.h index de6b2d66ac3..cf8ca1aec70 100644 --- a/chromium/base/task/post_job.h +++ b/chromium/base/task/post_job.h @@ -6,8 +6,12 @@ #define BASE_TASK_POST_JOB_H_ #include "base/base_export.h" +#include "base/callback.h" +#include "base/location.h" #include "base/logging.h" #include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/task/task_traits.h" #include "base/time/time.h" namespace base { @@ -23,8 +27,9 @@ class BASE_EXPORT JobDelegate { public: // A JobDelegate is instantiated for each worker task that is run. // |task_source| is the task source whose worker task is running with this - // delegate and |pooled_task_runner_delegate| provides communication with the - // thread pool. + // delegate and |pooled_task_runner_delegate| is used by ShouldYield() to + // check whether the pool wants this worker task to yield (null if this worker + // should never yield -- e.g. when the main thread is a worker). JobDelegate(internal::JobTaskSource* task_source, internal::PooledTaskRunnerDelegate* pooled_task_runner_delegate); ~JobDelegate(); @@ -42,7 +47,7 @@ class BASE_EXPORT JobDelegate { void YieldIfNeeded(); // Notifies the scheduler that max concurrency was increased, and the number - // of worker should be adjusted. + // of worker should be adjusted accordingly. See PostJob() for more details. void NotifyConcurrencyIncrease(); private: @@ -67,6 +72,95 @@ class BASE_EXPORT JobDelegate { DISALLOW_COPY_AND_ASSIGN(JobDelegate); }; +// Handle returned when posting a Job. Provides methods to control execution of +// the posted Job. +class BASE_EXPORT JobHandle { + public: + JobHandle(); + // A job must either be joined, canceled or detached before the JobHandle is + // destroyed. + ~JobHandle(); + + JobHandle(JobHandle&&); + JobHandle& operator=(JobHandle&&); + + // Update this Job's priority. + void UpdatePriority(TaskPriority new_priority); + + // Notifies the scheduler that max concurrency was increased, and the number + // of workers should be adjusted accordingly. See PostJob() for more details. + void NotifyConcurrencyIncrease(); + + // Contributes to the job on this thread. Doesn't return until all tasks have + // completed and max concurrency becomes 0. This also promotes this Job's + // priority to be at least as high as the calling thread's priority. + void Join(); + + // Forces all existing workers to yield ASAP. Waits until they have all + // returned from the Job's callback before returning. + void Cancel(); + + // Forces all existing workers to yield ASAP but doesn’t wait for them. + // Warning, this is dangerous if the Job's callback is bound to or has access + // to state which may be deleted after this call. + void CancelAndDetach(); + + // Can be invoked before ~JobHandle() to avoid waiting on the job completing. + void Detach(); + + private: + friend class internal::JobTaskSource; + + explicit JobHandle(scoped_refptr<internal::JobTaskSource> task_source); + + scoped_refptr<internal::JobTaskSource> task_source_; + + DISALLOW_COPY_AND_ASSIGN(JobHandle); +}; + +// Posts a repeating |worker_task| with specific |traits| to run in parallel. +// Returns a JobHandle associated with the Job, which can be joined, canceled or +// detached. +// To avoid scheduling overhead, |worker_task| should do as much work as +// possible in a loop when invoked, and JobDelegate::ShouldYield() should be +// periodically invoked to conditionally exit and let the scheduler prioritize +// work. +// +// A canonical implementation of |worker_task| looks like: +// void WorkerTask(JobDelegate* job_delegate) { +// while (!job_delegate->ShouldYield()) { +// auto work_item = worker_queue.TakeWorkItem(); // Smallest unit of work. +// if (!work_item) +// return: +// ProcessWork(work_item); +// } +// } +// +// |max_concurrency_callback| controls the maximum number of threads calling +// |worker_task| concurrently. |worker_task| is only invoked if the number of +// threads previously running |worker_task| was less than the value returned by +// |max_concurrency_callback|. In general, |max_concurrency_callback| should +// return the latest number of incomplete work items (smallest unit of work) +// left to processed. JobHandle/JobDelegate::NotifyConcurrencyIncrease() *must* +// be invoked shortly after |max_concurrency_callback| starts returning a value +// larger than previously returned values. This usually happens when new work +// items are added and the API user wants additional threads to invoke +// |worker_task| concurrently. The callbacks may be called concurrently on any +// thread until the job is complete. If the job handle is detached, the +// callbacks may still be called, so they must not access global state that +// could be destroyed. +// +// |traits| requirements: +// - base::ThreadPool() must be specified. +// - Extension traits (e.g. BrowserThread) cannot be specified. +// - base::ThreadPolicy must be specified if the priority of the task runner +// will ever be increased from BEST_EFFORT. +JobHandle BASE_EXPORT +PostJob(const Location& from_here, + const TaskTraits& traits, + RepeatingCallback<void(JobDelegate*)> worker_task, + RepeatingCallback<size_t()> max_concurrency_callback); + } // namespace experimental } // namespace base diff --git a/chromium/base/task/post_job_unittest.cc b/chromium/base/task/post_job_unittest.cc new file mode 100644 index 00000000000..3fc3d6d4b07 --- /dev/null +++ b/chromium/base/task/post_job_unittest.cc @@ -0,0 +1,40 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/task/post_job.h" + +#include <atomic> + +#include "base/task/test_task_traits_extension.h" +#include "base/test/bind_test_util.h" +#include "base/test/gtest_util.h" +#include "base/test/task_environment.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +TEST(PostJobTest, PostJobSimple) { + test::TaskEnvironment task_environment; + std::atomic_size_t num_tasks_to_run(4); + auto handle = experimental::PostJob( + FROM_HERE, ThreadPool(), + BindLambdaForTesting( + [&](experimental::JobDelegate* delegate) { --num_tasks_to_run; }), + BindLambdaForTesting([&]() -> size_t { return num_tasks_to_run; })); + handle.Join(); + DCHECK_EQ(num_tasks_to_run, 0U); +} + +TEST(PostJobTest, PostJobExtension) { + testing::FLAGS_gtest_death_test_style = "threadsafe"; + EXPECT_DCHECK_DEATH({ + auto handle = experimental::PostJob( + FROM_HERE, TestExtensionBoolTrait(), + BindRepeating([](experimental::JobDelegate* delegate) {}), + BindRepeating([]() -> size_t { return 0; })); + }); +} + +} // namespace base
\ No newline at end of file diff --git a/chromium/base/task/post_task.cc b/chromium/base/task/post_task.cc index 76415fbfe56..244e5f6adc8 100644 --- a/chromium/base/task/post_task.cc +++ b/chromium/base/task/post_task.cc @@ -41,6 +41,13 @@ TaskTraits GetTaskTraitsWithExplicitPriority(TaskTraits traits) { } TaskExecutor* GetTaskExecutorForTraits(const TaskTraits& traits) { + if (traits.use_current_thread()) { + TaskExecutor* executor = GetTaskExecutorForCurrentThread(); + DCHECK(executor) << "Couldn't find a TaskExecutor for this thread. Note " + "you can't use base::CurrentThread in a one-off " + "base::ThreadPool task."; + return executor; + } TaskExecutor* executor = GetRegisteredTaskExecutorForTraits(traits); DCHECK(executor || ThreadPoolInstance::Get()) << "Ref. Prerequisite section of post_task.h.\n\n" @@ -139,54 +146,4 @@ scoped_refptr<SingleThreadTaskRunner> CreateCOMSTATaskRunner( } #endif // defined(OS_WIN) -// TODO(crbug.com/968047): Update all call sites and remove these forwarding -// wrappers. -bool PostTaskWithTraits(const Location& from_here, - const TaskTraits& traits, - OnceClosure task) { - return PostTask(from_here, traits, std::move(task)); -} - -bool PostDelayedTaskWithTraits(const Location& from_here, - const TaskTraits& traits, - OnceClosure task, - TimeDelta delay) { - return PostDelayedTask(from_here, traits, std::move(task), delay); -} - -bool PostTaskWithTraitsAndReply(const Location& from_here, - const TaskTraits& traits, - OnceClosure task, - OnceClosure reply) { - return PostTaskAndReply(from_here, traits, std::move(task), std::move(reply)); -} - -scoped_refptr<TaskRunner> CreateTaskRunnerWithTraits(const TaskTraits& traits) { - return CreateTaskRunner(traits); -} - -scoped_refptr<SequencedTaskRunner> CreateSequencedTaskRunnerWithTraits( - const TaskTraits& traits) { - return CreateSequencedTaskRunner(traits); -} - -scoped_refptr<UpdateableSequencedTaskRunner> -CreateUpdateableSequencedTaskRunnerWithTraits(const TaskTraits& traits) { - return CreateUpdateableSequencedTaskRunner(traits); -} - -scoped_refptr<SingleThreadTaskRunner> CreateSingleThreadTaskRunnerWithTraits( - const TaskTraits& traits, - SingleThreadTaskRunnerThreadMode thread_mode) { - return CreateSingleThreadTaskRunner(traits, thread_mode); -} - -#if defined(OS_WIN) -scoped_refptr<SingleThreadTaskRunner> CreateCOMSTATaskRunnerWithTraits( - const TaskTraits& traits, - SingleThreadTaskRunnerThreadMode thread_mode) { - return CreateCOMSTATaskRunner(traits, thread_mode); -} -#endif // defined(OS_WIN) - } // namespace base diff --git a/chromium/base/task/post_task.h b/chromium/base/task/post_task.h index b02fd65a032..9f9b5a24fa3 100644 --- a/chromium/base/task/post_task.h +++ b/chromium/base/task/post_task.h @@ -171,21 +171,6 @@ bool PostTaskAndReplyWithResult(const Location& from_here, std::move(reply), Owned(result))); } -// Temporary wrapper for PostTaskAndReplyWithResult. -// TODO(crbug.com/968047): Update all call sites and remove. -template <template <typename> class CallbackType, - typename TaskReturnType, - typename ReplyArgType, - typename = EnableIfIsBaseCallback<CallbackType>> -bool PostTaskWithTraitsAndReplyWithResult( - const Location& from_here, - const TaskTraits& traits, - CallbackType<TaskReturnType()> task, - CallbackType<void(ReplyArgType)> reply) { - return PostTaskAndReplyWithResult(from_here, traits, std::move(task), - std::move(reply)); -} - // Returns a TaskRunner whose PostTask invocations result in scheduling tasks // using |traits|. Tasks may run in any order and in parallel. BASE_EXPORT scoped_refptr<TaskRunner> CreateTaskRunner( @@ -247,38 +232,6 @@ BASE_EXPORT scoped_refptr<SingleThreadTaskRunner> CreateCOMSTATaskRunner( SingleThreadTaskRunnerThreadMode::SHARED); #endif // defined(OS_WIN) -// Temporary wrappers for the task posting APIs while we remove the "WithTraits" -// suffix. -// TODO(crbug.com/968047): Update all call sites and remove. -BASE_EXPORT bool PostTaskWithTraits(const Location& from_here, - const TaskTraits& traits, - OnceClosure task); -BASE_EXPORT bool PostDelayedTaskWithTraits(const Location& from_here, - const TaskTraits& traits, - OnceClosure task, - TimeDelta delay); -BASE_EXPORT bool PostTaskWithTraitsAndReply(const Location& from_here, - const TaskTraits& traits, - OnceClosure task, - OnceClosure reply); -BASE_EXPORT scoped_refptr<TaskRunner> CreateTaskRunnerWithTraits( - const TaskTraits& traits); -BASE_EXPORT scoped_refptr<SequencedTaskRunner> -CreateSequencedTaskRunnerWithTraits(const TaskTraits& traits); -BASE_EXPORT scoped_refptr<UpdateableSequencedTaskRunner> -CreateUpdateableSequencedTaskRunnerWithTraits(const TaskTraits& traits); -BASE_EXPORT scoped_refptr<SingleThreadTaskRunner> -CreateSingleThreadTaskRunnerWithTraits( - const TaskTraits& traits, - SingleThreadTaskRunnerThreadMode thread_mode = - SingleThreadTaskRunnerThreadMode::SHARED); -#if defined(OS_WIN) -BASE_EXPORT scoped_refptr<SingleThreadTaskRunner> -CreateCOMSTATaskRunnerWithTraits(const TaskTraits& traits, - SingleThreadTaskRunnerThreadMode thread_mode = - SingleThreadTaskRunnerThreadMode::SHARED); -#endif // defined(OS_WIN) - } // namespace base #endif // BASE_TASK_POST_TASK_H_ diff --git a/chromium/base/task/post_task_unittest.cc b/chromium/base/task/post_task_unittest.cc index ed7fb3db3bb..798eba8e580 100644 --- a/chromium/base/task/post_task_unittest.cc +++ b/chromium/base/task/post_task_unittest.cc @@ -5,9 +5,11 @@ #include "base/task/post_task.h" #include "base/bind_helpers.h" +#include "base/run_loop.h" #include "base/task/scoped_set_task_priority_for_current_thread.h" #include "base/task/task_executor.h" #include "base/task/test_task_traits_extension.h" +#include "base/test/bind_test_util.h" #include "base/test/gtest_util.h" #include "base/test/task_environment.h" #include "base/test/test_simple_task_runner.h" @@ -17,6 +19,8 @@ using ::testing::_; using ::testing::Invoke; +using ::testing::IsNull; +using ::testing::NotNull; using ::testing::Return; namespace base { @@ -177,6 +181,159 @@ TEST_F(PostTaskTestWithExecutor, PostTaskToTaskExecutor) { } } +TEST_F(PostTaskTestWithExecutor, + ThreadPoolTaskRunnerGetTaskExecutorForCurrentThread) { + auto task_runner = CreateTaskRunner({ThreadPool()}); + RunLoop run_loop; + + EXPECT_TRUE(task_runner->PostTask( + FROM_HERE, BindLambdaForTesting([&]() { + // We don't have an executor for a ThreadPool task runner becuse they + // are for one shot tasks. + EXPECT_THAT(GetTaskExecutorForCurrentThread(), IsNull()); + run_loop.Quit(); + }))); + + run_loop.Run(); +} + +TEST_F(PostTaskTestWithExecutor, + ThreadPoolSequencedTaskRunnerGetTaskExecutorForCurrentThread) { + auto sequenced_task_runner = CreateSequencedTaskRunner({ThreadPool()}); + RunLoop run_loop; + + EXPECT_TRUE(sequenced_task_runner->PostTask( + FROM_HERE, BindLambdaForTesting([&]() { + EXPECT_THAT(GetTaskExecutorForCurrentThread(), NotNull()); + run_loop.Quit(); + }))); + + run_loop.Run(); +} + +TEST_F(PostTaskTestWithExecutor, + ThreadPoolSingleThreadTaskRunnerGetTaskExecutorForCurrentThread) { + auto single_thread_task_runner = CreateSingleThreadTaskRunner({ThreadPool()}); + RunLoop run_loop; + + EXPECT_TRUE(single_thread_task_runner->PostTask( + FROM_HERE, BindLambdaForTesting([&]() { + EXPECT_THAT(GetTaskExecutorForCurrentThread(), NotNull()); + run_loop.Quit(); + }))); + + run_loop.Run(); +} + +TEST_F(PostTaskTestWithExecutor, ThreadPoolTaskRunnerCurrentThreadTrait) { + auto task_runner = CreateTaskRunner({ThreadPool()}); + RunLoop run_loop; + + EXPECT_TRUE(task_runner->PostTask(FROM_HERE, BindLambdaForTesting([&]() { + // CurrentThread is meaningless in this + // context. + EXPECT_DCHECK_DEATH( + PostTask(FROM_HERE, {CurrentThread()}, + DoNothing())); + run_loop.Quit(); + }))); + + run_loop.Run(); +} + +TEST_F(PostTaskTestWithExecutor, + ThreadPoolSequencedTaskRunnerCurrentThreadTrait) { + auto sequenced_task_runner = CreateSequencedTaskRunner({ThreadPool()}); + RunLoop run_loop; + + auto current_thread_task = BindLambdaForTesting([&]() { + EXPECT_TRUE(sequenced_task_runner->RunsTasksInCurrentSequence()); + run_loop.Quit(); + }); + + EXPECT_TRUE(sequenced_task_runner->PostTask( + FROM_HERE, BindLambdaForTesting([&]() { + EXPECT_TRUE( + PostTask(FROM_HERE, {CurrentThread()}, current_thread_task)); + }))); + + run_loop.Run(); +} + +TEST_F(PostTaskTestWithExecutor, + ThreadPoolSingleThreadTaskRunnerCurrentThreadTrait) { + auto single_thread_task_runner = CreateSingleThreadTaskRunner({ThreadPool()}); + RunLoop run_loop; + + auto current_thread_task = BindLambdaForTesting([&]() { + EXPECT_TRUE(single_thread_task_runner->RunsTasksInCurrentSequence()); + run_loop.Quit(); + }); + + EXPECT_TRUE(single_thread_task_runner->PostTask( + FROM_HERE, BindLambdaForTesting([&]() { + EXPECT_TRUE( + PostTask(FROM_HERE, {CurrentThread()}, current_thread_task)); + }))); + + run_loop.Run(); +} + +TEST_F(PostTaskTestWithExecutor, ThreadPoolCurrentThreadChangePriority) { + auto single_thread_task_runner = + CreateSingleThreadTaskRunner({ThreadPool(), TaskPriority::USER_BLOCKING}); + RunLoop run_loop; + + auto current_thread_task = BindLambdaForTesting([&]() { + EXPECT_TRUE(single_thread_task_runner->RunsTasksInCurrentSequence()); + run_loop.Quit(); + }); + + EXPECT_TRUE(single_thread_task_runner->PostTask( + FROM_HERE, BindLambdaForTesting([&]() { + // We should be able to request a priority change, although it may be + // ignored. + EXPECT_TRUE(PostTask(FROM_HERE, + {CurrentThread(), TaskPriority::USER_VISIBLE}, + current_thread_task)); + }))); + + run_loop.Run(); +} + +TEST_F(PostTaskTestWithExecutor, + ThreadPoolCurrentThreadCantChangeShutdownBehavior) { + auto single_thread_task_runner = CreateSingleThreadTaskRunner( + {ThreadPool(), TaskShutdownBehavior::SKIP_ON_SHUTDOWN}); + RunLoop run_loop; + + EXPECT_TRUE(single_thread_task_runner->PostTask( + FROM_HERE, BindLambdaForTesting([&]() { + EXPECT_DCHECK_DEATH(PostTask( + FROM_HERE, {CurrentThread(), TaskShutdownBehavior::BLOCK_SHUTDOWN}, + DoNothing())); + run_loop.Quit(); + }))); + + run_loop.Run(); +} + +TEST_F(PostTaskTestWithExecutor, + ThreadPoolCurrentThreadCantSetSyncPrimitivesInNonSyncTaskRunner) { + auto single_thread_task_runner = CreateSingleThreadTaskRunner({ThreadPool()}); + RunLoop run_loop; + + EXPECT_TRUE(single_thread_task_runner->PostTask( + FROM_HERE, BindLambdaForTesting([&]() { + EXPECT_DCHECK_DEATH( + PostTask(FROM_HERE, {CurrentThread(), WithBaseSyncPrimitives()}, + DoNothing())); + run_loop.Quit(); + }))); + + run_loop.Run(); +} + TEST_F(PostTaskTestWithExecutor, RegisterExecutorTwice) { testing::FLAGS_gtest_death_test_style = "threadsafe"; EXPECT_DCHECK_DEATH( diff --git a/chromium/base/task/promise/abstract_promise.cc b/chromium/base/task/promise/abstract_promise.cc index f48ba8517e2..315cfd30bfc 100644 --- a/chromium/base/task/promise/abstract_promise.cc +++ b/chromium/base/task/promise/abstract_promise.cc @@ -8,6 +8,7 @@ #include "base/lazy_instance.h" #include "base/sequenced_task_runner.h" #include "base/task/promise/dependent_list.h" +#include "base/task/promise/post_task_executor.h" #include "base/threading/sequenced_task_runner_handle.h" namespace base { @@ -65,6 +66,10 @@ AbstractPromise::~AbstractPromise() { OnCanceled(); } +void AbstractPromise::EmplaceResolvedVoid() { + emplace(Resolved<void>()); +} + bool AbstractPromise::IsCanceled() const { if (dependents_.IsCanceled()) return true; @@ -290,7 +295,7 @@ AbstractPromise* AbstractPromise::GetCurriedPromise() { const PromiseExecutor* AbstractPromise::GetExecutor() const { if (!value_.ContainsPromiseExecutor()) return nullptr; - return value_.Get<internal::PromiseExecutor>(); + return value_.Get<PromiseExecutor>(); } PromiseExecutor::PrerequisitePolicy AbstractPromise::GetPrerequisitePolicy() { @@ -509,7 +514,7 @@ void AbstractPromise::OnRejectMakeDependantsUseCurriedPrerequisite( void AbstractPromise::DispatchPromise() { if (task_runner_) { - task_runner_->PostPromiseInternal(this, TimeDelta()); + task_runner_->PostPromiseInternal(WrappedPromise(this), TimeDelta()); } else { Execute(); } @@ -670,14 +675,66 @@ void AbstractPromise::AdjacencyList::Clear() { prerequisite_list_.clear(); } else { // If there's multiple prerequisites we can't do that because the - // DependentList::Nodes may still be in use by some of them. Instead we - // release our prerequisite references and rely on refcounting to release - // the owning AbstractPromise. + // DependentList::Nodes may still be in use by some of them. + // Instead we release our prerequisite references and rely on refcounting to + // release the owning AbstractPromise. for (DependentList::Node& node : prerequisite_list_) { node.ClearPrerequisite(); } } } +BasePromise::BasePromise() = default; + +BasePromise::BasePromise( + scoped_refptr<internal::AbstractPromise> abstract_promise) + : abstract_promise_(std::move(abstract_promise)) {} + +BasePromise::BasePromise(const BasePromise& other) = default; +BasePromise::BasePromise(BasePromise&& other) = default; + +BasePromise& BasePromise::operator=(const BasePromise& other) = default; +BasePromise& BasePromise::operator=(BasePromise&& other) = default; + +BasePromise::~BasePromise() = default; + } // namespace internal + +WrappedPromise::WrappedPromise() = default; + +WrappedPromise::WrappedPromise(scoped_refptr<internal::AbstractPromise> promise) + : promise_(std::move(promise)) {} + +WrappedPromise::WrappedPromise(internal::PassedPromise&& passed_promise) + : promise_(passed_promise.Release(), subtle::kAdoptRefTag) { + DCHECK(promise_); +} + +WrappedPromise::WrappedPromise(const Location& from_here, OnceClosure task) + : WrappedPromise(internal::AbstractPromise::CreateNoPrerequisitePromise( + from_here, + RejectPolicy::kMustCatchRejection, + internal::DependentList::ConstructUnresolved(), + internal::PromiseExecutor::Data( + base::in_place_type_t<internal::PostTaskExecutor<void>>(), + std::move(task)))) {} + +WrappedPromise::WrappedPromise(const WrappedPromise& other) = default; +WrappedPromise::WrappedPromise(WrappedPromise&& other) = default; + +WrappedPromise& WrappedPromise::operator=(const WrappedPromise& other) = + default; +WrappedPromise& WrappedPromise::operator=(WrappedPromise&& other) = default; + +WrappedPromise::~WrappedPromise() = default; + +void WrappedPromise::Execute() { + DCHECK(promise_); + promise_->Execute(); +} + +void WrappedPromise::Clear() { + promise_ = nullptr; +} + } // namespace base diff --git a/chromium/base/task/promise/abstract_promise.h b/chromium/base/task/promise/abstract_promise.h index 9f67c688bd4..c16132c187c 100644 --- a/chromium/base/task/promise/abstract_promise.h +++ b/chromium/base/task/promise/abstract_promise.h @@ -23,6 +23,9 @@ class TaskRunner; template <typename ResolveType, typename RejectType> class ManualPromiseResolver; +template <typename ResolveType, typename RejectType> +class Promise; + // AbstractPromise Memory Management. // // Consider a chain of promises: P1, P2 & P3 @@ -265,13 +268,71 @@ enum class RejectPolicy { kCatchNotRequired, }; +class WrappedPromise; + namespace internal { template <typename T, typename... Args> class PromiseCallbackHelper; -class PromiseHolder; +class AbstractPromise; class AbstractPromiseTest; +class BasePromise; + +// A binary size optimization to reduce the overhead of passing a scoped_refptr +// to Promise<> returned by PostTask. There are many thousands of PostTasks so +// even a single extra instruction (such as the scoped_refptr move constructor +// clearing the pointer) adds up. This is why we're not constructing a Promise<> +// with a scoped_refptr. +// +// The constructor calls AddRef, it's up to the owner of this object to either +// call Clear (which calls Release) or AbstractPromise in order to pass +// ownership onto a WrappedPromise. +class BASE_EXPORT PassedPromise { + public: + explicit inline PassedPromise(const scoped_refptr<AbstractPromise>& promise); + + PassedPromise() : promise_(nullptr) {} + + PassedPromise(const PassedPromise&) = delete; + PassedPromise& operator=(const PassedPromise&) = delete; + +#if DCHECK_IS_ON() + PassedPromise(PassedPromise&& other) noexcept : promise_(other.promise_) { + DCHECK(promise_); + other.promise_ = nullptr; + } + + PassedPromise& operator=(PassedPromise&& other) noexcept { + DCHECK(!promise_); + promise_ = other.promise_; + DCHECK(promise_); + other.promise_ = nullptr; + return *this; + } + + ~PassedPromise() { + DCHECK(!promise_) << "The PassedPromise must be Cleared or passed onto a " + "Wrapped Promise"; + } +#else + PassedPromise(PassedPromise&&) noexcept = default; + PassedPromise& operator=(PassedPromise&&) noexcept = default; +#endif + + AbstractPromise* Release() { + AbstractPromise* promise = promise_; +#if DCHECK_IS_ON() + promise_ = nullptr; +#endif + return promise; + } + + AbstractPromise* get() const { return promise_; } + + private: + AbstractPromise* promise_; +}; // Internal promise representation, maintains a graph of dependencies and posts // promises as they become ready. In debug builds various sanity checks are @@ -308,12 +369,11 @@ class BASE_EXPORT AbstractPromise RejectPolicy reject_policy, ConstructType tag, PromiseExecutor::Data&& executor_data) noexcept { - scoped_refptr<AbstractPromise> promise = - subtle::AdoptRefIfNeeded(new internal::AbstractPromise( - nullptr, from_here, nullptr, reject_policy, - tag, std::move(executor_data)), - AbstractPromise::kRefCountPreference); - return promise; + return subtle::AdoptRefIfNeeded( + new internal::AbstractPromise(nullptr, from_here, nullptr, + reject_policy, tag, + std::move(executor_data)), + AbstractPromise::kRefCountPreference); } AbstractPromise(const AbstractPromise&) = delete; @@ -347,7 +407,9 @@ class BASE_EXPORT AbstractPromise public: PromiseValue& value() { return value_; } +#if DCHECK_IS_ON() ~ValueHandle() { value_.reset(); } +#endif private: friend class AbstractPromise; @@ -357,6 +419,8 @@ class BASE_EXPORT AbstractPromise PromiseValue& value_; }; + // Used for promise results that require move semantics. E.g. a promise chain + // involving a std::unique_ptr<>. ValueHandle TakeValue() { return ValueHandle(value_); } // Returns nullptr if there isn't a curried promise. @@ -380,6 +444,10 @@ class BASE_EXPORT AbstractPromise "Use scoped_refptr<AbstractPromise> instead"); } + // An out-of line emplace(Resolved<void>()); Useful for reducing binary + // bloat in executor templates. + void EmplaceResolvedVoid(); + // This is separate from AbstractPromise to reduce the memory footprint of // regular PostTask without promise chains. class BASE_EXPORT AdjacencyList { @@ -457,9 +525,14 @@ class BASE_EXPORT AbstractPromise void IgnoreUncaughtCatchForTesting(); - private: - friend class AbstractPromiseTest; + // Signals that this promise was cancelled. If executor hasn't run yet, this + // will prevent it from running and cancels any dependent promises unless they + // have PrerequisitePolicy::kAny, in which case they will only be canceled if + // all of their prerequisites are canceled. If OnCanceled() or OnResolved() or + // OnRejected() has already run, this does nothing. + void OnCanceled(); + private: friend base::RefCountedThreadSafe<AbstractPromise>; friend class AbstractPromiseTest; @@ -470,8 +543,6 @@ class BASE_EXPORT AbstractPromise template <typename T, typename... Args> friend class PromiseCallbackHelper; - friend class PromiseHolder; - template <typename ConstructType> AbstractPromise(const scoped_refptr<TaskRunner>& task_runner, const Location& from_here, @@ -520,13 +591,6 @@ class BASE_EXPORT AbstractPromise // have been canceled, in which case null is returned. AbstractPromise* FindCurriedAncestor(); - // Signals that this promise was cancelled. If executor hasn't run yet, this - // will prevent it from running and cancels any dependent promises unless they - // have PrerequisitePolicy::kAny, in which case they will only be canceled if - // all of their prerequisites are canceled. If OnCanceled() or OnResolved() or - // OnRejected() has already run, this does nothing. - void OnCanceled(); - // Signals that |value_| now contains a resolve value. Dependent promises may // scheduled for execution. void OnResolved(); @@ -714,7 +778,115 @@ class BASE_EXPORT AbstractPromise std::unique_ptr<AdjacencyList> prerequisites_; }; +PassedPromise::PassedPromise(const scoped_refptr<AbstractPromise>& promise) + : promise_(promise.get()) { + promise_->AddRef(); +} + +// Non-templatized base class of the Promise<> template. This is a binary size +// optimization, letting us use an out of line destructor in the template +// instead of the more complex scoped_refptr<> destructor. +class BASE_EXPORT BasePromise { + public: + BasePromise(); + + BasePromise(const BasePromise& other); + BasePromise(BasePromise&& other) noexcept; + + BasePromise& operator=(const BasePromise& other); + BasePromise& operator=(BasePromise&& other) noexcept; + + // We want an out of line destructor to reduce binary size. + ~BasePromise(); + + // Returns true if the promise is not null. + operator bool() const { return abstract_promise_.get(); } + + protected: + struct InlineConstructor {}; + + explicit BasePromise( + scoped_refptr<internal::AbstractPromise> abstract_promise); + + // We want this to be inlined to reduce binary size for the Promise<> + // constructor. Its a template to bypass ChromiumStyle plugin which otherwise + // insists this is out of line. + template <typename T> + explicit BasePromise(internal::PassedPromise&& passed_promise, + T InlineConstructor) + : abstract_promise_(passed_promise.Release(), subtle::kAdoptRefTag) {} + + scoped_refptr<internal::AbstractPromise> abstract_promise_; +}; + } // namespace internal + +// Wrapper around scoped_refptr<base::internal::AbstractPromise> which is +// intended for use by TaskRunner implementations. +class BASE_EXPORT WrappedPromise { + public: + WrappedPromise(); + + explicit WrappedPromise(scoped_refptr<internal::AbstractPromise> promise); + + WrappedPromise(const WrappedPromise& other); + WrappedPromise(WrappedPromise&& other) noexcept; + + WrappedPromise& operator=(const WrappedPromise& other); + WrappedPromise& operator=(WrappedPromise&& other) noexcept; + + explicit WrappedPromise(internal::PassedPromise&& passed_promise); + + // Constructs a promise to run |task|. + WrappedPromise(const Location& from_here, OnceClosure task); + + // If the WrappedPromise hasn't been executed, cleared or taken by + // TakeForTesting, it will be canceled to prevent memory leaks of dependent + // tasks that will never run. + ~WrappedPromise(); + + // Returns true if the promise is not null. + operator bool() const { return promise_.get(); } + + bool IsCanceled() const { + DCHECK(promise_); + return promise_->IsCanceled(); + } + + void OnCanceled() { + DCHECK(promise_); + promise_->OnCanceled(); + } + + // Can only be called once, clears |promise_| after execution. + void Execute(); + + // Clears |promise_|. + void Clear(); + + const Location& from_here() const { + DCHECK(promise_); + return promise_->from_here(); + } + + scoped_refptr<internal::AbstractPromise>& GetForTesting() { return promise_; } + + scoped_refptr<internal::AbstractPromise> TakeForTesting() { + return std::move(promise_); + } + + private: + template <typename ResolveType, typename RejectType> + friend class Promise; + + template <typename T, typename... Args> + friend class internal::PromiseCallbackHelper; + + friend class Promises; + + scoped_refptr<internal::AbstractPromise> promise_; +}; + } // namespace base #endif // BASE_TASK_PROMISE_ABSTRACT_PROMISE_H_ diff --git a/chromium/base/task/promise/abstract_promise_unittest.cc b/chromium/base/task/promise/abstract_promise_unittest.cc index ea8d31fd898..5cbe4498d54 100644 --- a/chromium/base/task/promise/abstract_promise_unittest.cc +++ b/chromium/base/task/promise/abstract_promise_unittest.cc @@ -238,10 +238,13 @@ class AbstractPromiseTest : public testing::Test { #endif std::move(settings.callback)); - return AbstractPromise::Create( - settings.task_runner, settings.from_here, - std::move(settings.prerequisites), settings.reject_policy, - DependentList::ConstructUnresolved(), std::move(executor_data)); + return WrappedPromise(AbstractPromise::Create( + settings.task_runner, settings.from_here, + std::move(settings.prerequisites), + settings.reject_policy, + DependentList::ConstructUnresolved(), + std::move(executor_data))) + .TakeForTesting(); } PromiseSettings settings; @@ -293,7 +296,7 @@ class AbstractPromiseTest : public testing::Test { PromiseSettingsBuilder AllPromise( Location from_here, - std::vector<internal::DependentList::Node> prerequisite_list) { + std::vector<DependentList::Node> prerequisite_list) { PromiseSettingsBuilder builder( from_here, std::make_unique<AbstractPromise::AdjacencyList>( std::move(prerequisite_list))); diff --git a/chromium/base/task/promise/dependent_list.h b/chromium/base/task/promise/dependent_list.h index 020bdbfc77f..3245c1cb48f 100644 --- a/chromium/base/task/promise/dependent_list.h +++ b/chromium/base/task/promise/dependent_list.h @@ -59,7 +59,7 @@ class BASE_EXPORT DependentList { // Align Node on an 8-byte boundary to ensure the first 3 bits are 0 and can // be used to store additional state (see static_asserts below). - class BASE_EXPORT alignas(8) Node { + class BASE_EXPORT ALIGNAS(8) Node { public: Node(); explicit Node(Node&& other) noexcept; diff --git a/chromium/base/task/promise/finally_executor.h b/chromium/base/task/promise/finally_executor.h index 7dc1c798a5c..a80f81c5ee9 100644 --- a/chromium/base/task/promise/finally_executor.h +++ b/chromium/base/task/promise/finally_executor.h @@ -43,9 +43,15 @@ class FinallyExecutor { void Execute(AbstractPromise* promise) { AbstractPromise* prerequisite = promise->GetOnlyPrerequisite(); - CallbackT* resolve_executor = static_cast<CallbackT*>(&common_.callback_); - RunHelper<CallbackT, void, ResolveStorage, RejectStorage>::Run( - std::move(*resolve_executor), prerequisite, promise); + // Internally RunHelper uses const RepeatingCallback<>& to avoid the + // binary size overhead of moving a scoped_refptr<> about. We respect + // the onceness of the callback and RunHelper will overwrite the callback + // with the result. + using RepeatingCB = typename ToRepeatingCallback<CallbackT>::value; + RepeatingCB* resolve_executor = + static_cast<RepeatingCB*>(&common_.callback_); + RunHelper<RepeatingCB, void, ResolveStorage, RejectStorage>::Run( + *resolve_executor, prerequisite, promise); } #if DCHECK_IS_ON() diff --git a/chromium/base/task/promise/helpers.cc b/chromium/base/task/promise/helpers.cc index 8a68a123956..f100cac0c29 100644 --- a/chromium/base/task/promise/helpers.cc +++ b/chromium/base/task/promise/helpers.cc @@ -11,32 +11,11 @@ namespace base { namespace internal { -PromiseHolder::PromiseHolder(scoped_refptr<AbstractPromise> promise) - : promise_(std::move(promise)) {} - -PromiseHolder::~PromiseHolder() { - // Detect if the promise was not executed and if so cancel to ensure memory - // is released. - if (promise_) - promise_->OnCanceled(); -} - -PromiseHolder::PromiseHolder(PromiseHolder&& other) - : promise_(std::move(other.promise_)) {} - -scoped_refptr<AbstractPromise> PromiseHolder::Unwrap() const { - return std::move(promise_); -} - -scoped_refptr<TaskRunner> GetCurrentSequence() { - return SequencedTaskRunnerHandle::Get(); -} - DoNothing ToCallbackBase(DoNothing task) { return task; } -scoped_refptr<AbstractPromise> ConstructAbstractPromiseWithSinglePrerequisite( +PassedPromise ConstructAbstractPromiseWithSinglePrerequisite( const scoped_refptr<TaskRunner>& task_runner, const Location& from_here, AbstractPromise* prerequisite, @@ -46,26 +25,35 @@ scoped_refptr<AbstractPromise> ConstructAbstractPromiseWithSinglePrerequisite( if (!prerequisite) { // Ensure the destructor for |executor_data| runs. PromiseExecutor dummy_executor(std::move(executor_data)); - return nullptr; + return PassedPromise(); } - return AbstractPromise::Create( + return PassedPromise(AbstractPromise::Create( task_runner, from_here, std::make_unique<AbstractPromise::AdjacencyList>(prerequisite), RejectPolicy::kMustCatchRejection, - internal::DependentList::ConstructUnresolved(), std::move(executor_data)); + internal::DependentList::ConstructUnresolved(), + std::move(executor_data))); } -scoped_refptr<AbstractPromise> ConstructManualPromiseResolverPromise( +PassedPromise ConstructHereAbstractPromiseWithSinglePrerequisite( const Location& from_here, - RejectPolicy reject_policy, - bool can_resolve, - bool can_reject) { - return AbstractPromise::CreateNoPrerequisitePromise( + AbstractPromise* prerequisite, + internal::PromiseExecutor::Data&& executor_data) noexcept { + return ConstructAbstractPromiseWithSinglePrerequisite( + SequencedTaskRunnerHandle::Get(), from_here, prerequisite, + std::move(executor_data)); +} + +PassedPromise ConstructManualPromiseResolverPromise(const Location& from_here, + RejectPolicy reject_policy, + bool can_resolve, + bool can_reject) { + return PassedPromise(AbstractPromise::CreateNoPrerequisitePromise( from_here, reject_policy, internal::DependentList::ConstructUnresolved(), internal::PromiseExecutor::Data( in_place_type_t<internal::NoOpPromiseExecutor>(), can_resolve, - can_reject)); + can_reject))); } } // namespace internal diff --git a/chromium/base/task/promise/helpers.h b/chromium/base/task/promise/helpers.h index 29cb49011ab..87d9ff64bec 100644 --- a/chromium/base/task/promise/helpers.h +++ b/chromium/base/task/promise/helpers.h @@ -19,11 +19,6 @@ class DoNothing; namespace internal { -// A wrapper around SequencedTaskRunnerHandle::Get(). This file is included by -// base/task_runner.h which means we can't include anything that depends on -// that! -scoped_refptr<TaskRunner> BASE_EXPORT GetCurrentSequence(); - template <typename T> using ToNonVoidT = std::conditional_t<std::is_void<T>::value, Void, T>; @@ -412,9 +407,26 @@ class ArgMoveSemanticsHelper { } }; +// Helper for converting a callback to its repeating variant. +template <typename Cb> +struct ToRepeatingCallback; + +template <typename Cb> +struct ToRepeatingCallback<OnceCallback<Cb>> { + using value = RepeatingCallback<Cb>; +}; + +template <typename Cb> +struct ToRepeatingCallback<RepeatingCallback<Cb>> { + using value = RepeatingCallback<Cb>; +}; + // Helper for running a promise callback and storing the result if any. // -// Callback = signature of the callback to execute, +// Callback = signature of the callback to execute. Note we use repeating +// callbacks to avoid the binary size overhead of a once callback which will +// generate a destructor which is redundant because we overwrite the executor +// with the promise result which also triggers the destructor. // ArgStorageType = type of the callback parameter (or void if none) // ResolveStorage = type to use for resolve, usually Resolved<T>. // RejectStorage = type to use for reject, usually Rejected<T>. @@ -431,18 +443,18 @@ template <typename CbResult, typename ArgStorageType, typename ResolveStorage, typename RejectStorage> -struct RunHelper<OnceCallback<CbResult(CbArg)>, +struct RunHelper<RepeatingCallback<CbResult(CbArg)>, ArgStorageType, ResolveStorage, RejectStorage> { - using Callback = OnceCallback<CbResult(CbArg)>; + using Callback = RepeatingCallback<CbResult(CbArg)>; - static void Run(Callback&& executor, + static void Run(const Callback& executor, AbstractPromise* arg, AbstractPromise* result) { EmplaceHelper<ResolveStorage, RejectStorage>::Emplace( - result, std::move(executor).Run( - ArgMoveSemanticsHelper<CbArg, ArgStorageType>::Get(arg))); + result, + executor.Run(ArgMoveSemanticsHelper<CbArg, ArgStorageType>::Get(arg))); } }; @@ -451,19 +463,18 @@ template <typename CbArg, typename ArgStorageType, typename ResolveStorage, typename RejectStorage> -struct RunHelper<OnceCallback<void(CbArg)>, +struct RunHelper<RepeatingCallback<void(CbArg)>, ArgStorageType, ResolveStorage, RejectStorage> { - using Callback = OnceCallback<void(CbArg)>; + using Callback = RepeatingCallback<void(CbArg)>; - static void Run(Callback&& executor, + static void Run(const Callback& executor, AbstractPromise* arg, AbstractPromise* result) { static_assert(std::is_void<typename ResolveStorage::Type>::value, ""); - std::move(executor).Run( - ArgMoveSemanticsHelper<CbArg, ArgStorageType>::Get(arg)); - result->emplace(Resolved<void>()); + executor.Run(ArgMoveSemanticsHelper<CbArg, ArgStorageType>::Get(arg)); + result->EmplaceResolvedVoid(); } }; @@ -472,17 +483,17 @@ template <typename CbResult, typename ArgStorageType, typename ResolveStorage, typename RejectStorage> -struct RunHelper<OnceCallback<CbResult()>, +struct RunHelper<RepeatingCallback<CbResult()>, ArgStorageType, ResolveStorage, RejectStorage> { - using Callback = OnceCallback<CbResult()>; + using Callback = RepeatingCallback<CbResult()>; - static void Run(Callback&& executor, + static void Run(const Callback& executor, AbstractPromise* arg, AbstractPromise* result) { - EmplaceHelper<ResolveStorage, RejectStorage>::Emplace( - result, std::move(executor).Run()); + EmplaceHelper<ResolveStorage, RejectStorage>::Emplace(result, + executor.Run()); } }; @@ -490,16 +501,16 @@ struct RunHelper<OnceCallback<CbResult()>, template <typename ArgStorageType, typename ResolveStorage, typename RejectStorage> -struct RunHelper<OnceCallback<void()>, +struct RunHelper<RepeatingCallback<void()>, ArgStorageType, ResolveStorage, RejectStorage> { - static void Run(OnceCallback<void()>&& executor, + static void Run(const RepeatingCallback<void()>& executor, AbstractPromise* arg, AbstractPromise* result) { static_assert(std::is_void<typename ResolveStorage::Type>::value, ""); - std::move(executor).Run(); - result->emplace(Resolved<void>()); + executor.Run(); + result->EmplaceResolvedVoid(); } }; @@ -538,79 +549,51 @@ template <typename CbResult, typename... CbArgs, typename ResolveStorage, typename RejectStorage> -struct RunHelper<OnceCallback<CbResult(CbArgs...)>, +struct RunHelper<RepeatingCallback<CbResult(CbArgs...)>, Resolved<std::tuple<CbArgs...>>, ResolveStorage, RejectStorage> { - using Callback = OnceCallback<CbResult(CbArgs...)>; + using Callback = RepeatingCallback<CbResult(CbArgs...)>; using StorageType = Resolved<std::tuple<CbArgs...>>; using IndexSequence = std::index_sequence_for<CbArgs...>; - static void Run(Callback&& executor, + static void Run(const Callback& executor, AbstractPromise* arg, AbstractPromise* result) { AbstractPromise::ValueHandle value = arg->TakeValue(); std::tuple<CbArgs...>& tuple = value.value().Get<StorageType>()->value; - RunInternal(std::move(executor), tuple, result, + RunInternal(executor, tuple, result, std::integral_constant<bool, std::is_void<CbResult>::value>(), IndexSequence{}); } private: template <typename Callback, size_t... Indices> - static void RunInternal(Callback&& executor, + static void RunInternal(const Callback& executor, std::tuple<CbArgs...>& tuple, AbstractPromise* result, std::false_type void_result, std::index_sequence<Indices...>) { - EmplaceHelper<ResolveStorage, RejectStorage>::Emplace( - std::move(executor).Run( - TupleArgMoveSemanticsHelper<Callback, std::tuple<CbArgs...>, - Indices>::Get(tuple)...)); + EmplaceHelper<ResolveStorage, RejectStorage>::Emplace(executor.Run( + TupleArgMoveSemanticsHelper<Callback, std::tuple<CbArgs...>, + Indices>::Get(tuple)...)); } template <typename Callback, size_t... Indices> - static void RunInternal(Callback&& executor, + static void RunInternal(const Callback& executor, std::tuple<CbArgs...>& tuple, AbstractPromise* result, std::true_type void_result, std::index_sequence<Indices...>) { - std::move(executor).Run( - TupleArgMoveSemanticsHelper<Callback, std::tuple<CbArgs...>, - Indices>::Get(tuple)...); - result->emplace(Resolved<void>()); + executor.Run(TupleArgMoveSemanticsHelper<Callback, std::tuple<CbArgs...>, + Indices>::Get(tuple)...); + result->EmplaceResolvedVoid(); } }; -// For use with base::Bind*. Cancels the promise if the callback was not run by -// the time the callback is deleted. -class BASE_EXPORT PromiseHolder { - public: - explicit PromiseHolder(scoped_refptr<internal::AbstractPromise> promise); - - ~PromiseHolder(); - - PromiseHolder(PromiseHolder&& other); - - scoped_refptr<internal::AbstractPromise> Unwrap() const; - - private: - mutable scoped_refptr<internal::AbstractPromise> promise_; -}; - -} // namespace internal - -template <> -struct BindUnwrapTraits<internal::PromiseHolder> { - static scoped_refptr<internal::AbstractPromise> Unwrap( - const internal::PromiseHolder& o) { - return o.Unwrap(); - } -}; - -namespace internal { - -// Used by ManualPromiseResolver<> to generate callbacks. +// Used by ManualPromiseResolver<> to generate callbacks. Note the use of +// WrappedPromise, this is necessary because we want to cancel the promise (to +// release memory) if the callback gets deleted without having being run. template <typename T, typename... Args> class PromiseCallbackHelper { public: @@ -624,7 +607,7 @@ class PromiseCallbackHelper { std::forward<Args>(args)...); promise->OnResolved(); }, - PromiseHolder(promise)); + promise); } static RepeatingCallback GetRepeatingResolveCallback( @@ -635,7 +618,7 @@ class PromiseCallbackHelper { std::forward<Args>(args)...); promise->OnResolved(); }, - PromiseHolder(promise)); + promise); } static Callback GetRejectCallback(scoped_refptr<AbstractPromise>& promise) { @@ -645,7 +628,7 @@ class PromiseCallbackHelper { std::forward<Args>(args)...); promise->OnRejected(); }, - PromiseHolder(promise)); + promise); } static RepeatingCallback GetRepeatingRejectCallback( @@ -656,7 +639,7 @@ class PromiseCallbackHelper { std::forward<Args>(args)...); promise->OnRejected(); }, - PromiseHolder(promise)); + promise); } }; @@ -679,8 +662,7 @@ struct IsValidPromiseArg<PromiseType&, CallbackArgType> { // rejection storage type. template <typename RejectT> struct AllPromiseRejectHelper { - static void Reject(AbstractPromise* result, - const scoped_refptr<AbstractPromise>& prerequisite) { + static void Reject(AbstractPromise* result, AbstractPromise* prerequisite) { result->emplace(scoped_refptr<AbstractPromise>(prerequisite)); } }; @@ -709,14 +691,20 @@ CallbackBase&& ToCallbackBase(const CallbackT&& task) { // Helps reduce template bloat by moving AbstractPromise construction out of // line. -scoped_refptr<AbstractPromise> BASE_EXPORT -ConstructAbstractPromiseWithSinglePrerequisite( +PassedPromise BASE_EXPORT ConstructAbstractPromiseWithSinglePrerequisite( const scoped_refptr<TaskRunner>& task_runner, const Location& from_here, AbstractPromise* prerequsite, - internal::PromiseExecutor::Data&& executor_data) noexcept; + PromiseExecutor::Data&& executor_data) noexcept; + +// Like ConstructAbstractPromiseWithSinglePrerequisite except tasks are posted +// onto SequencedTaskRunnerHandle::Get(). +PassedPromise BASE_EXPORT ConstructHereAbstractPromiseWithSinglePrerequisite( + const Location& from_here, + AbstractPromise* prerequsite, + PromiseExecutor::Data&& executor_data) noexcept; -scoped_refptr<AbstractPromise> BASE_EXPORT +PassedPromise BASE_EXPORT ConstructManualPromiseResolverPromise(const Location& from_here, RejectPolicy reject_policy, bool can_resolve, diff --git a/chromium/base/task/promise/helpers_unittest.cc b/chromium/base/task/promise/helpers_unittest.cc index afdc9e7079e..f13fb5c8c7a 100644 --- a/chromium/base/task/promise/helpers_unittest.cc +++ b/chromium/base/task/promise/helpers_unittest.cc @@ -187,8 +187,9 @@ TEST(EmplaceHelper, EmplacePromiseResult) { TEST(EmplaceHelper, EmplacePromise) { scoped_refptr<AbstractPromise> promise = DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); - scoped_refptr<AbstractPromise> curried = DoNothingPromiseBuilder(FROM_HERE); + PassedPromise curried = NoOpPromiseExecutor::Create( + FROM_HERE, false, false, RejectPolicy::kCatchNotRequired); EmplaceHelper<Resolved<int>, Rejected<NoReject>>::Emplace( promise.get(), Promise<int>(std::move(curried))); @@ -243,8 +244,8 @@ TEST(RunHelper, CallbackVoidArgumentIntResult) { scoped_refptr<AbstractPromise> result = DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); - RunHelper<OnceCallback<int()>, Resolved<void>, Resolved<int>, - Rejected<std::string>>::Run(BindOnce([]() { return 123; }), + RunHelper<RepeatingCallback<int()>, Resolved<void>, Resolved<int>, + Rejected<std::string>>::Run(BindRepeating([]() { return 123; }), arg.get(), result.get()); EXPECT_EQ(result->value().template Get<Resolved<int>>()->value, 123); @@ -255,8 +256,8 @@ TEST(RunHelper, CallbackVoidArgumentVoidResult) { scoped_refptr<AbstractPromise> result = DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); - RunHelper<OnceCallback<void()>, Resolved<void>, Resolved<void>, - Rejected<std::string>>::Run(BindOnce([]() {}), arg.get(), + RunHelper<RepeatingCallback<void()>, Resolved<void>, Resolved<void>, + Rejected<std::string>>::Run(BindRepeating([]() {}), arg.get(), result.get()); EXPECT_TRUE(result->value().ContainsResolved()); @@ -268,8 +269,8 @@ TEST(RunHelper, CallbackIntArgumentIntResult) { DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); arg->emplace(Resolved<int>(123)); - RunHelper<OnceCallback<int(int)>, Resolved<int>, Resolved<int>, - Rejected<std::string>>::Run(BindOnce([](int value) { + RunHelper<RepeatingCallback<int(int)>, Resolved<int>, Resolved<int>, + Rejected<std::string>>::Run(BindRepeating([](int value) { return value + 1; }), arg.get(), result.get()); @@ -284,7 +285,7 @@ TEST(RunHelper, CallbackIntArgumentArgumentVoidResult) { arg->emplace(Resolved<int>(123)); int value; - RunHelper<OnceCallback<void(int)>, Resolved<int>, Resolved<void>, + RunHelper<RepeatingCallback<void(int)>, Resolved<int>, Resolved<void>, Rejected<std::string>>::Run(BindLambdaForTesting([&](int arg) { value = arg; }), diff --git a/chromium/base/task/promise/no_op_promise_executor.cc b/chromium/base/task/promise/no_op_promise_executor.cc index 74a5bcc57c8..e168ed91007 100644 --- a/chromium/base/task/promise/no_op_promise_executor.cc +++ b/chromium/base/task/promise/no_op_promise_executor.cc @@ -45,15 +45,14 @@ bool NoOpPromiseExecutor::CanReject() const { void NoOpPromiseExecutor::Execute(AbstractPromise* promise) {} // static -scoped_refptr<internal::AbstractPromise> NoOpPromiseExecutor::Create( - Location from_here, - bool can_resolve, - bool can_reject, - RejectPolicy reject_policy) { - return AbstractPromise::CreateNoPrerequisitePromise( +PassedPromise NoOpPromiseExecutor::Create(Location from_here, + bool can_resolve, + bool can_reject, + RejectPolicy reject_policy) { + return PassedPromise(AbstractPromise::CreateNoPrerequisitePromise( from_here, reject_policy, DependentList::ConstructUnresolved(), PromiseExecutor::Data(in_place_type_t<NoOpPromiseExecutor>(), can_resolve, - can_reject)); + can_reject))); } } // namespace internal diff --git a/chromium/base/task/promise/no_op_promise_executor.h b/chromium/base/task/promise/no_op_promise_executor.h index 005ee8c0130..13f8ef0891b 100644 --- a/chromium/base/task/promise/no_op_promise_executor.h +++ b/chromium/base/task/promise/no_op_promise_executor.h @@ -21,10 +21,10 @@ class BASE_EXPORT NoOpPromiseExecutor { static constexpr PromiseExecutor::PrerequisitePolicy kPrerequisitePolicy = PromiseExecutor::PrerequisitePolicy::kNever; - static scoped_refptr<AbstractPromise> Create(Location from_here, - bool can_resolve, - bool can_reject, - RejectPolicy reject_policy); + static PassedPromise Create(Location from_here, + bool can_resolve, + bool can_reject, + RejectPolicy reject_policy); PromiseExecutor::PrerequisitePolicy GetPrerequisitePolicy() const; bool IsCancelled() const; diff --git a/chromium/base/task/promise/post_task_executor.h b/chromium/base/task/promise/post_task_executor.h index c018113de63..e3882ba1712 100644 --- a/chromium/base/task/promise/post_task_executor.h +++ b/chromium/base/task/promise/post_task_executor.h @@ -52,10 +52,14 @@ class PostTaskExecutor { static_assert(sizeof(CallbackBase) == sizeof(OnceCallback<ReturnType()>), "We assume it's possible to cast from CallbackBase to " "OnceCallback<ReturnType()>"); - OnceCallback<ReturnType()>* task = - static_cast<OnceCallback<ReturnType()>*>(&task_); - internal::RunHelper<OnceCallback<ReturnType()>, void, ResolveStorage, - RejectStorage>::Run(std::move(*task), nullptr, promise); + // Internally RunHelper uses const RepeatingCallback<>& to avoid the + // binary size overhead of moving a scoped_refptr<> about. We respect + // the onceness of the callback and RunHelper will overwrite the callback + // with the result. + RepeatingCallback<ReturnType()>* task = + static_cast<RepeatingCallback<ReturnType()>*>(&task_); + internal::RunHelper<RepeatingCallback<ReturnType()>, void, ResolveStorage, + RejectStorage>::Run(*task, nullptr, promise); } private: diff --git a/chromium/base/task/promise/post_task_executor_unittest.cc b/chromium/base/task/promise/post_task_executor_unittest.cc index 8e86aaefad4..1d3fc57e4d0 100644 --- a/chromium/base/task/promise/post_task_executor_unittest.cc +++ b/chromium/base/task/promise/post_task_executor_unittest.cc @@ -16,9 +16,8 @@ namespace internal { class PostTaskExecutorTest : public testing::Test { public: template <typename CallbackT> - scoped_refptr<internal::AbstractPromise> CreatePostTaskPromise( - const Location& from_here, - CallbackT&& task) { + WrappedPromise CreatePostTaskPromise(const Location& from_here, + CallbackT&& task) { // Extract properties from |task| callback. using CallbackTraits = CallbackTraits<std::decay_t<CallbackT>>; @@ -27,20 +26,20 @@ class PostTaskExecutorTest : public testing::Test { internal::PostTaskExecutor<typename CallbackTraits::ReturnType>>(), internal::ToCallbackBase(std::move(task))); - return AbstractPromise::CreateNoPrerequisitePromise( + return WrappedPromise(AbstractPromise::CreateNoPrerequisitePromise( from_here, RejectPolicy::kMustCatchRejection, internal::DependentList::ConstructUnresolved(), - std::move(executor_data)); + std::move(executor_data))); } }; TEST_F(PostTaskExecutorTest, OnceClosure) { bool run = false; - scoped_refptr<AbstractPromise> p = CreatePostTaskPromise( + WrappedPromise p = CreatePostTaskPromise( FROM_HERE, BindOnce([](bool* run) { *run = true; }, &run)); - p->Execute(); + p.Execute(); EXPECT_TRUE(run); } @@ -48,20 +47,19 @@ TEST_F(PostTaskExecutorTest, OnceClosure) { TEST_F(PostTaskExecutorTest, RepeatingClosure) { bool run = false; - scoped_refptr<AbstractPromise> p = CreatePostTaskPromise( + WrappedPromise p = CreatePostTaskPromise( FROM_HERE, BindRepeating([](bool* run) { *run = true; }, &run)); - p->Execute(); + p.Execute(); EXPECT_TRUE(run); } TEST_F(PostTaskExecutorTest, DoNothing) { // Check it compiles and the executor doesn't crash when run. - scoped_refptr<AbstractPromise> p = - CreatePostTaskPromise(FROM_HERE, DoNothing()); + WrappedPromise p = CreatePostTaskPromise(FROM_HERE, DoNothing()); - p->Execute(); + p.Execute(); } } // namespace internal diff --git a/chromium/base/task/promise/promise.h b/chromium/base/task/promise/promise.h index 4f2275ea183..467f74cb8e4 100644 --- a/chromium/base/task/promise/promise.h +++ b/chromium/base/task/promise/promise.h @@ -30,12 +30,12 @@ BASE_EXPORT scoped_refptr<TaskRunner> CreateTaskRunner( // callback will be posted immediately, otherwise it has to wait. // // Promise<> is copyable, moveable and thread safe. Under the hood -// internal::AbstractPromise is refcounted so retaining multiple Promises<> will +// AbstractPromise is refcounted so retaining multiple Promises<> will // prevent that part of the promise graph from being released. template <typename ResolveType, typename RejectType = NoReject> -class Promise { +class Promise : public internal::BasePromise { public: - Promise() : abstract_promise_(nullptr) {} + Promise() = default; static_assert( !std::is_reference<ResolveType>::value || @@ -49,16 +49,17 @@ class Promise { explicit Promise( scoped_refptr<internal::AbstractPromise> abstract_promise) noexcept - : abstract_promise_(std::move(abstract_promise)) {} + : BasePromise(std::move(abstract_promise)) {} - ~Promise() = default; + // Every PostTask calls this constructor so we need to be careful to avoid + // unnecessary binary bloat. + explicit Promise(internal::PassedPromise passed_promise) noexcept + : BasePromise(std::move(passed_promise), + BasePromise::InlineConstructor()) {} - operator bool() const { return !!abstract_promise_; } + ~Promise() = default; - bool IsCancelledForTesting() const { - DCHECK(abstract_promise_); - return abstract_promise_->IsCanceled(); - } + bool IsCancelledForTesting() const { return abstract_promise_->IsCanceled(); } // Waits until the promise has settled and if resolved it returns the resolved // value. @@ -75,8 +76,10 @@ class Promise { } DCHECK(abstract_promise_->IsResolved()) << "Can't take resolved value, promise wasn't resolved."; - return std::move( - abstract_promise_->TakeValue().value().Get<Resolved<T>>()->value); + return std::move(abstract_promise_->TakeValue() + .value() + .template Get<Resolved<T>>() + ->value); } // Waits until the promise has settled and if rejected it returns the rejected @@ -95,8 +98,10 @@ class Promise { abstract_promise_->IgnoreUncaughtCatchForTesting(); DCHECK(abstract_promise_->IsRejected()) << "Can't take rejected value, promise wasn't rejected."; - return std::move( - abstract_promise_->TakeValue().value().Get<Rejected<T>>()->value); + return std::move(abstract_promise_->TakeValue() + .value() + .template Get<Rejected<T>>() + ->value); } bool IsResolvedForTesting() const { @@ -122,22 +127,22 @@ class Promise { // // |task_runner| is const-ref to avoid bloat due the destructor (which posts a // task). - template <typename RejectCb> + template <typename CatchCb> auto CatchOn(const scoped_refptr<TaskRunner>& task_runner, const Location& from_here, - RejectCb on_reject) noexcept { + CatchCb on_reject) noexcept { DCHECK(!on_reject.is_null()); // Extract properties from the |on_reject| callback. - using RejectCallbackTraits = internal::CallbackTraits<RejectCb>; - using RejectCallbackArgT = typename RejectCallbackTraits::ArgType; + using CatchCallbackTraits = internal::CallbackTraits<CatchCb>; + using CatchCallbackArgT = typename CatchCallbackTraits::ArgType; // Compute the resolve and reject types of the returned Promise. using ReturnedPromiseTraits = internal::PromiseCombiner<ResolveType, NoReject, // We've caught the reject case. - typename RejectCallbackTraits::ResolveType, - typename RejectCallbackTraits::RejectType>; + typename CatchCallbackTraits::ResolveType, + typename CatchCallbackTraits::RejectType>; using ReturnedPromiseResolveT = typename ReturnedPromiseTraits::ResolveType; using ReturnedPromiseRejectT = typename ReturnedPromiseTraits::RejectType; @@ -148,13 +153,13 @@ class Promise { static_assert(ReturnedPromiseTraits::valid, "Ambiguous promise resolve type"); static_assert( - internal::IsValidPromiseArg<RejectType, RejectCallbackArgT>::value || - std::is_void<RejectCallbackArgT>::value, + internal::IsValidPromiseArg<RejectType, CatchCallbackArgT>::value || + std::is_void<CatchCallbackArgT>::value, "|on_reject| callback must accept Promise::RejectType or void."); static_assert( - !std::is_reference<RejectCallbackArgT>::value || - std::is_const<std::remove_reference_t<RejectCallbackArgT>>::value, + !std::is_reference<CatchCallbackArgT>::value || + std::is_const<std::remove_reference_t<CatchCallbackArgT>>::value, "Google C++ Style: References in function parameters must be const."); return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>( @@ -163,7 +168,7 @@ class Promise { internal::PromiseExecutor::Data( in_place_type_t<internal::ThenAndCatchExecutor< OnceClosure, // Never called. - OnceCallback<typename RejectCallbackTraits::SignatureType>, + OnceCallback<typename CatchCallbackTraits::SignatureType>, internal::NoCallback, RejectType, Resolved<ReturnedPromiseResolveT>, Rejected<ReturnedPromiseRejectT>>>(), @@ -171,18 +176,59 @@ class Promise { internal::ToCallbackBase(std::move(on_reject))))); } - template <typename RejectCb> + template <typename CatchCb> auto CatchOn(const TaskTraits& traits, const Location& from_here, - RejectCb&& on_reject) noexcept { + CatchCb&& on_reject) noexcept { return CatchOn(CreateTaskRunner(traits), from_here, - std::forward<RejectCb>(on_reject)); + std::forward<CatchCb>(on_reject)); } - template <typename RejectCb> - auto CatchHere(const Location& from_here, RejectCb&& on_reject) noexcept { - return CatchOn(internal::GetCurrentSequence(), from_here, - std::forward<RejectCb>(on_reject)); + template <typename CatchCb> + auto CatchHere(const Location& from_here, CatchCb&& on_reject) noexcept { + DCHECK(!on_reject.is_null()); + + // Extract properties from the |on_reject| callback. + using CatchCallbackTraits = internal::CallbackTraits<CatchCb>; + using CatchCallbackArgT = typename CatchCallbackTraits::ArgType; + + // Compute the resolve and reject types of the returned Promise. + using ReturnedPromiseTraits = + internal::PromiseCombiner<ResolveType, + NoReject, // We've caught the reject case. + typename CatchCallbackTraits::ResolveType, + typename CatchCallbackTraits::RejectType>; + using ReturnedPromiseResolveT = typename ReturnedPromiseTraits::ResolveType; + using ReturnedPromiseRejectT = typename ReturnedPromiseTraits::RejectType; + + static_assert(!std::is_same<NoReject, RejectType>::value, + "Can't catch a NoReject promise."); + + // Check we wouldn't need to return Promise<Variant<...>, ...> + static_assert(ReturnedPromiseTraits::valid, + "Ambiguous promise resolve type"); + static_assert( + internal::IsValidPromiseArg<RejectType, CatchCallbackArgT>::value || + std::is_void<CatchCallbackArgT>::value, + "|on_reject| callback must accept Promise::RejectType or void."); + + static_assert( + !std::is_reference<CatchCallbackArgT>::value || + std::is_const<std::remove_reference_t<CatchCallbackArgT>>::value, + "Google C++ Style: References in function parameters must be const."); + + return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>( + ConstructHereAbstractPromiseWithSinglePrerequisite( + from_here, abstract_promise_.get(), + internal::PromiseExecutor::Data( + in_place_type_t<internal::ThenAndCatchExecutor< + OnceClosure, // Never called. + OnceCallback<typename CatchCallbackTraits::SignatureType>, + internal::NoCallback, RejectType, + Resolved<ReturnedPromiseResolveT>, + Rejected<ReturnedPromiseRejectT>>>(), + OnceClosure(), + internal::ToCallbackBase(std::move(on_reject))))); } // A task to execute |on_resolve| is posted on |task_runner| as soon as this @@ -198,23 +244,22 @@ class Promise { // // |task_runner| is const-ref to avoid bloat due the destructor (which posts a // task). - template <typename ResolveCb> + template <typename ThenCb> auto ThenOn(const scoped_refptr<TaskRunner>& task_runner, const Location& from_here, - ResolveCb on_resolve) noexcept { + ThenCb on_resolve) noexcept { DCHECK(!on_resolve.is_null()); // Extract properties from the |on_resolve| callback. - using ResolveCallbackTraits = - internal::CallbackTraits<std::decay_t<ResolveCb>>; - using ResolveCallbackArgT = typename ResolveCallbackTraits::ArgType; + using ThenCallbackTraits = internal::CallbackTraits<std::decay_t<ThenCb>>; + using ThenCallbackArgT = typename ThenCallbackTraits::ArgType; // Compute the resolve and reject types of the returned Promise. using ReturnedPromiseTraits = internal::PromiseCombiner<NoResolve, // We've caught the resolve case. RejectType, - typename ResolveCallbackTraits::ResolveType, - typename ResolveCallbackTraits::RejectType>; + typename ThenCallbackTraits::ResolveType, + typename ThenCallbackTraits::RejectType>; using ReturnedPromiseResolveT = typename ReturnedPromiseTraits::ResolveType; using ReturnedPromiseRejectT = typename ReturnedPromiseTraits::RejectType; @@ -223,13 +268,13 @@ class Promise { "Ambiguous promise reject type"); static_assert( - internal::IsValidPromiseArg<ResolveType, ResolveCallbackArgT>::value || - std::is_void<ResolveCallbackArgT>::value, + internal::IsValidPromiseArg<ResolveType, ThenCallbackArgT>::value || + std::is_void<ThenCallbackArgT>::value, "|on_resolve| callback must accept Promise::ResolveType or void."); static_assert( - !std::is_reference<ResolveCallbackArgT>::value || - std::is_const<std::remove_reference_t<ResolveCallbackArgT>>::value, + !std::is_reference<ThenCallbackArgT>::value || + std::is_const<std::remove_reference_t<ThenCallbackArgT>>::value, "Google C++ Style: References in function parameters must be const."); return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>( @@ -237,7 +282,7 @@ class Promise { task_runner, from_here, abstract_promise_.get(), internal::PromiseExecutor::Data( in_place_type_t<internal::ThenAndCatchExecutor< - OnceCallback<typename ResolveCallbackTraits::SignatureType>, + OnceCallback<typename ThenCallbackTraits::SignatureType>, OnceClosure, ResolveType, internal::NoCallback, Resolved<ReturnedPromiseResolveT>, Rejected<ReturnedPromiseRejectT>>>(), @@ -245,18 +290,56 @@ class Promise { OnceClosure()))); } - template <typename ResolveCb> + template <typename ThenCb> auto ThenOn(const TaskTraits& traits, const Location& from_here, - ResolveCb&& on_resolve) noexcept { + ThenCb&& on_resolve) noexcept { return ThenOn(CreateTaskRunner(traits), from_here, - std::forward<ResolveCb>(on_resolve)); + std::forward<ThenCb>(on_resolve)); } - template <typename ResolveCb> - auto ThenHere(const Location& from_here, ResolveCb&& on_resolve) noexcept { - return ThenOn(internal::GetCurrentSequence(), from_here, - std::forward<ResolveCb>(on_resolve)); + template <typename ThenCb> + auto ThenHere(const Location& from_here, ThenCb&& on_resolve) noexcept { + DCHECK(!on_resolve.is_null()); + + // Extract properties from the |on_resolve| callback. + using ThenCallbackTraits = internal::CallbackTraits<std::decay_t<ThenCb>>; + using ThenCallbackArgT = typename ThenCallbackTraits::ArgType; + + // Compute the resolve and reject types of the returned Promise. + using ReturnedPromiseTraits = + internal::PromiseCombiner<NoResolve, // We've caught the resolve case. + RejectType, + typename ThenCallbackTraits::ResolveType, + typename ThenCallbackTraits::RejectType>; + using ReturnedPromiseResolveT = typename ReturnedPromiseTraits::ResolveType; + using ReturnedPromiseRejectT = typename ReturnedPromiseTraits::RejectType; + + // Check we wouldn't need to return Promise<..., Variant<...>> + static_assert(ReturnedPromiseTraits::valid, + "Ambiguous promise reject type"); + + static_assert( + internal::IsValidPromiseArg<ResolveType, ThenCallbackArgT>::value || + std::is_void<ThenCallbackArgT>::value, + "|on_resolve| callback must accept Promise::ResolveType or void."); + + static_assert( + !std::is_reference<ThenCallbackArgT>::value || + std::is_const<std::remove_reference_t<ThenCallbackArgT>>::value, + "Google C++ Style: References in function parameters must be const."); + + return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>( + ConstructHereAbstractPromiseWithSinglePrerequisite( + from_here, abstract_promise_.get(), + internal::PromiseExecutor::Data( + in_place_type_t<internal::ThenAndCatchExecutor< + OnceCallback<typename ThenCallbackTraits::SignatureType>, + OnceClosure, ResolveType, internal::NoCallback, + Resolved<ReturnedPromiseResolveT>, + Rejected<ReturnedPromiseRejectT>>>(), + internal::ToCallbackBase(std::move(on_resolve)), + OnceClosure()))); } // A task to execute |on_reject| is posted on |task_runner| as soon as this @@ -279,26 +362,26 @@ class Promise { // // |task_runner| is const-ref to avoid bloat due the destructor (which posts a // task). - template <typename ResolveCb, typename RejectCb> + template <typename ThenCb, typename CatchCb> auto ThenOn(const scoped_refptr<TaskRunner>& task_runner, const Location& from_here, - ResolveCb on_resolve, - RejectCb on_reject) noexcept { + ThenCb on_resolve, + CatchCb on_reject) noexcept { DCHECK(!on_resolve.is_null()); DCHECK(!on_reject.is_null()); // Extract properties from the |on_resolve| and |on_reject| callbacks. - using ResolveCallbackTraits = internal::CallbackTraits<ResolveCb>; - using RejectCallbackTraits = internal::CallbackTraits<RejectCb>; - using ResolveCallbackArgT = typename ResolveCallbackTraits::ArgType; - using RejectCallbackArgT = typename RejectCallbackTraits::ArgType; + using ThenCallbackTraits = internal::CallbackTraits<ThenCb>; + using CatchCallbackTraits = internal::CallbackTraits<CatchCb>; + using ThenCallbackArgT = typename ThenCallbackTraits::ArgType; + using CatchCallbackArgT = typename CatchCallbackTraits::ArgType; // Compute the resolve and reject types of the returned Promise. using ReturnedPromiseTraits = - internal::PromiseCombiner<typename ResolveCallbackTraits::ResolveType, - typename ResolveCallbackTraits::RejectType, - typename RejectCallbackTraits::ResolveType, - typename RejectCallbackTraits::RejectType>; + internal::PromiseCombiner<typename ThenCallbackTraits::ResolveType, + typename ThenCallbackTraits::RejectType, + typename CatchCallbackTraits::ResolveType, + typename CatchCallbackTraits::RejectType>; using ReturnedPromiseResolveT = typename ReturnedPromiseTraits::ResolveType; using ReturnedPromiseRejectT = typename ReturnedPromiseTraits::RejectType; @@ -310,23 +393,23 @@ class Promise { "compatible types."); static_assert( - internal::IsValidPromiseArg<ResolveType, ResolveCallbackArgT>::value || - std::is_void<ResolveCallbackArgT>::value, + internal::IsValidPromiseArg<ResolveType, ThenCallbackArgT>::value || + std::is_void<ThenCallbackArgT>::value, "|on_resolve| callback must accept Promise::ResolveType or void."); static_assert( - internal::IsValidPromiseArg<RejectType, RejectCallbackArgT>::value || - std::is_void<RejectCallbackArgT>::value, + internal::IsValidPromiseArg<RejectType, CatchCallbackArgT>::value || + std::is_void<CatchCallbackArgT>::value, "|on_reject| callback must accept Promise::RejectType or void."); static_assert( - !std::is_reference<ResolveCallbackArgT>::value || - std::is_const<std::remove_reference_t<ResolveCallbackArgT>>::value, + !std::is_reference<ThenCallbackArgT>::value || + std::is_const<std::remove_reference_t<ThenCallbackArgT>>::value, "Google C++ Style: References in function parameters must be const."); static_assert( - !std::is_reference<RejectCallbackArgT>::value || - std::is_const<std::remove_reference_t<RejectCallbackArgT>>::value, + !std::is_reference<CatchCallbackArgT>::value || + std::is_const<std::remove_reference_t<CatchCallbackArgT>>::value, "Google C++ Style: References in function parameters must be const."); return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>( @@ -334,31 +417,84 @@ class Promise { task_runner, from_here, abstract_promise_.get(), internal::PromiseExecutor::Data( in_place_type_t<internal::ThenAndCatchExecutor< - OnceCallback<typename ResolveCallbackTraits::SignatureType>, - OnceCallback<typename RejectCallbackTraits::SignatureType>, + OnceCallback<typename ThenCallbackTraits::SignatureType>, + OnceCallback<typename CatchCallbackTraits::SignatureType>, ResolveType, RejectType, Resolved<ReturnedPromiseResolveT>, Rejected<ReturnedPromiseRejectT>>>(), internal::ToCallbackBase(std::move(on_resolve)), internal::ToCallbackBase(std::move(on_reject))))); } - template <typename ResolveCb, typename RejectCb> + template <typename ThenCb, typename CatchCb> auto ThenOn(const TaskTraits& traits, const Location& from_here, - ResolveCb on_resolve, - RejectCb on_reject) noexcept { + ThenCb on_resolve, + CatchCb on_reject) noexcept { return ThenOn(CreateTaskRunner(traits), from_here, - std::forward<ResolveCb>(on_resolve), - std::forward<RejectCb>(on_reject)); + std::forward<ThenCb>(on_resolve), + std::forward<CatchCb>(on_reject)); } - template <typename ResolveCb, typename RejectCb> + template <typename ThenCb, typename CatchCb> auto ThenHere(const Location& from_here, - ResolveCb on_resolve, - RejectCb on_reject) noexcept { - return ThenOn(internal::GetCurrentSequence(), from_here, - std::forward<ResolveCb>(on_resolve), - std::forward<RejectCb>(on_reject)); + ThenCb on_resolve, + CatchCb on_reject) noexcept { + DCHECK(!on_resolve.is_null()); + DCHECK(!on_reject.is_null()); + + // Extract properties from the |on_resolve| and |on_reject| callbacks. + using ThenCallbackTraits = internal::CallbackTraits<ThenCb>; + using CatchCallbackTraits = internal::CallbackTraits<CatchCb>; + using ThenCallbackArgT = typename ThenCallbackTraits::ArgType; + using CatchCallbackArgT = typename CatchCallbackTraits::ArgType; + + // Compute the resolve and reject types of the returned Promise. + using ReturnedPromiseTraits = + internal::PromiseCombiner<typename ThenCallbackTraits::ResolveType, + typename ThenCallbackTraits::RejectType, + typename CatchCallbackTraits::ResolveType, + typename CatchCallbackTraits::RejectType>; + using ReturnedPromiseResolveT = typename ReturnedPromiseTraits::ResolveType; + using ReturnedPromiseRejectT = typename ReturnedPromiseTraits::RejectType; + + static_assert(!std::is_same<NoReject, RejectType>::value, + "Can't catch a NoReject promise."); + + static_assert(ReturnedPromiseTraits::valid, + "|on_resolve| callback and |on_resolve| callback must return " + "compatible types."); + + static_assert( + internal::IsValidPromiseArg<ResolveType, ThenCallbackArgT>::value || + std::is_void<ThenCallbackArgT>::value, + "|on_resolve| callback must accept Promise::ResolveType or void."); + + static_assert( + internal::IsValidPromiseArg<RejectType, CatchCallbackArgT>::value || + std::is_void<CatchCallbackArgT>::value, + "|on_reject| callback must accept Promise::RejectType or void."); + + static_assert( + !std::is_reference<ThenCallbackArgT>::value || + std::is_const<std::remove_reference_t<ThenCallbackArgT>>::value, + "Google C++ Style: References in function parameters must be const."); + + static_assert( + !std::is_reference<CatchCallbackArgT>::value || + std::is_const<std::remove_reference_t<CatchCallbackArgT>>::value, + "Google C++ Style: References in function parameters must be const."); + + return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>( + ConstructHereAbstractPromiseWithSinglePrerequisite( + from_here, abstract_promise_.get(), + internal::PromiseExecutor::Data( + in_place_type_t<internal::ThenAndCatchExecutor< + OnceCallback<typename ThenCallbackTraits::SignatureType>, + OnceCallback<typename CatchCallbackTraits::SignatureType>, + ResolveType, RejectType, Resolved<ReturnedPromiseResolveT>, + Rejected<ReturnedPromiseRejectT>>>(), + internal::ToCallbackBase(std::move(on_resolve)), + internal::ToCallbackBase(std::move(on_reject))))); } // A task to execute |finally_callback| on |task_runner| is posted after the @@ -405,8 +541,24 @@ class Promise { template <typename FinallyCb> auto FinallyHere(const Location& from_here, FinallyCb finally_callback) noexcept { - return FinallyOn(internal::GetCurrentSequence(), from_here, - std::move(finally_callback)); + // Extract properties from |finally_callback| callback. + using CallbackTraits = internal::CallbackTraits<FinallyCb>; + using ReturnedPromiseResolveT = typename CallbackTraits::ResolveType; + using ReturnedPromiseRejectT = typename CallbackTraits::RejectType; + + using CallbackArgT = typename CallbackTraits::ArgType; + static_assert(std::is_void<CallbackArgT>::value, + "|finally_callback| callback must have no arguments"); + + return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>( + ConstructHereAbstractPromiseWithSinglePrerequisite( + from_here, abstract_promise_.get(), + internal::PromiseExecutor::Data( + in_place_type_t<internal::FinallyExecutor< + OnceCallback<typename CallbackTraits::ReturnType()>, + Resolved<ReturnedPromiseResolveT>, + Rejected<ReturnedPromiseRejectT>>>(), + internal::ToCallbackBase(std::move(finally_callback))))); } template <typename... Args> @@ -442,8 +594,6 @@ class Promise { nullptr, from_here, nullptr, RejectPolicy::kMustCatchRejection, internal::DependentList::ConstructResolved(), std::move(executor_data))); - promise->emplace(in_place_type_t<Rejected<RejectType>>(), - std::forward<Args>(args)...); return Promise<ResolveType, RejectType>(std::move(promise)); } @@ -454,6 +604,11 @@ class Promise { abstract_promise_->IgnoreUncaughtCatchForTesting(); } + const scoped_refptr<internal::AbstractPromise>& GetScopedRefptrForTesting() + const { + return abstract_promise_; + } + private: template <typename A, typename B> friend class Promise; @@ -471,8 +626,6 @@ class Promise { template <typename A, typename B> friend class ManualPromiseResolver; - - scoped_refptr<internal::AbstractPromise> abstract_promise_; }; // Used for manually resolving and rejecting a Promise. This is for @@ -624,8 +777,8 @@ class Promises { std::vector<internal::DependentList::Node> prerequisite_list( sizeof...(promises)); int i = 0; - for (auto&& p : {promises.abstract_promise_...}) { - prerequisite_list[i++].SetPrerequisite(p.get()); + for (auto&& p : {promises.abstract_promise_.get()...}) { + prerequisite_list[i++].SetPrerequisite(p); } internal::PromiseExecutor::Data executor_data( diff --git a/chromium/base/task/promise/promise_unittest.cc b/chromium/base/task/promise/promise_unittest.cc index bb3dda41cb3..d81adacb1a0 100644 --- a/chromium/base/task/promise/promise_unittest.cc +++ b/chromium/base/task/promise/promise_unittest.cc @@ -106,6 +106,86 @@ TEST(PromiseMemoryLeakTest, TargetTaskRunnerClearsTasks) { EXPECT_TRUE(delete_reply_flag); } +TEST(PromiseMemoryLeakTest, GetResolveCallbackNeverRun) { + test::TaskEnvironment task_environment_; + OnceCallback<void(int)> cb; + MockObject mock_object; + bool delete_task_flag = false; + + { + ManualPromiseResolver<int> p(FROM_HERE); + cb = p.GetResolveCallback(); + + p.promise().ThenHere( + FROM_HERE, BindOnce(&MockObject::Task, Unretained(&mock_object), + MakeRefCounted<ObjectToDelete>(&delete_task_flag))); + } + + EXPECT_FALSE(delete_task_flag); + cb = OnceCallback<void(int)>(); + EXPECT_TRUE(delete_task_flag); +} + +TEST(PromiseMemoryLeakTest, GetRepeatingResolveCallbackNeverRun) { + test::TaskEnvironment task_environment_; + RepeatingCallback<void(int)> cb; + MockObject mock_object; + bool delete_task_flag = false; + + { + ManualPromiseResolver<int> p(FROM_HERE); + cb = p.GetRepeatingResolveCallback(); + + p.promise().ThenHere( + FROM_HERE, BindOnce(&MockObject::Task, Unretained(&mock_object), + MakeRefCounted<ObjectToDelete>(&delete_task_flag))); + } + + EXPECT_FALSE(delete_task_flag); + cb = RepeatingCallback<void(int)>(); + EXPECT_TRUE(delete_task_flag); +} + +TEST(PromiseMemoryLeakTest, GetRejectCallbackNeverRun) { + test::TaskEnvironment task_environment_; + OnceCallback<void(int)> cb; + MockObject mock_object; + bool delete_task_flag = false; + + { + ManualPromiseResolver<void, int> p(FROM_HERE); + cb = p.GetRejectCallback(); + + p.promise().CatchHere( + FROM_HERE, BindOnce(&MockObject::Task, Unretained(&mock_object), + MakeRefCounted<ObjectToDelete>(&delete_task_flag))); + } + + EXPECT_FALSE(delete_task_flag); + cb = OnceCallback<void(int)>(); + EXPECT_TRUE(delete_task_flag); +} + +TEST(PromiseMemoryLeakTest, GetRepeatingRejectCallbackNeverRun) { + test::TaskEnvironment task_environment_; + RepeatingCallback<void(int)> cb; + MockObject mock_object; + bool delete_task_flag = false; + + { + ManualPromiseResolver<void, int> p(FROM_HERE); + cb = p.GetRepeatingRejectCallback(); + + p.promise().CatchHere( + FROM_HERE, BindOnce(&MockObject::Task, Unretained(&mock_object), + MakeRefCounted<ObjectToDelete>(&delete_task_flag))); + } + + EXPECT_FALSE(delete_task_flag); + cb = RepeatingCallback<void(int)>(); + EXPECT_TRUE(delete_task_flag); +} + TEST_F(PromiseTest, GetResolveCallbackThen) { ManualPromiseResolver<int> p(FROM_HERE); p.GetResolveCallback().Run(123); @@ -1602,13 +1682,17 @@ TEST_F(PromiseTest, MoveOnlyTypeMultipleCatchesNotAllowed) { auto p = Promise<void, std::unique_ptr<int>>::CreateRejected( FROM_HERE, std::make_unique<int>(123)); - p.CatchHere(FROM_HERE, - BindOnce([](std::unique_ptr<int> i) { EXPECT_EQ(123, *i); })); + auto r = p.CatchHere( + FROM_HERE, BindOnce([](std::unique_ptr<int> i) { EXPECT_EQ(123, *i); })); EXPECT_DCHECK_DEATH({ p.CatchHere(FROM_HERE, BindOnce([](std::unique_ptr<int> i) { EXPECT_EQ(123, *i); })); }); + + // TODO(alexclarke): Temporary, remove when SequenceManager handles promises + // natively. + r.GetScopedRefptrForTesting()->OnCanceled(); #endif } diff --git a/chromium/base/task/promise/promise_value.h b/chromium/base/task/promise/promise_value.h index 32ec758716d..3b1cfc43464 100644 --- a/chromium/base/task/promise/promise_value.h +++ b/chromium/base/task/promise/promise_value.h @@ -40,9 +40,6 @@ struct Resolved { "Can't have Resolved<NoResolve>"); } - Resolved(const Resolved& other) = default; - Resolved(Resolved&& other) = default; - // Conversion constructor accepts any arguments except Resolved<T>. template < typename... Args, @@ -75,9 +72,6 @@ struct Rejected { "Can't have Rejected<NoReject>"); } - Rejected(const Rejected& other) = default; - Rejected(Rejected&& other) = default; - // Conversion constructor accepts any arguments except Rejected<T>. template < typename... Args, diff --git a/chromium/base/task/promise/then_and_catch_executor.cc b/chromium/base/task/promise/then_and_catch_executor.cc index 5f7f8f09ad0..5eb38e99b78 100644 --- a/chromium/base/task/promise/then_and_catch_executor.cc +++ b/chromium/base/task/promise/then_and_catch_executor.cc @@ -8,14 +8,14 @@ namespace base { namespace internal { bool ThenAndCatchExecutorCommon::IsCancelled() const { - if (!resolve_callback_.is_null()) { + if (!then_callback_.is_null()) { // If there is both a resolve and a reject executor they must be canceled // at the same time. - DCHECK(reject_callback_.is_null() || - reject_callback_.IsCancelled() == resolve_callback_.IsCancelled()); - return resolve_callback_.IsCancelled(); + DCHECK(catch_callback_.is_null() || + catch_callback_.IsCancelled() == then_callback_.IsCancelled()); + return then_callback_.IsCancelled(); } - return reject_callback_.IsCancelled(); + return catch_callback_.IsCancelled(); } void ThenAndCatchExecutorCommon::Execute(AbstractPromise* promise, @@ -23,16 +23,16 @@ void ThenAndCatchExecutorCommon::Execute(AbstractPromise* promise, ExecuteCallback execute_catch) { AbstractPromise* prerequisite = promise->GetOnlyPrerequisite(); if (prerequisite->IsResolved()) { - if (ProcessNullCallback(resolve_callback_, prerequisite, promise)) + if (ProcessNullCallback(then_callback_, prerequisite, promise)) return; - execute_then(prerequisite, promise, &resolve_callback_); + execute_then(prerequisite, promise, &then_callback_); } else { DCHECK(prerequisite->IsRejected()); - if (ProcessNullCallback(reject_callback_, prerequisite, promise)) + if (ProcessNullCallback(catch_callback_, prerequisite, promise)) return; - execute_catch(prerequisite, promise, &reject_callback_); + execute_catch(prerequisite, promise, &catch_callback_); } } diff --git a/chromium/base/task/promise/then_and_catch_executor.h b/chromium/base/task/promise/then_and_catch_executor.h index 56d30ba2a11..7668fc1fbcb 100644 --- a/chromium/base/task/promise/then_and_catch_executor.h +++ b/chromium/base/task/promise/then_and_catch_executor.h @@ -17,11 +17,11 @@ namespace internal { // Exists to reduce template bloat. class BASE_EXPORT ThenAndCatchExecutorCommon { public: - ThenAndCatchExecutorCommon(internal::CallbackBase&& resolve_executor, - internal::CallbackBase&& reject_executor) noexcept - : resolve_callback_(std::move(resolve_executor)), - reject_callback_(std::move(reject_executor)) { - DCHECK(!resolve_callback_.is_null() || !reject_callback_.is_null()); + ThenAndCatchExecutorCommon(internal::CallbackBase&& then_callback, + internal::CallbackBase&& catch_callback) noexcept + : then_callback_(std::move(then_callback)), + catch_callback_(std::move(catch_callback)) { + DCHECK(!then_callback_.is_null() || !catch_callback_.is_null()); } ~ThenAndCatchExecutorCommon() = default; @@ -44,24 +44,23 @@ class BASE_EXPORT ThenAndCatchExecutorCommon { AbstractPromise* arg, AbstractPromise* result); - CallbackBase resolve_callback_; - CallbackBase reject_callback_; + CallbackBase then_callback_; + CallbackBase catch_callback_; }; // Tag signals no callback which is used to eliminate dead code. struct NoCallback {}; -template <typename ResolveOnceCallback, - typename RejectOnceCallback, +template <typename ThenOnceCallback, + typename CatchOnceCallback, typename ArgResolve, typename ArgReject, typename ResolveStorage, typename RejectStorage> class ThenAndCatchExecutor { public: - using ResolveReturnT = - typename CallbackTraits<ResolveOnceCallback>::ReturnType; - using RejectReturnT = typename CallbackTraits<RejectOnceCallback>::ReturnType; + using ThenReturnT = typename CallbackTraits<ThenOnceCallback>::ReturnType; + using CatchReturnT = typename CallbackTraits<CatchOnceCallback>::ReturnType; using PrerequisiteCouldResolve = std::integral_constant<bool, !std::is_same<ArgResolve, NoCallback>::value>; @@ -69,8 +68,8 @@ class ThenAndCatchExecutor { std::integral_constant<bool, !std::is_same<ArgReject, NoCallback>::value>; ThenAndCatchExecutor(CallbackBase&& resolve_callback, - CallbackBase&& reject_callback) noexcept - : common_(std::move(resolve_callback), std::move(reject_callback)) {} + CallbackBase&& catch_callback) noexcept + : common_(std::move(resolve_callback), std::move(catch_callback)) {} bool IsCancelled() const { return common_.IsCancelled(); } @@ -85,29 +84,29 @@ class ThenAndCatchExecutor { #if DCHECK_IS_ON() PromiseExecutor::ArgumentPassingType ResolveArgumentPassingType() const { - return common_.resolve_callback_.is_null() + return common_.then_callback_.is_null() ? PromiseExecutor::ArgumentPassingType::kNoCallback - : CallbackTraits<ResolveOnceCallback>::argument_passing_type; + : CallbackTraits<ThenOnceCallback>::argument_passing_type; } PromiseExecutor::ArgumentPassingType RejectArgumentPassingType() const { - return common_.reject_callback_.is_null() + return common_.catch_callback_.is_null() ? PromiseExecutor::ArgumentPassingType::kNoCallback - : CallbackTraits<RejectOnceCallback>::argument_passing_type; + : CallbackTraits<CatchOnceCallback>::argument_passing_type; } bool CanResolve() const { - return (!common_.resolve_callback_.is_null() && - PromiseCallbackTraits<ResolveReturnT>::could_resolve) || - (!common_.reject_callback_.is_null() && - PromiseCallbackTraits<RejectReturnT>::could_resolve); + return (!common_.then_callback_.is_null() && + PromiseCallbackTraits<ThenReturnT>::could_resolve) || + (!common_.catch_callback_.is_null() && + PromiseCallbackTraits<CatchReturnT>::could_resolve); } bool CanReject() const { - return (!common_.resolve_callback_.is_null() && - PromiseCallbackTraits<ResolveReturnT>::could_reject) || - (!common_.reject_callback_.is_null() && - PromiseCallbackTraits<RejectReturnT>::could_reject); + return (!common_.then_callback_.is_null() && + PromiseCallbackTraits<ThenReturnT>::could_reject) || + (!common_.catch_callback_.is_null() && + PromiseCallbackTraits<CatchReturnT>::could_reject); } #endif @@ -121,8 +120,8 @@ class ThenAndCatchExecutor { static void ExecuteCatch(AbstractPromise* prerequisite, AbstractPromise* promise, - CallbackBase* reject_callback) { - ExecuteCatchInternal(prerequisite, promise, reject_callback, + CallbackBase* catch_callback) { + ExecuteCatchInternal(prerequisite, promise, catch_callback, PrerequisiteCouldReject()); } @@ -130,10 +129,16 @@ class ThenAndCatchExecutor { AbstractPromise* promise, CallbackBase* resolve_callback, std::true_type can_resolve) { - RunHelper<ResolveOnceCallback, Resolved<ArgResolve>, ResolveStorage, - RejectStorage>:: - Run(std::move(*static_cast<ResolveOnceCallback*>(resolve_callback)), - prerequisite, promise); + // Internally RunHelper uses const RepeatingCallback<>& to avoid the + // binary size overhead of moving a scoped_refptr<> about. We respect + // the onceness of the callback and RunHelper will overwrite the callback + // with the result. + using RepeatingThenCB = + typename ToRepeatingCallback<ThenOnceCallback>::value; + RunHelper< + RepeatingThenCB, Resolved<ArgResolve>, ResolveStorage, + RejectStorage>::Run(*static_cast<RepeatingThenCB*>(resolve_callback), + prerequisite, promise); } static void ExecuteThenInternal(AbstractPromise* prerequisite, @@ -145,17 +150,23 @@ class ThenAndCatchExecutor { static void ExecuteCatchInternal(AbstractPromise* prerequisite, AbstractPromise* promise, - CallbackBase* reject_callback, + CallbackBase* catch_callback, std::true_type can_reject) { - RunHelper<RejectOnceCallback, Rejected<ArgReject>, ResolveStorage, - RejectStorage>:: - Run(std::move(*static_cast<RejectOnceCallback*>(reject_callback)), - prerequisite, promise); + // Internally RunHelper uses const RepeatingCallback<>& to avoid the + // binary size overhead of moving a scoped_refptr<> about. We respect + // the onceness of the callback and RunHelper will overwrite the callback + // with the result. + using RepeatingCatchCB = + typename ToRepeatingCallback<CatchOnceCallback>::value; + RunHelper< + RepeatingCatchCB, Rejected<ArgReject>, ResolveStorage, + RejectStorage>::Run(*static_cast<RepeatingCatchCB*>(catch_callback), + prerequisite, promise); } static void ExecuteCatchInternal(AbstractPromise* prerequisite, AbstractPromise* promise, - CallbackBase* reject_callback, + CallbackBase* catch_callback, std::false_type can_reject) { // |prerequisite| can't reject so don't generate dead code. } diff --git a/chromium/base/task/sequence_manager/sequence_manager.h b/chromium/base/task/sequence_manager/sequence_manager.h index cd2163d7793..6e1cb0b752b 100644 --- a/chromium/base/task/sequence_manager/sequence_manager.h +++ b/chromium/base/task/sequence_manager/sequence_manager.h @@ -11,6 +11,7 @@ #include "base/macros.h" #include "base/message_loop/message_pump_type.h" #include "base/message_loop/timer_slack.h" +#include "base/sequenced_task_runner.h" #include "base/single_thread_task_runner.h" #include "base/task/sequence_manager/task_queue_impl.h" #include "base/task/sequence_manager/task_time_observer.h" @@ -19,6 +20,7 @@ namespace base { class MessagePump; +class TaskObserver; namespace sequence_manager { @@ -102,6 +104,11 @@ class BASE_EXPORT SequenceManager { kNone, kEnabled, kEnabledWithBacktrace, + + // Logs high priority tasks and the lower priority tasks they skipped + // past. Useful for debugging test failures caused by scheduler policy + // changes. + kReorderedOnly, }; TaskLogging task_execution_logging = TaskLogging::kNone; @@ -141,6 +148,10 @@ class BASE_EXPORT SequenceManager { // performs this initialization automatically. virtual void BindToCurrentThread() = 0; + // Returns the task runner the current task was posted on. Returns null if no + // task is currently running. Must be called on the bound thread. + virtual scoped_refptr<SequencedTaskRunner> GetTaskRunnerForCurrentTask() = 0; + // Finishes the initialization for a SequenceManager created via // CreateUnboundSequenceManagerWithPump(). Must not be called in any other // circumstances. The ownership of the pump is transferred to SequenceManager. @@ -238,6 +249,14 @@ class BASE_EXPORT SequenceManager { virtual std::unique_ptr<NativeWorkHandle> OnNativeWorkPending( TaskQueue::QueuePriority priority) = 0; + // Adds an observer which reports task execution. Can only be called on the + // same thread that |this| is running on. + virtual void AddTaskObserver(TaskObserver* task_observer) = 0; + + // Removes an observer which reports task execution. Can only be called on the + // same thread that |this| is running on. + virtual void RemoveTaskObserver(TaskObserver* task_observer) = 0; + protected: virtual std::unique_ptr<internal::TaskQueueImpl> CreateTaskQueueImpl( const TaskQueue::Spec& spec) = 0; diff --git a/chromium/base/task/sequence_manager/sequence_manager_impl.cc b/chromium/base/task/sequence_manager/sequence_manager_impl.cc index 26977d6687b..7eea742aeb2 100644 --- a/chromium/base/task/sequence_manager/sequence_manager_impl.cc +++ b/chromium/base/task/sequence_manager/sequence_manager_impl.cc @@ -320,6 +320,16 @@ void SequenceManagerImpl::BindToCurrentThread( BindToMessagePump(std::move(pump)); } +scoped_refptr<SequencedTaskRunner> +SequenceManagerImpl::GetTaskRunnerForCurrentTask() { + DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); + if (main_thread_only().task_execution_stack.empty()) + return nullptr; + return main_thread_only() + .task_execution_stack.back() + .pending_task.task_runner; +} + void SequenceManagerImpl::CompleteInitializationOnBoundThread() { controller_->AddNestingObserver(this); main_thread_only().nesting_observer_registered_ = true; @@ -488,10 +498,10 @@ const char* RunTaskTraceNameForPriority(TaskQueue::QueuePriority priority) { } // namespace -Optional<Task> SequenceManagerImpl::TakeTask() { - Optional<Task> task = TakeTaskImpl(); +Task* SequenceManagerImpl::SelectNextTask() { + Task* task = SelectNextTaskImpl(); if (!task) - return base::nullopt; + return nullptr; ExecutingTask& executing_task = *main_thread_only().task_execution_stack.rbegin(); @@ -503,62 +513,70 @@ Optional<Task> SequenceManagerImpl::TakeTask() { "task_type", executing_task.task_type); TRACE_EVENT_BEGIN0("sequence_manager", executing_task.task_queue_name); -#if DCHECK_IS_ON() && !defined(OS_NACL) - LogTaskDebugInfo(executing_task); -#endif - return task; } #if DCHECK_IS_ON() && !defined(OS_NACL) void SequenceManagerImpl::LogTaskDebugInfo( - const ExecutingTask& executing_task) { + const WorkQueue* selected_work_queue) const { + const Task* task = selected_work_queue->GetFrontTask(); switch (settings_.task_execution_logging) { case Settings::TaskLogging::kNone: break; case Settings::TaskLogging::kEnabled: - LOG(INFO) << "#" - << static_cast<uint64_t>( - executing_task.pending_task.enqueue_order()) - << " " << executing_task.task_queue_name - << (executing_task.pending_task.cross_thread_ - ? " Run crossthread " - : " Run ") - << executing_task.pending_task.posted_from.ToString(); + LOG(INFO) << "#" << static_cast<uint64_t>(task->enqueue_order()) << " " + << selected_work_queue->task_queue()->GetName() + << (task->cross_thread_ ? " Run crossthread " : " Run ") + << task->posted_from.ToString(); break; case Settings::TaskLogging::kEnabledWithBacktrace: { std::array<const void*, PendingTask::kTaskBacktraceLength + 1> task_trace; - task_trace[0] = executing_task.pending_task.posted_from.program_counter(); - std::copy(executing_task.pending_task.task_backtrace.begin(), - executing_task.pending_task.task_backtrace.end(), + task_trace[0] = task->posted_from.program_counter(); + std::copy(task->task_backtrace.begin(), task->task_backtrace.end(), task_trace.begin() + 1); size_t length = 0; while (length < task_trace.size() && task_trace[length]) ++length; if (length == 0) break; - LOG(INFO) << "#" - << static_cast<uint64_t>( - executing_task.pending_task.enqueue_order()) - << " " << executing_task.task_queue_name - << (executing_task.pending_task.cross_thread_ - ? " Run crossthread " - : " Run ") + LOG(INFO) << "#" << static_cast<uint64_t>(task->enqueue_order()) << " " + << selected_work_queue->task_queue()->GetName() + << (task->cross_thread_ ? " Run crossthread " : " Run ") << debug::StackTrace(task_trace.data(), length); break; } + + case Settings::TaskLogging::kReorderedOnly: { + std::vector<const Task*> skipped_tasks; + main_thread_only().selector.CollectSkippedOverLowerPriorityTasks( + selected_work_queue, &skipped_tasks); + + if (skipped_tasks.empty()) + break; + + LOG(INFO) << "#" << static_cast<uint64_t>(task->enqueue_order()) << " " + << selected_work_queue->task_queue()->GetName() + << (task->cross_thread_ ? " Run crossthread " : " Run ") + << task->posted_from.ToString(); + + for (const Task* skipped_task : skipped_tasks) { + LOG(INFO) << "# (skipped over) " + << static_cast<uint64_t>(skipped_task->enqueue_order()) << " " + << skipped_task->posted_from.ToString(); + } + } } } #endif // DCHECK_IS_ON() && !defined(OS_NACL) -Optional<Task> SequenceManagerImpl::TakeTaskImpl() { +Task* SequenceManagerImpl::SelectNextTaskImpl() { CHECK(Validate()); DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("sequence_manager"), - "SequenceManagerImpl::TakeTask"); + "SequenceManagerImpl::SelectNextTask"); ReloadEmptyWorkQueues(); LazyNow lazy_now(controller_->GetClock()); @@ -579,7 +597,7 @@ Optional<Task> SequenceManagerImpl::TakeTaskImpl() { this, AsValueWithSelectorResult(work_queue, /* force_verbose */ false)); if (!work_queue) - return nullopt; + return nullptr; // If the head task was canceled, remove it and run the selector again. if (UNLIKELY(work_queue->RemoveAllCanceledTasksFromFront())) @@ -604,9 +622,13 @@ Optional<Task> SequenceManagerImpl::TakeTaskImpl() { work_queue->task_queue()->GetQueuePriority()))) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("sequence_manager"), "SequenceManager.YieldToNative"); - return nullopt; + return nullptr; } +#if DCHECK_IS_ON() && !defined(OS_NACL) + LogTaskDebugInfo(work_queue); +#endif // DCHECK_IS_ON() && !defined(OS_NACL) + main_thread_only().task_execution_stack.emplace_back( work_queue->TakeTaskFromWorkQueue(), work_queue->task_queue(), InitializeTaskTiming(work_queue->task_queue())); @@ -615,7 +637,7 @@ Optional<Task> SequenceManagerImpl::TakeTaskImpl() { *main_thread_only().task_execution_stack.rbegin(); NotifyWillProcessTask(&executing_task, &lazy_now); - return std::move(executing_task.pending_task); + return &executing_task.pending_task; } } diff --git a/chromium/base/task/sequence_manager/sequence_manager_impl.h b/chromium/base/task/sequence_manager/sequence_manager_impl.h index a8c1659f52a..1ee7944a06e 100644 --- a/chromium/base/task/sequence_manager/sequence_manager_impl.h +++ b/chromium/base/task/sequence_manager/sequence_manager_impl.h @@ -25,6 +25,7 @@ #include "base/message_loop/message_pump_type.h" #include "base/pending_task.h" #include "base/run_loop.h" +#include "base/sequenced_task_runner.h" #include "base/single_thread_task_runner.h" #include "base/synchronization/lock.h" #include "base/task/common/task_annotator.h" @@ -41,8 +42,6 @@ namespace base { -class TaskObserver; - namespace trace_event { class ConvertableToTraceFormat; } // namespace trace_event @@ -103,6 +102,7 @@ class BASE_EXPORT SequenceManagerImpl // SequenceManager implementation: void BindToCurrentThread() override; + scoped_refptr<SequencedTaskRunner> GetTaskRunnerForCurrentTask() override; void BindToMessagePump(std::unique_ptr<MessagePump> message_pump) override; void SetObserver(Observer* observer) override; void AddTaskTimeObserver(TaskTimeObserver* task_time_observer) override; @@ -126,16 +126,16 @@ class BASE_EXPORT SequenceManagerImpl std::string DescribeAllPendingTasks() const override; std::unique_ptr<NativeWorkHandle> OnNativeWorkPending( TaskQueue::QueuePriority priority) override; + void AddTaskObserver(TaskObserver* task_observer) override; + void RemoveTaskObserver(TaskObserver* task_observer) override; // SequencedTaskSource implementation: - Optional<Task> TakeTask() override; + Task* SelectNextTask() override; void DidRunTask() override; TimeDelta DelayTillNextTask(LazyNow* lazy_now) const override; bool HasPendingHighResolutionTasks() override; bool OnSystemIdle() override; - void AddTaskObserver(TaskObserver* task_observer); - void RemoveTaskObserver(TaskObserver* task_observer); void AddDestructionObserver( MessageLoopCurrent::DestructionObserver* destruction_observer); void RemoveDestructionObserver( @@ -230,7 +230,8 @@ class BASE_EXPORT SequenceManagerImpl // We have to track rentrancy because we support nested runloops but the // selector interface is unaware of those. This struct keeps track off all - // task related state needed to make pairs of TakeTask() / DidRunTask() work. + // task related state needed to make pairs of SelectNextTask() / DidRunTask() + // work. struct ExecutingTask { ExecutingTask(Task&& task, internal::TaskQueueImpl* task_queue, @@ -377,8 +378,8 @@ class BASE_EXPORT SequenceManagerImpl void RecordCrashKeys(const PendingTask&); // Helper to terminate all scoped trace events to allow starting new ones - // in TakeTask(). - Optional<Task> TakeTaskImpl(); + // in SelectNextTask(). + Task* SelectNextTaskImpl(); // Check if a task of priority |priority| should run given the pending set of // native work. @@ -388,7 +389,7 @@ class BASE_EXPORT SequenceManagerImpl TimeDelta GetDelayTillNextDelayedTask(LazyNow* lazy_now) const; #if DCHECK_IS_ON() - void LogTaskDebugInfo(const ExecutingTask& executing_task); + void LogTaskDebugInfo(const internal::WorkQueue* work_queue) const; #endif // Determines if wall time or thread time should be recorded for the next diff --git a/chromium/base/task/sequence_manager/sequence_manager_impl_unittest.cc b/chromium/base/task/sequence_manager/sequence_manager_impl_unittest.cc index cb2f91b8176..3e1bb5dc3db 100644 --- a/chromium/base/task/sequence_manager/sequence_manager_impl_unittest.cc +++ b/chromium/base/task/sequence_manager/sequence_manager_impl_unittest.cc @@ -566,6 +566,18 @@ class TestCountUsesTimeSource : public TickClock { DISALLOW_COPY_AND_ASSIGN(TestCountUsesTimeSource); }; +TEST_P(SequenceManagerTest, GetCorrectTaskRunnerForCurrentTask) { + auto queue = CreateTaskQueue(); + + queue->task_runner()->PostTask( + FROM_HERE, BindLambdaForTesting([&]() { + EXPECT_EQ(queue->task_runner(), + sequence_manager()->GetTaskRunnerForCurrentTask()); + })); + + RunLoop().RunUntilIdle(); +} + TEST_P(SequenceManagerTest, NowNotCalledIfUnneeded) { sequence_manager()->SetWorkBatchSize(6); @@ -2356,9 +2368,9 @@ TEST_P(SequenceManagerTest, TaskQueueObserver_ImmediateTask) { Mock::VerifyAndClearExpectations(&observer); // Unless the immediate work queue is emptied. - sequence_manager()->TakeTask(); + sequence_manager()->SelectNextTask(); sequence_manager()->DidRunTask(); - sequence_manager()->TakeTask(); + sequence_manager()->SelectNextTask(); sequence_manager()->DidRunTask(); EXPECT_CALL(observer, OnPostTask(_, TimeDelta())); EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_)); diff --git a/chromium/base/task/sequence_manager/sequenced_task_source.h b/chromium/base/task/sequence_manager/sequenced_task_source.h index b1153fb32e8..5ea8874ab5e 100644 --- a/chromium/base/task/sequence_manager/sequenced_task_source.h +++ b/chromium/base/task/sequence_manager/sequenced_task_source.h @@ -19,13 +19,13 @@ class SequencedTaskSource { public: virtual ~SequencedTaskSource() = default; - // Returns the next task to run from this source or nullopt if + // Returns the next task to run from this source or nullptr if // there're no more tasks ready to run. If a task is returned, - // DidRunTask() must be invoked before the next call to TakeTask(). - virtual Optional<Task> TakeTask() = 0; + // DidRunTask() must be invoked before the next call to SelectNextTask(). + virtual Task* SelectNextTask() = 0; // Notifies this source that the task previously obtained - // from TakeTask() has been completed. + // from SelectNextTask() has been completed. virtual void DidRunTask() = 0; // Returns the delay till the next task or TimeDelta::Max() diff --git a/chromium/base/task/sequence_manager/task_queue_impl.cc b/chromium/base/task/sequence_manager/task_queue_impl.cc index d109f915ded..7c22f9c1121 100644 --- a/chromium/base/task/sequence_manager/task_queue_impl.cc +++ b/chromium/base/task/sequence_manager/task_queue_impl.cc @@ -78,16 +78,18 @@ TaskQueueImpl::TaskRunner::~TaskRunner() {} bool TaskQueueImpl::TaskRunner::PostDelayedTask(const Location& location, OnceClosure callback, TimeDelta delay) { - return task_poster_->PostTask(PostedTask(std::move(callback), location, delay, - Nestable::kNestable, task_type_)); + return task_poster_->PostTask(PostedTask(this, std::move(callback), location, + delay, Nestable::kNestable, + task_type_)); } bool TaskQueueImpl::TaskRunner::PostNonNestableDelayedTask( const Location& location, OnceClosure callback, TimeDelta delay) { - return task_poster_->PostTask(PostedTask(std::move(callback), location, delay, - Nestable::kNonNestable, task_type_)); + return task_poster_->PostTask(PostedTask(this, std::move(callback), location, + delay, Nestable::kNonNestable, + task_type_)); } bool TaskQueueImpl::TaskRunner::RunsTasksInCurrentSequence() const { @@ -423,8 +425,10 @@ void TaskQueueImpl::PushOntoDelayedIncomingQueue(Task pending_task) { #endif // TODO(altimin): Add a copy method to Task to capture metadata here. + auto task_runner = pending_task.task_runner; PostImmediateTaskImpl( - PostedTask(BindOnce(&TaskQueueImpl::ScheduleDelayedWorkTask, + PostedTask(std::move(task_runner), + BindOnce(&TaskQueueImpl::ScheduleDelayedWorkTask, Unretained(this), std::move(pending_task)), FROM_HERE, TimeDelta(), Nestable::kNonNestable, pending_task.task_type), diff --git a/chromium/base/task/sequence_manager/task_queue_selector.cc b/chromium/base/task/sequence_manager/task_queue_selector.cc index 5e3a14a8e80..cb58d9b4f3a 100644 --- a/chromium/base/task/sequence_manager/task_queue_selector.cc +++ b/chromium/base/task/sequence_manager/task_queue_selector.cc @@ -159,6 +159,15 @@ void TaskQueueSelector::WorkQueueSetBecameNonEmpty(size_t set_index) { } } +void TaskQueueSelector::CollectSkippedOverLowerPriorityTasks( + const internal::WorkQueue* selected_work_queue, + std::vector<const Task*>* result) const { + delayed_work_queue_sets_.CollectSkippedOverLowerPriorityTasks( + selected_work_queue, result); + immediate_work_queue_sets_.CollectSkippedOverLowerPriorityTasks( + selected_work_queue, result); +} + #if DCHECK_IS_ON() || !defined(NDEBUG) bool TaskQueueSelector::CheckContainsQueueForTest( const internal::TaskQueueImpl* queue) const { diff --git a/chromium/base/task/sequence_manager/task_queue_selector.h b/chromium/base/task/sequence_manager/task_queue_selector.h index 2ae9b52ef05..8a79a5e52bd 100644 --- a/chromium/base/task/sequence_manager/task_queue_selector.h +++ b/chromium/base/task/sequence_manager/task_queue_selector.h @@ -76,6 +76,12 @@ class BASE_EXPORT TaskQueueSelector : public WorkQueueSets::Observer { void WorkQueueSetBecameEmpty(size_t set_index) override; void WorkQueueSetBecameNonEmpty(size_t set_index) override; + // Populates |result| with tasks with lower priority than the first task from + // |selected_work_queue| which could otherwise run now. + void CollectSkippedOverLowerPriorityTasks( + const internal::WorkQueue* selected_work_queue, + std::vector<const Task*>* result) const; + protected: WorkQueueSets* delayed_work_queue_sets() { return &delayed_work_queue_sets_; } diff --git a/chromium/base/task/sequence_manager/task_queue_selector_unittest.cc b/chromium/base/task/sequence_manager/task_queue_selector_unittest.cc index c99366471ba..504477048dd 100644 --- a/chromium/base/task/sequence_manager/task_queue_selector_unittest.cc +++ b/chromium/base/task/sequence_manager/task_queue_selector_unittest.cc @@ -74,7 +74,7 @@ class TaskQueueSelectorTestBase : public testing::Test { EnqueueOrderGenerator enqueue_order_generator; for (size_t i = 0; i < num_tasks; i++) { task_queues_[queue_indices[i]]->immediate_work_queue()->Push( - Task(PostedTask(test_closure_, FROM_HERE), TimeTicks(), + Task(PostedTask(nullptr, test_closure_, FROM_HERE), TimeTicks(), EnqueueOrder(), enqueue_order_generator.GenerateNext())); } } @@ -84,15 +84,15 @@ class TaskQueueSelectorTestBase : public testing::Test { size_t num_tasks) { for (size_t i = 0; i < num_tasks; i++) { task_queues_[queue_indices[i]]->immediate_work_queue()->Push(Task( - PostedTask(test_closure_, FROM_HERE), TimeTicks(), EnqueueOrder(), - EnqueueOrder::FromIntForTesting(enqueue_orders[i]))); + PostedTask(nullptr, test_closure_, FROM_HERE), TimeTicks(), + EnqueueOrder(), EnqueueOrder::FromIntForTesting(enqueue_orders[i]))); } } void PushTask(const size_t queue_index, const size_t enqueue_order) { task_queues_[queue_index]->immediate_work_queue()->Push( - Task(PostedTask(test_closure_, FROM_HERE), TimeTicks(), EnqueueOrder(), - EnqueueOrder::FromIntForTesting(enqueue_order))); + Task(PostedTask(nullptr, test_closure_, FROM_HERE), TimeTicks(), + EnqueueOrder(), EnqueueOrder::FromIntForTesting(enqueue_order))); } std::vector<size_t> PopTasksAndReturnQueueIndices() { @@ -666,8 +666,8 @@ TEST_F(TaskQueueSelectorTest, ChooseWithPriority_Empty) { TEST_F(TaskQueueSelectorTest, ChooseWithPriority_OnlyDelayed) { task_queues_[0]->delayed_work_queue()->Push( - Task(PostedTask(test_closure_, FROM_HERE), TimeTicks(), EnqueueOrder(), - EnqueueOrder::FromIntForTesting(2))); + Task(PostedTask(nullptr, test_closure_, FROM_HERE), TimeTicks(), + EnqueueOrder(), EnqueueOrder::FromIntForTesting(2))); bool chose_delayed_over_immediate = false; EXPECT_EQ( @@ -680,8 +680,8 @@ TEST_F(TaskQueueSelectorTest, ChooseWithPriority_OnlyDelayed) { TEST_F(TaskQueueSelectorTest, ChooseWithPriority_OnlyImmediate) { task_queues_[0]->immediate_work_queue()->Push( - Task(PostedTask(test_closure_, FROM_HERE), TimeTicks(), EnqueueOrder(), - EnqueueOrder::FromIntForTesting(2))); + Task(PostedTask(nullptr, test_closure_, FROM_HERE), TimeTicks(), + EnqueueOrder(), EnqueueOrder::FromIntForTesting(2))); bool chose_delayed_over_immediate = false; EXPECT_EQ( @@ -706,8 +706,8 @@ TEST_F(TaskQueueSelectorTest, TestObserverWithOneBlockedQueue) { task_queue->SetQueueEnabled(false); selector.DisableQueue(task_queue.get()); - Task task(PostedTask(test_closure_, FROM_HERE), TimeTicks(), EnqueueOrder(), - EnqueueOrder::FromIntForTesting(2)); + Task task(PostedTask(nullptr, test_closure_, FROM_HERE), TimeTicks(), + EnqueueOrder(), EnqueueOrder::FromIntForTesting(2)); task_queue->immediate_work_queue()->Push(std::move(task)); EXPECT_EQ(nullptr, selector.SelectWorkQueueToService()); @@ -736,10 +736,10 @@ TEST_F(TaskQueueSelectorTest, TestObserverWithTwoBlockedQueues) { selector.SetQueuePriority(task_queue2.get(), TaskQueue::kControlPriority); - Task task1(PostedTask(test_closure_, FROM_HERE), TimeTicks(), + Task task1(PostedTask(nullptr, test_closure_, FROM_HERE), TimeTicks(), EnqueueOrder::FromIntForTesting(2), EnqueueOrder::FromIntForTesting(2)); - Task task2(PostedTask(test_closure_, FROM_HERE), TimeTicks(), + Task task2(PostedTask(nullptr, test_closure_, FROM_HERE), TimeTicks(), EnqueueOrder::FromIntForTesting(3), EnqueueOrder::FromIntForTesting(3)); task_queue->immediate_work_queue()->Push(std::move(task1)); @@ -762,6 +762,21 @@ TEST_F(TaskQueueSelectorTest, TestObserverWithTwoBlockedQueues) { task_queue2->UnregisterTaskQueue(); } +TEST_F(TaskQueueSelectorTest, CollectSkippedOverLowerPriorityTasks) { + size_t queue_order[] = {0, 1, 2, 3, 2, 1, 0}; + PushTasks(queue_order, 7); + selector_.SetQueuePriority(task_queues_[3].get(), TaskQueue::kHighPriority); + + std::vector<const Task*> result; + selector_.CollectSkippedOverLowerPriorityTasks( + task_queues_[3]->immediate_work_queue(), &result); + + ASSERT_EQ(3u, result.size()); + EXPECT_EQ(2u, result[0]->enqueue_order()); // The order here isn't important. + EXPECT_EQ(3u, result[1]->enqueue_order()); + EXPECT_EQ(4u, result[2]->enqueue_order()); +} + class DisabledAntiStarvationLogicTaskQueueSelectorTest : public TaskQueueSelectorTestBase, public testing::WithParamInterface<TaskQueue::QueuePriority> { @@ -839,13 +854,13 @@ class ChooseWithPriorityTest TEST_P(ChooseWithPriorityTest, RoundRobinTest) { task_queues_[0]->immediate_work_queue()->Push(Task( - PostedTask(test_closure_, FROM_HERE), TimeTicks(), + PostedTask(nullptr, test_closure_, FROM_HERE), TimeTicks(), EnqueueOrder::FromIntForTesting(GetParam().immediate_task_enqueue_order), EnqueueOrder::FromIntForTesting( GetParam().immediate_task_enqueue_order))); task_queues_[0]->delayed_work_queue()->Push(Task( - PostedTask(test_closure_, FROM_HERE), TimeTicks(), + PostedTask(nullptr, test_closure_, FROM_HERE), TimeTicks(), EnqueueOrder::FromIntForTesting(GetParam().delayed_task_enqueue_order), EnqueueOrder::FromIntForTesting(GetParam().delayed_task_enqueue_order))); diff --git a/chromium/base/task/sequence_manager/tasks.cc b/chromium/base/task/sequence_manager/tasks.cc index 14bd306b947..a3bd5ce60f6 100644 --- a/chromium/base/task/sequence_manager/tasks.cc +++ b/chromium/base/task/sequence_manager/tasks.cc @@ -17,6 +17,7 @@ Task::Task(internal::PostedTask posted_task, desired_run_time, posted_task.nestable), task_type(posted_task.task_type), + task_runner(std::move(posted_task.task_runner)), enqueue_order_(enqueue_order) { // We use |sequence_num| in DelayedWakeUp for ordering purposes and it // may wrap around to a negative number during the static cast, hence, @@ -28,9 +29,15 @@ Task::Task(internal::PostedTask posted_task, queue_time = posted_task.queue_time; } -namespace internal { +Task::Task(Task&& move_from) = default; + +Task::~Task() = default; -PostedTask::PostedTask(OnceClosure callback, +Task& Task::operator=(Task&& other) = default; + +namespace internal { +PostedTask::PostedTask(scoped_refptr<SequencedTaskRunner> task_runner, + OnceClosure callback, Location location, TimeDelta delay, Nestable nestable, @@ -39,7 +46,8 @@ PostedTask::PostedTask(OnceClosure callback, location(location), delay(delay), nestable(nestable), - task_type(task_type) {} + task_type(task_type), + task_runner(std::move(task_runner)) {} PostedTask::PostedTask(PostedTask&& move_from) noexcept : callback(std::move(move_from.callback)), @@ -47,6 +55,7 @@ PostedTask::PostedTask(PostedTask&& move_from) noexcept delay(move_from.delay), nestable(move_from.nestable), task_type(move_from.task_type), + task_runner(std::move(move_from.task_runner)), queue_time(move_from.queue_time) {} PostedTask::~PostedTask() = default; diff --git a/chromium/base/task/sequence_manager/tasks.h b/chromium/base/task/sequence_manager/tasks.h index 0c886c4b9ca..d1e1912ead2 100644 --- a/chromium/base/task/sequence_manager/tasks.h +++ b/chromium/base/task/sequence_manager/tasks.h @@ -6,6 +6,7 @@ #define BASE_TASK_SEQUENCE_MANAGER_TASKS_H_ #include "base/pending_task.h" +#include "base/sequenced_task_runner.h" #include "base/task/sequence_manager/enqueue_order.h" namespace base { @@ -21,7 +22,8 @@ enum class WakeUpResolution { kLow, kHigh }; // Wrapper around PostTask method arguments and the assigned task type. // Eventually it becomes a PendingTask once accepted by a TaskQueueImpl. struct BASE_EXPORT PostedTask { - explicit PostedTask(OnceClosure callback = OnceClosure(), + explicit PostedTask(scoped_refptr<SequencedTaskRunner> task_runner, + OnceClosure callback = OnceClosure(), Location location = Location(), TimeDelta delay = TimeDelta(), Nestable nestable = Nestable::kNestable, @@ -34,6 +36,9 @@ struct BASE_EXPORT PostedTask { TimeDelta delay; Nestable nestable; TaskType task_type; + // The task runner this task is running on. Can be used by task runners that + // support posting back to the "current sequence". + scoped_refptr<SequencedTaskRunner> task_runner; // The time at which the task was queued. TimeTicks queue_time; @@ -76,6 +81,9 @@ struct BASE_EXPORT Task : public PendingTask { EnqueueOrder enqueue_order = EnqueueOrder(), internal::WakeUpResolution wake_up_resolution = internal::WakeUpResolution::kLow); + Task(Task&& move_from); + ~Task(); + Task& operator=(Task&& other); internal::DelayedWakeUp delayed_wake_up() const { return internal::DelayedWakeUp{delayed_run_time, sequence_num}; @@ -97,6 +105,10 @@ struct BASE_EXPORT Task : public PendingTask { TaskType task_type; + // The task runner this task is running on. Can be used by task runners that + // support posting back to the "current sequence". + scoped_refptr<SequencedTaskRunner> task_runner; + #if DCHECK_IS_ON() bool cross_thread_; #endif diff --git a/chromium/base/task/sequence_manager/thread_controller_impl.cc b/chromium/base/task/sequence_manager/thread_controller_impl.cc index 22bffdb8a5b..676b9a6708d 100644 --- a/chromium/base/task/sequence_manager/thread_controller_impl.cc +++ b/chromium/base/task/sequence_manager/thread_controller_impl.cc @@ -174,7 +174,7 @@ void ThreadControllerImpl::DoWork(WorkType work_type) { WeakPtr<ThreadControllerImpl> weak_ptr = weak_factory_.GetWeakPtr(); // TODO(scheduler-dev): Consider moving to a time based work batch instead. for (int i = 0; i < main_sequence_only().work_batch_size_; i++) { - Optional<PendingTask> task = sequence_->TakeTask(); + Task* task = sequence_->SelectNextTask(); if (!task) break; @@ -189,7 +189,7 @@ void ThreadControllerImpl::DoWork(WorkType work_type) { // Trace events should finish before we call DidRunTask to ensure that // SequenceManager trace events do not interfere with them. TRACE_TASK_EXECUTION("ThreadControllerImpl::RunTask", *task); - task_annotator_.RunTask("SequenceManager RunTask", &*task); + task_annotator_.RunTask("SequenceManager RunTask", task); } if (!weak_ptr) diff --git a/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc b/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc index deeda0e4260..9b0a058efbc 100644 --- a/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc +++ b/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc @@ -342,7 +342,7 @@ TimeDelta ThreadControllerWithMessagePumpImpl::DoWorkImpl( DCHECK(main_thread_only().task_source); for (int i = 0; i < main_thread_only().work_batch_size; i++) { - Optional<Task> task = main_thread_only().task_source->TakeTask(); + Task* task = main_thread_only().task_source->SelectNextTask(); if (!task) break; @@ -362,7 +362,7 @@ TimeDelta ThreadControllerWithMessagePumpImpl::DoWorkImpl( // Trace events should finish before we call DidRunTask to ensure that // SequenceManager trace events do not interfere with them. TRACE_TASK_EXECUTION("ThreadControllerImpl::RunTask", *task); - task_annotator_.RunTask("SequenceManager RunTask", &*task); + task_annotator_.RunTask("SequenceManager RunTask", task); } #if DCHECK_IS_ON() diff --git a/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc b/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc index 64eb8f7e74b..3c02becf16d 100644 --- a/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc +++ b/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc @@ -79,17 +79,17 @@ class FakeSequencedTaskSource : public internal::SequencedTaskSource { explicit FakeSequencedTaskSource(TickClock* clock) : clock_(clock) {} ~FakeSequencedTaskSource() override = default; - Optional<Task> TakeTask() override { + Task* SelectNextTask() override { if (tasks_.empty()) - return nullopt; + return nullptr; if (tasks_.front().delayed_run_time > clock_->NowTicks()) - return nullopt; - Task task = std::move(tasks_.front()); + return nullptr; + running_stack_.push_back(std::move(tasks_.front())); tasks_.pop(); - return task; + return &running_stack_.back(); } - void DidRunTask() override {} + void DidRunTask() override { running_stack_.pop_back(); } TimeDelta DelayTillNextTask(LazyNow* lazy_now) const override { if (tasks_.empty()) @@ -106,8 +106,9 @@ class FakeSequencedTaskSource : public internal::SequencedTaskSource { TimeTicks delayed_run_time) { DCHECK(tasks_.empty() || delayed_run_time.is_null() || tasks_.back().delayed_run_time < delayed_run_time); - tasks_.push(Task(internal::PostedTask(std::move(task), posted_from), - delayed_run_time, EnqueueOrder::FromIntForTesting(13))); + tasks_.push( + Task(internal::PostedTask(nullptr, std::move(task), posted_from), + delayed_run_time, EnqueueOrder::FromIntForTesting(13))); } bool HasPendingHighResolutionTasks() override { return false; } @@ -117,6 +118,7 @@ class FakeSequencedTaskSource : public internal::SequencedTaskSource { private: TickClock* clock_; std::queue<Task> tasks_; + std::vector<Task> running_stack_; }; TimeTicks Seconds(int seconds) { diff --git a/chromium/base/task/sequence_manager/work_queue.cc b/chromium/base/task/sequence_manager/work_queue.cc index 0d9df4a150a..2dfd04da230 100644 --- a/chromium/base/task/sequence_manager/work_queue.cc +++ b/chromium/base/task/sequence_manager/work_queue.cc @@ -305,6 +305,16 @@ void WorkQueue::PopTaskForTesting() { tasks_.pop_front(); } +void WorkQueue::CollectTasksOlderThan(EnqueueOrder reference, + std::vector<const Task*>* result) const { + for (const Task& task : tasks_) { + if (task.enqueue_order() >= reference) + break; + + result->push_back(&task); + } +} + } // namespace internal } // namespace sequence_manager } // namespace base diff --git a/chromium/base/task/sequence_manager/work_queue.h b/chromium/base/task/sequence_manager/work_queue.h index 849d48cac5d..65fdee4ca28 100644 --- a/chromium/base/task/sequence_manager/work_queue.h +++ b/chromium/base/task/sequence_manager/work_queue.h @@ -161,6 +161,11 @@ class BASE_EXPORT WorkQueue { // Test support function. This should not be used in production code. void PopTaskForTesting(); + // Iterates through |tasks_| adding any that are older than |reference| to + // |result|. + void CollectTasksOlderThan(EnqueueOrder reference, + std::vector<const Task*>* result) const; + private: bool InsertFenceImpl(EnqueueOrder fence); diff --git a/chromium/base/task/sequence_manager/work_queue_sets.cc b/chromium/base/task/sequence_manager/work_queue_sets.cc index c2f9886271d..68ec9613338 100644 --- a/chromium/base/task/sequence_manager/work_queue_sets.cc +++ b/chromium/base/task/sequence_manager/work_queue_sets.cc @@ -237,6 +237,19 @@ bool WorkQueueSets::ContainsWorkQueueForTest( } #endif +void WorkQueueSets::CollectSkippedOverLowerPriorityTasks( + const internal::WorkQueue* selected_work_queue, + std::vector<const Task*>* result) const { + EnqueueOrder selected_enqueue_order; + CHECK(selected_work_queue->GetFrontTaskEnqueueOrder(&selected_enqueue_order)); + for (size_t priority = selected_work_queue->work_queue_set_index() + 1; + priority < TaskQueue::kQueuePriorityCount; priority++) { + for (const OldestTaskEnqueueOrder& pair : work_queue_heaps_[priority]) { + pair.value->CollectTasksOlderThan(selected_enqueue_order, result); + } + } +} + } // namespace internal } // namespace sequence_manager } // namespace base diff --git a/chromium/base/task/sequence_manager/work_queue_sets.h b/chromium/base/task/sequence_manager/work_queue_sets.h index 90cc6d789c1..f128c62c369 100644 --- a/chromium/base/task/sequence_manager/work_queue_sets.h +++ b/chromium/base/task/sequence_manager/work_queue_sets.h @@ -95,6 +95,12 @@ class BASE_EXPORT WorkQueueSets { const char* GetName() const { return name_; } + // Collects ready tasks which where skipped over when |selected_work_queue| + // was selected. Note this is somewhat expensive. + void CollectSkippedOverLowerPriorityTasks( + const internal::WorkQueue* selected_work_queue, + std::vector<const Task*>* result) const; + private: struct OldestTaskEnqueueOrder { EnqueueOrder key; diff --git a/chromium/base/task/sequence_manager/work_queue_sets_unittest.cc b/chromium/base/task/sequence_manager/work_queue_sets_unittest.cc index 30f3bb8fa68..6450573d5b1 100644 --- a/chromium/base/task/sequence_manager/work_queue_sets_unittest.cc +++ b/chromium/base/task/sequence_manager/work_queue_sets_unittest.cc @@ -51,14 +51,14 @@ class WorkQueueSetsTest : public testing::Test { } Task FakeTaskWithEnqueueOrder(int enqueue_order) { - Task fake_task(PostedTask(BindOnce([] {}), FROM_HERE), TimeTicks(), + Task fake_task(PostedTask(nullptr, BindOnce([] {}), FROM_HERE), TimeTicks(), EnqueueOrder(), EnqueueOrder::FromIntForTesting(enqueue_order)); return fake_task; } Task FakeNonNestableTaskWithEnqueueOrder(int enqueue_order) { - Task fake_task(PostedTask(BindOnce([] {}), FROM_HERE), TimeTicks(), + Task fake_task(PostedTask(nullptr, BindOnce([] {}), FROM_HERE), TimeTicks(), EnqueueOrder(), EnqueueOrder::FromIntForTesting(enqueue_order)); fake_task.nestable = Nestable::kNonNestable; @@ -331,6 +331,32 @@ TEST_F(WorkQueueSetsTest, PushNonNestableTaskToFront) { EXPECT_EQ(queue1, work_queue_sets_->GetOldestQueueInSet(set)); } +TEST_F(WorkQueueSetsTest, CollectSkippedOverLowerPriorityTasks) { + WorkQueue* queue1 = NewTaskQueue("queue1"); + WorkQueue* queue2 = NewTaskQueue("queue2"); + WorkQueue* queue3 = NewTaskQueue("queue3"); + + work_queue_sets_->ChangeSetIndex(queue1, 3); + work_queue_sets_->ChangeSetIndex(queue2, 2); + work_queue_sets_->ChangeSetIndex(queue3, 1); + + queue1->Push(FakeTaskWithEnqueueOrder(1)); + queue1->Push(FakeTaskWithEnqueueOrder(2)); + queue2->Push(FakeTaskWithEnqueueOrder(3)); + queue3->Push(FakeTaskWithEnqueueOrder(4)); + queue3->Push(FakeTaskWithEnqueueOrder(5)); + queue2->Push(FakeTaskWithEnqueueOrder(6)); + queue1->Push(FakeTaskWithEnqueueOrder(7)); + + std::vector<const Task*> result; + work_queue_sets_->CollectSkippedOverLowerPriorityTasks(queue3, &result); + + ASSERT_EQ(3u, result.size()); + EXPECT_EQ(3u, result[0]->enqueue_order()); // The order here isn't important. + EXPECT_EQ(1u, result[1]->enqueue_order()); + EXPECT_EQ(2u, result[2]->enqueue_order()); +} + } // namespace internal } // namespace sequence_manager } // namespace base diff --git a/chromium/base/task/sequence_manager/work_queue_unittest.cc b/chromium/base/task/sequence_manager/work_queue_unittest.cc index 721f60fda7d..1ab88bf5065 100644 --- a/chromium/base/task/sequence_manager/work_queue_unittest.cc +++ b/chromium/base/task/sequence_manager/work_queue_unittest.cc @@ -73,23 +73,23 @@ class WorkQueueTest : public testing::Test { protected: Task FakeCancelableTaskWithEnqueueOrder(int enqueue_order, WeakPtr<Cancelable> weak_ptr) { - Task fake_task( - PostedTask(BindOnce(&Cancelable::NopTask, weak_ptr), FROM_HERE), - TimeTicks(), EnqueueOrder(), - EnqueueOrder::FromIntForTesting(enqueue_order)); + Task fake_task(PostedTask(nullptr, BindOnce(&Cancelable::NopTask, weak_ptr), + FROM_HERE), + TimeTicks(), EnqueueOrder(), + EnqueueOrder::FromIntForTesting(enqueue_order)); return fake_task; } Task FakeTaskWithEnqueueOrder(int enqueue_order) { - Task fake_task(PostedTask(BindOnce(&NopTask), FROM_HERE), TimeTicks(), - EnqueueOrder(), + Task fake_task(PostedTask(nullptr, BindOnce(&NopTask), FROM_HERE), + TimeTicks(), EnqueueOrder(), EnqueueOrder::FromIntForTesting(enqueue_order)); return fake_task; } Task FakeNonNestableTaskWithEnqueueOrder(int enqueue_order) { - Task fake_task(PostedTask(BindOnce(&NopTask), FROM_HERE), TimeTicks(), - EnqueueOrder(), + Task fake_task(PostedTask(nullptr, BindOnce(&NopTask), FROM_HERE), + TimeTicks(), EnqueueOrder(), EnqueueOrder::FromIntForTesting(enqueue_order)); fake_task.nestable = Nestable::kNonNestable; return fake_task; @@ -555,6 +555,20 @@ TEST_F(WorkQueueTest, RemoveAllCanceledTasksFromFrontQueueBlockedByFence) { EXPECT_FALSE(work_queue_->GetFrontTaskEnqueueOrder(&enqueue_order)); } +TEST_F(WorkQueueTest, CollectTasksOlderThan) { + work_queue_->Push(FakeTaskWithEnqueueOrder(2)); + work_queue_->Push(FakeTaskWithEnqueueOrder(3)); + work_queue_->Push(FakeTaskWithEnqueueOrder(4)); + + std::vector<const Task*> result; + work_queue_->CollectTasksOlderThan(EnqueueOrder::FromIntForTesting(4), + &result); + + ASSERT_EQ(2u, result.size()); + EXPECT_EQ(2u, result[0]->enqueue_order()); + EXPECT_EQ(3u, result[1]->enqueue_order()); +} + } // namespace internal } // namespace sequence_manager } // namespace base diff --git a/chromium/base/task/simple_task_executor.cc b/chromium/base/task/simple_task_executor.cc new file mode 100644 index 00000000000..ffccf52be86 --- /dev/null +++ b/chromium/base/task/simple_task_executor.cc @@ -0,0 +1,62 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/task/simple_task_executor.h" + +namespace base { + +SimpleTaskExecutor::SimpleTaskExecutor( + scoped_refptr<SingleThreadTaskRunner> task_queue) + : task_queue_(std::move(task_queue)), + previous_task_executor_(GetTaskExecutorForCurrentThread()) { + DCHECK(task_queue_); + // The TaskExecutor API does not expect nesting, but this can happen in tests + // so we have to work around it here. + if (previous_task_executor_) + SetTaskExecutorForCurrentThread(nullptr); + SetTaskExecutorForCurrentThread(this); +} + +SimpleTaskExecutor::~SimpleTaskExecutor() { + if (previous_task_executor_) + SetTaskExecutorForCurrentThread(nullptr); + SetTaskExecutorForCurrentThread(previous_task_executor_); +} + +bool SimpleTaskExecutor::PostDelayedTask(const Location& from_here, + const TaskTraits& traits, + OnceClosure task, + TimeDelta delay) { + return task_queue_->PostDelayedTask(from_here, std::move(task), delay); +} + +scoped_refptr<TaskRunner> SimpleTaskExecutor::CreateTaskRunner( + const TaskTraits& traits) { + return task_queue_; +} + +scoped_refptr<SequencedTaskRunner> +SimpleTaskExecutor::CreateSequencedTaskRunner(const TaskTraits& traits) { + return task_queue_; +} + +scoped_refptr<SingleThreadTaskRunner> +SimpleTaskExecutor::CreateSingleThreadTaskRunner( + const TaskTraits& traits, + SingleThreadTaskRunnerThreadMode thread_mode) { + return task_queue_; +} + +#if defined(OS_WIN) +scoped_refptr<SingleThreadTaskRunner> +SimpleTaskExecutor::CreateCOMSTATaskRunner( + const TaskTraits& traits, + SingleThreadTaskRunnerThreadMode thread_mode) { + // It seems pretty unlikely this will be used on a comsta task thread. + NOTREACHED(); + return task_queue_; +} +#endif // defined(OS_WIN) + +} // namespace base diff --git a/chromium/base/task/simple_task_executor.h b/chromium/base/task/simple_task_executor.h new file mode 100644 index 00000000000..7d9a74dc5d2 --- /dev/null +++ b/chromium/base/task/simple_task_executor.h @@ -0,0 +1,52 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_TASK_SIMPLE_TASK_EXECUTOR_H_ +#define BASE_TASK_SIMPLE_TASK_EXECUTOR_H_ + +#include "base/task/task_executor.h" +#include "build/build_config.h" + +namespace base { + +// A simple TaskExecutor with exactly one SingleThreadTaskRunner. +// Must be instantiated and destroyed on the thread that runs tasks for the +// SingleThreadTaskRunner. +class BASE_EXPORT SimpleTaskExecutor : public TaskExecutor { + public: + explicit SimpleTaskExecutor(scoped_refptr<SingleThreadTaskRunner> task_queue); + + ~SimpleTaskExecutor() override; + + bool PostDelayedTask(const Location& from_here, + const TaskTraits& traits, + OnceClosure task, + TimeDelta delay) override; + + scoped_refptr<TaskRunner> CreateTaskRunner(const TaskTraits& traits) override; + + scoped_refptr<SequencedTaskRunner> CreateSequencedTaskRunner( + const TaskTraits& traits) override; + + scoped_refptr<SingleThreadTaskRunner> CreateSingleThreadTaskRunner( + const TaskTraits& traits, + SingleThreadTaskRunnerThreadMode thread_mode) override; + +#if defined(OS_WIN) + scoped_refptr<SingleThreadTaskRunner> CreateCOMSTATaskRunner( + const TaskTraits& traits, + SingleThreadTaskRunnerThreadMode thread_mode) override; +#endif // defined(OS_WIN) + + private: + const scoped_refptr<SingleThreadTaskRunner> task_queue_; + + // In tests there may already be a TaskExecutor registered for the thread, we + // keep tack of the previous TaskExecutor and restored it upon destruction. + TaskExecutor* const previous_task_executor_; +}; + +} // namespace base + +#endif // BASE_TASK_SIMPLE_TASK_EXECUTOR_H_ diff --git a/chromium/base/task/single_thread_task_executor.cc b/chromium/base/task/single_thread_task_executor.cc index 7e12b784faf..01d993be952 100644 --- a/chromium/base/task/single_thread_task_executor.cc +++ b/chromium/base/task/single_thread_task_executor.cc @@ -17,7 +17,8 @@ SingleThreadTaskExecutor::SingleThreadTaskExecutor(MessagePumpType type) .Build())), default_task_queue_(sequence_manager_->CreateTaskQueue( sequence_manager::TaskQueue::Spec("default_tq"))), - type_(type) { + type_(type), + simple_task_executor_(task_runner()) { sequence_manager_->SetDefaultTaskRunner(default_task_queue_->task_runner()); sequence_manager_->BindToMessagePump(MessagePump::Create(type)); diff --git a/chromium/base/task/single_thread_task_executor.h b/chromium/base/task/single_thread_task_executor.h index d350420d797..c048a830cf1 100644 --- a/chromium/base/task/single_thread_task_executor.h +++ b/chromium/base/task/single_thread_task_executor.h @@ -11,6 +11,7 @@ #include "base/memory/scoped_refptr.h" #include "base/message_loop/message_pump_type.h" #include "base/single_thread_task_runner.h" +#include "base/task/simple_task_executor.h" namespace base { @@ -40,6 +41,7 @@ class BASE_EXPORT SingleThreadTaskExecutor { std::unique_ptr<sequence_manager::SequenceManager> sequence_manager_; scoped_refptr<sequence_manager::TaskQueue> default_task_queue_; MessagePumpType type_; + SimpleTaskExecutor simple_task_executor_; DISALLOW_COPY_AND_ASSIGN(SingleThreadTaskExecutor); }; diff --git a/chromium/base/task/single_thread_task_executor_unittest.cc b/chromium/base/task/single_thread_task_executor_unittest.cc new file mode 100644 index 00000000000..b64294f404f --- /dev/null +++ b/chromium/base/task/single_thread_task_executor_unittest.cc @@ -0,0 +1,58 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/task/single_thread_task_executor.h" + +#include "base/run_loop.h" +#include "base/task/post_task.h" +#include "base/test/bind_test_util.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::IsNull; +using ::testing::NotNull; + +namespace base { + +TEST(SingleThreadTaskExecutorTest, GetTaskExecutorForCurrentThread) { + EXPECT_THAT(GetTaskExecutorForCurrentThread(), IsNull()); + + { + SingleThreadTaskExecutor single_thread_task_executor; + EXPECT_THAT(GetTaskExecutorForCurrentThread(), NotNull()); + } + + EXPECT_THAT(GetTaskExecutorForCurrentThread(), IsNull()); +} + +TEST(SingleThreadTaskExecutorTest, + GetTaskExecutorForCurrentThreadInPostedTask) { + SingleThreadTaskExecutor single_thread_task_executor; + TaskExecutor* task_executor = GetTaskExecutorForCurrentThread(); + + EXPECT_THAT(task_executor, NotNull()); + + RunLoop run_loop; + single_thread_task_executor.task_runner()->PostTask( + FROM_HERE, BindLambdaForTesting([&]() { + EXPECT_EQ(GetTaskExecutorForCurrentThread(), task_executor); + run_loop.Quit(); + })); + + run_loop.Run(); +} + +TEST(SingleThreadTaskExecutorTest, CurrentThread) { + SingleThreadTaskExecutor single_thread_task_executor; + + EXPECT_EQ(single_thread_task_executor.task_runner(), + base::CreateSingleThreadTaskRunner({base::CurrentThread()})); + + // There's only one task queue so priority is ignored. + EXPECT_EQ(single_thread_task_executor.task_runner(), + base::CreateSingleThreadTaskRunner( + {base::CurrentThread(), base::TaskPriority::BEST_EFFORT})); +} + +} // namespace base diff --git a/chromium/base/task/task_executor.cc b/chromium/base/task/task_executor.cc index 8b527b0cd5b..355fa3f445f 100644 --- a/chromium/base/task/task_executor.cc +++ b/chromium/base/task/task_executor.cc @@ -6,8 +6,10 @@ #include <type_traits> +#include "base/no_destructor.h" #include "base/task/task_traits.h" #include "base/task/task_traits_extension.h" +#include "base/threading/thread_local.h" namespace base { @@ -30,6 +32,21 @@ static_assert( } // namespace +ThreadLocalPointer<TaskExecutor>* GetTLSForCurrentTaskExecutor() { + static NoDestructor<ThreadLocalPointer<TaskExecutor>> instance; + return instance.get(); +} + +void SetTaskExecutorForCurrentThread(TaskExecutor* task_executor) { + DCHECK(!task_executor || !GetTLSForCurrentTaskExecutor()->Get() || + GetTLSForCurrentTaskExecutor()->Get() == task_executor); + GetTLSForCurrentTaskExecutor()->Set(task_executor); +} + +TaskExecutor* GetTaskExecutorForCurrentThread() { + return GetTLSForCurrentTaskExecutor()->Get(); +} + void RegisterTaskExecutor(uint8_t extension_id, TaskExecutor* task_executor) { DCHECK_NE(extension_id, TaskTraitsExtensionStorage::kInvalidExtensionId); DCHECK_LE(extension_id, TaskTraitsExtensionStorage::kMaxExtensionId); @@ -57,4 +74,4 @@ TaskExecutor* GetRegisteredTaskExecutorForTraits(const TaskTraits& traits) { return nullptr; } -} // namespace base
\ No newline at end of file +} // namespace base diff --git a/chromium/base/task/task_executor.h b/chromium/base/task/task_executor.h index b4e79e14c9a..a062fdc4391 100644 --- a/chromium/base/task/task_executor.h +++ b/chromium/base/task/task_executor.h @@ -74,6 +74,13 @@ void BASE_EXPORT RegisterTaskExecutor(uint8_t extension_id, TaskExecutor* task_executor); void BASE_EXPORT UnregisterTaskExecutorForTesting(uint8_t extension_id); +// Stores the provided TaskExecutor in TLS for the current thread, to be used by +// tasks with the CurrentThread() trait. +void BASE_EXPORT SetTaskExecutorForCurrentThread(TaskExecutor* task_executor); + +// Returns the task executor registered for the current thread. +BASE_EXPORT TaskExecutor* GetTaskExecutorForCurrentThread(); + // Determines whether a registered TaskExecutor will handle tasks with the given // |traits| and, if so, returns a pointer to it. Otherwise, returns |nullptr|. TaskExecutor* GetRegisteredTaskExecutorForTraits(const TaskTraits& traits); diff --git a/chromium/base/task/task_traits.h b/chromium/base/task/task_traits.h index c1e52ed0f07..03abf17913f 100644 --- a/chromium/base/task/task_traits.h +++ b/chromium/base/task/task_traits.h @@ -185,6 +185,14 @@ struct WithBaseSyncPrimitives {}; // between tasks, see base::PostTask::CreateSequencedTaskRunner. struct ThreadPool {}; +// Tasks and task runners with this thread will run tasks on the virtual thread +// (sequence) they are posted/created from. Other traits may be specified +// alongside this one to refine properties for the associated tasks +// (e.g. base::TaskPriority or content::BrowserTaskType) as long as those traits +// are compatible with the current thread (e.g. cannot specify base::MayBlock() +// on a non-blocking thread or alter base::TaskShutdownBehavior). +struct CurrentThread {}; + // Describes metadata for a single task or a group of tasks. class BASE_EXPORT TaskTraits { public: @@ -196,6 +204,7 @@ class BASE_EXPORT TaskTraits { ValidTrait(MayBlock); ValidTrait(WithBaseSyncPrimitives); ValidTrait(ThreadPool); + ValidTrait(CurrentThread); }; // Invoking this constructor without arguments produces TaskTraits that are @@ -255,15 +264,22 @@ class BASE_EXPORT TaskTraits { may_block_(trait_helpers::HasTrait<MayBlock, ArgTypes...>()), with_base_sync_primitives_( trait_helpers::HasTrait<WithBaseSyncPrimitives, ArgTypes...>()), - use_thread_pool_(trait_helpers::HasTrait<ThreadPool, ArgTypes...>()) { + use_thread_pool_(trait_helpers::HasTrait<ThreadPool, ArgTypes...>()), + use_current_thread_( + trait_helpers::HasTrait<CurrentThread, ArgTypes...>()) { constexpr bool has_thread_pool = trait_helpers::HasTrait<ThreadPool, ArgTypes...>(); constexpr bool has_extension = !trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value; + constexpr bool has_current_thread = + trait_helpers::HasTrait<CurrentThread, ArgTypes...>(); + static_assert( + !has_current_thread || !has_thread_pool, + "base::CurrentThread is mutually exclusive with base::ThreadPool"); static_assert( - has_thread_pool ^ has_extension, + has_thread_pool ^ has_extension || has_current_thread, "Traits must explicitly specify a destination (e.g. ThreadPool or a " - "named thread like BrowserThread)"); + "named thread like BrowserThread, or CurrentThread)"); } constexpr TaskTraits(const TaskTraits& other) = default; @@ -271,14 +287,15 @@ class BASE_EXPORT TaskTraits { // TODO(eseckler): Default the comparison operator once C++20 arrives. bool operator==(const TaskTraits& other) const { - static_assert(sizeof(TaskTraits) == 15, + static_assert(sizeof(TaskTraits) == 16, "Update comparison operator when TaskTraits change"); return extension_ == other.extension_ && priority_ == other.priority_ && shutdown_behavior_ == other.shutdown_behavior_ && thread_policy_ == other.thread_policy_ && may_block_ == other.may_block_ && with_base_sync_primitives_ == other.with_base_sync_primitives_ && - use_thread_pool_ == other.use_thread_pool_; + use_thread_pool_ == other.use_thread_pool_ && + use_current_thread_ == other.use_current_thread_; } // Sets the priority of tasks with these traits to |priority|. @@ -335,6 +352,10 @@ class BASE_EXPORT TaskTraits { // Returns true if tasks with these traits execute on the thread pool. constexpr bool use_thread_pool() const { return use_thread_pool_; } + // Returns true if tasks with these traits execute on the virtual thread + // (sequence) they are posted/created from. + constexpr bool use_current_thread() const { return use_current_thread_; } + uint8_t extension_id() const { return extension_.extension_id; } // Access the extension data by parsing it into the provided extension type. @@ -353,6 +374,7 @@ class BASE_EXPORT TaskTraits { TaskPriority priority, bool may_block, bool use_thread_pool, + bool use_current_thread, TaskTraitsExtensionStorage extension) : extension_(extension), priority_(static_cast<uint8_t>(priority) | @@ -362,8 +384,9 @@ class BASE_EXPORT TaskTraits { thread_policy_(static_cast<uint8_t>(ThreadPolicy::PREFER_BACKGROUND)), may_block_(may_block), with_base_sync_primitives_(false), - use_thread_pool_(use_thread_pool) { - static_assert(sizeof(TaskTraits) == 15, "Keep this constructor up to date"); + use_thread_pool_(use_thread_pool), + use_current_thread_(use_current_thread) { + static_assert(sizeof(TaskTraits) == 16, "Keep this constructor up to date"); } // This bit is set in |priority_|, |shutdown_behavior_| and |thread_policy_| @@ -378,6 +401,7 @@ class BASE_EXPORT TaskTraits { bool may_block_; bool with_base_sync_primitives_; bool use_thread_pool_; + bool use_current_thread_; }; // Returns string literals for the enums defined in this file. These methods diff --git a/chromium/base/task/task_traits_unittest.nc b/chromium/base/task/task_traits_unittest.nc index 8755ca028ff..73fc7be5506 100644 --- a/chromium/base/task/task_traits_unittest.nc +++ b/chromium/base/task/task_traits_unittest.nc @@ -26,6 +26,8 @@ constexpr TaskTraits traits = {TaskShutdownBehavior::BLOCK_SHUTDOWN, TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}; #elif defined(NCTEST_TASK_TRAITS_INVALID_TYPE) // [r"no matching constructor for initialization of 'const base::TaskTraits'"] constexpr TaskTraits traits = {TaskShutdownBehavior::BLOCK_SHUTDOWN, true}; +#elif defined(NCTEST_TASK_TRAITS_CURRENT_THREAD_AND_THREADPOOL) // [r"base::CurrentThread is mutually exclusive with base::ThreadPool"] +constexpr TaskTraits traits = {ThreadPool(), CurrentThread()}; #endif } // namespace base diff --git a/chromium/base/task/thread_pool/historical_histogram_data.md b/chromium/base/task/thread_pool/historical_histogram_data.md new file mode 100644 index 00000000000..d253731b6fb --- /dev/null +++ b/chromium/base/task/thread_pool/historical_histogram_data.md @@ -0,0 +1,92 @@ +# Historical Histogram Data + +This page presents data captured from `base::ThreadPool` histograms at a given +point in time so it can be used in future design decisions. + +All data is 28-day aggregation on Stable channel. + +## Number of tasks between waits + +Number of tasks between two waits by a foreground worker thread in a +browser/renderer process. + +Histogram name: ThreadPool.NumTasksBetweenWaits.(Browser/Renderer).Foreground +Date: August 2019 +Values in tables below are percentiles. + +### Windows + +| Number of tasks | Browser process | Renderer process | +|-----------------|-----------------|------------------| +| 1 | 87 | 92 | +| 2 | 95 | 98 | +| 5 | 99 | 100 | + +### Mac + +| Number of tasks | Browser process | Renderer process | +|-----------------|-----------------|------------------| +| 1 | 81 | 90 | +| 2 | 92 | 97 | +| 5 | 98 | 100 | + +### Android + +| Number of tasks | Browser process | Renderer process | +|-----------------|-----------------|------------------| +| 1 | 92 | 96 | +| 2 | 97 | 98 | +| 5 | 99 | 100 | + + +## Number of tasks run while queueing + +Number of tasks run by ThreadPool while task was queuing (from time task was +posted until time it was run). Recorded for dummy heartbeat tasks in the +*browser* process. The heartbeat recording avoids dependencies between this +report and other work in the system. + +Histogram name: ThreadPool.NumTasksRunWhileQueuing.Browser.* +Date: September 2019 +Values in tables below are percentiles. + +Note: In *renderer* processes, on all platforms/priorities, 0 tasks are run +while queuing at 99.5th percentile. + +### Windows + +| Number of tasks | USER_BLOCKING | USER_VISIBLE | BEST_EFFORT | +|-----------------|---------------|--------------|-------------| +| 0 | 95 | 93 | 90 | +| 1 | 98 | 95 | 92 | +| 2 | 99 | 96 | 93 | +| 5 | 100 | 98 | 95 | + +### Mac + +| Number of tasks | USER_BLOCKING | USER_VISIBLE | BEST_EFFORT | +|-----------------|---------------|--------------|-------------| +| 0 | 100 | 100 | 99 | +| 1 | 100 | 100 | 99 | +| 2 | 100 | 100 | 99 | +| 5 | 100 | 100 | 100 | + +### Android + +| Number of tasks | USER_BLOCKING | USER_VISIBLE | BEST_EFFORT | +|-----------------|---------------|--------------|-------------| +| 0 | 99 | 98 | 97 | +| 1 | 100 | 99 | 99 | +| 2 | 100 | 99 | 99 | +| 5 | 100 | 100 | 100 | + +### Chrome OS + +For all priorities, 0 tasks are run while queueing at 99.5th percentile. + +### Analysis + +The number of tasks that run while a BEST_EFFORT task is queued is unexpectedly +low. We should explore creating threads less aggressively, at the expense of +keeping BEST_EFFORT tasks in the queue for a longer time. See +[Bug 906079](https://crbug.com/906079). diff --git a/chromium/base/task/thread_pool/initialization_util.cc b/chromium/base/task/thread_pool/initialization_util.cc index d70c4c3d27d..6fd197300c9 100644 --- a/chromium/base/task/thread_pool/initialization_util.cc +++ b/chromium/base/task/thread_pool/initialization_util.cc @@ -6,6 +6,7 @@ #include <algorithm> +#include "base/numerics/ranges.h" #include "base/system/sys_info.h" namespace base { @@ -16,7 +17,7 @@ int RecommendedMaxNumberOfThreadsInThreadGroup(int min, int offset) { const int num_of_cores = SysInfo::NumberOfProcessors(); const int threads = std::ceil<int>(num_of_cores * cores_multiplier) + offset; - return std::min(max, std::max(min, threads)); + return ClampToRange(threads, min, max); } } // namespace base diff --git a/chromium/base/task/thread_pool/job_task_source.cc b/chromium/base/task/thread_pool/job_task_source.cc index c6e5f9733c5..be52945d0d9 100644 --- a/chromium/base/task/thread_pool/job_task_source.cc +++ b/chromium/base/task/thread_pool/job_task_source.cc @@ -19,6 +19,120 @@ namespace base { namespace internal { +// Memory ordering on |state_| operations +// +// The write operation on |state_| in WillRunTask() uses +// std::memory_order_release, matched by std::memory_order_acquire on read +// operations (in DidProcessTask()) to establish a +// Release-Acquire ordering. When a call to WillRunTask() is caused by an +// increase of max concurrency followed by an associated +// NotifyConcurrencyIncrease(), the priority queue lock guarantees an +// happens-after relation with NotifyConcurrencyIncrease(). This ensures that an +// increase of max concurrency that happened-before NotifyConcurrencyIncrease() +// is visible to a read operation that happens-after WillRunTask(). +// +// In DidProcessTask(), this is necessary to +// ensure that the task source is always re-enqueued when it needs to. When the +// task source needs to be queued, either because the current task yielded or +// because of NotifyConcurrencyIncrease(), one of the following is true: +// A) DidProcessTask() happens-after WillRunTask(): +// T1: Current task returns (because it is done) or yields. +// T2: Increases the value returned by GetMaxConcurrency() +// NotifyConcurrencyIncrease() enqueues the task source +// T3: WillRunTask(), in response to the concurrency increase - Release +// Does not keep the TaskSource in PriorityQueue because it is at max +// concurrency +// T1: DidProcessTask() - Acquire - Because of memory barrier, sees the same +// (or newer) max concurrency as T2 +// Re-enqueues the TaskSource because no longer at max concurrency +// Without the memory barrier, T1 may see an outdated max concurrency that +// is lower than the actual max concurrency and won't re-enqueue the +// task source, because it thinks it's already saturated. +// The task source often needs to be re-enqueued if its task +// completed because it yielded and |max_concurrency| wasn't decreased. +// B) DidProcessTask() happens-before WillRunTask(): +// T1: Current task returns (because it is done) or yields +// T2: Increases the value returned by GetMaxConcurrency() +// NotifyConcurrencyIncrease() enqueues the task source +// T1: DidProcessTask() - Acquire (ineffective) +// Since the task source is already in the queue, it doesn't matter +// whether T1 re-enqueues the task source or not. +// Note that stale values the other way around can cause incorrectly +// re-enqueuing this task_source, which is not an issue because the queues +// support empty task sources. + +JobTaskSource::State::State() = default; +JobTaskSource::State::~State() = default; + +JobTaskSource::State::Value JobTaskSource::State::Cancel() { + return {value_.fetch_or(kCanceledMask, std::memory_order_relaxed)}; +} + +JobTaskSource::State::Value +JobTaskSource::State::TryIncrementWorkerCountFromWorkerRelease( + size_t max_concurrency) { + uint32_t value_before_add = value_.load(std::memory_order_relaxed); + + // std::memory_order_release on success to establish Release-Acquire ordering + // with DecrementWorkerCountAcquire() (see Memory Ordering comment at top of + // the file). + while (!(value_before_add & kCanceledMask) && + (value_before_add >> kWorkerCountBitOffset) < max_concurrency && + !value_.compare_exchange_weak( + value_before_add, value_before_add + kWorkerCountIncrement, + std::memory_order_release, std::memory_order_relaxed)) { + } + return {value_before_add}; +} + +JobTaskSource::State::Value +JobTaskSource::State::DecrementWorkerCountFromWorkerAcquire() { + const size_t value_before_sub = + value_.fetch_sub(kWorkerCountIncrement, std::memory_order_acquire); + DCHECK((value_before_sub >> kWorkerCountBitOffset) > 0); + return {value_before_sub}; +} + +JobTaskSource::State::Value +JobTaskSource::State::IncrementWorkerCountFromJoiningThread() { + size_t value_before_add = + value_.fetch_add(kWorkerCountIncrement, std::memory_order_relaxed); + return {value_before_add}; +} + +JobTaskSource::State::Value +JobTaskSource::State::DecrementWorkerCountFromJoiningThread() { + const size_t value_before_sub = + value_.fetch_sub(kWorkerCountIncrement, std::memory_order_relaxed); + DCHECK((value_before_sub >> kWorkerCountBitOffset) > 0); + return {value_before_sub}; +} + +JobTaskSource::State::Value JobTaskSource::State::Load() const { + return {value_.load(std::memory_order_relaxed)}; +} + +JobTaskSource::JoinFlag::JoinFlag() = default; +JobTaskSource::JoinFlag::~JoinFlag() = default; + +void JobTaskSource::JoinFlag::SetWaiting() { + const auto previous_value = + value_.exchange(kWaitingForWorkerToYield, std::memory_order_relaxed); + DCHECK(previous_value == kNotWaiting); +} + +bool JobTaskSource::JoinFlag::ShouldWorkerYield() { + // The fetch_and() sets the state to kWaitingForWorkerToSignal if it was + // previously kWaitingForWorkerToYield, otherwise it leaves it unchanged. + return value_.fetch_and(kWaitingForWorkerToSignal, + std::memory_order_relaxed) == + kWaitingForWorkerToYield; +} + +bool JobTaskSource::JoinFlag::ShouldWorkerSignal() { + return value_.exchange(kNotWaiting, std::memory_order_relaxed) != kNotWaiting; +} + JobTaskSource::JobTaskSource( const Location& from_here, const TaskTraits& traits, @@ -28,64 +142,122 @@ JobTaskSource::JobTaskSource( : TaskSource(traits, nullptr, TaskSourceExecutionMode::kJob), from_here_(from_here), max_concurrency_callback_(std::move(max_concurrency_callback)), - worker_task_(base::BindRepeating( - [](JobTaskSource* self, - const RepeatingCallback<void(experimental::JobDelegate*)>& - worker_task) { + worker_task_(std::move(worker_task)), + primary_task_(base::BindRepeating( + [](JobTaskSource* self) { // Each worker task has its own delegate with associated state. experimental::JobDelegate job_delegate{self, self->delegate_}; - worker_task.Run(&job_delegate); + self->worker_task_.Run(&job_delegate); }, - base::Unretained(this), - std::move(worker_task))), + base::Unretained(this))), queue_time_(TimeTicks::Now()), delegate_(delegate) { DCHECK(delegate_); } JobTaskSource::~JobTaskSource() { -#if DCHECK_IS_ON() - auto worker_count = worker_count_.load(std::memory_order_relaxed); // Make sure there's no outstanding active run operation left. - DCHECK(worker_count == 0U || worker_count == kInvalidWorkerCount) - << worker_count; -#endif + DCHECK_EQ(state_.Load().worker_count(), 0U); } ExecutionEnvironment JobTaskSource::GetExecutionEnvironment() { return {SequenceToken::Create(), nullptr}; } +bool JobTaskSource::WillJoin() { + { + CheckedAutoLock auto_lock(lock_); + DCHECK(!worker_released_condition_); // This may only be called once. + worker_released_condition_ = lock_.CreateConditionVariable(); + } + // std::memory_order_relaxed on |worker_count_| is sufficient because call to + // GetMaxConcurrency() is used for a best effort early exit. Stale values will + // only cause WaitForParticipationOpportunity() to be called. + const auto state_before_add = state_.IncrementWorkerCountFromJoiningThread(); + + if (!state_before_add.is_canceled() && + state_before_add.worker_count() < GetMaxConcurrency()) { + return true; + } + return WaitForParticipationOpportunity(); +} + +bool JobTaskSource::RunJoinTask() { + experimental::JobDelegate job_delegate{this, nullptr}; + worker_task_.Run(&job_delegate); + + // std::memory_order_relaxed on |worker_count_| is sufficient because the call + // to GetMaxConcurrency() is used for a best effort early exit. Stale values + // will only cause WaitForParticipationOpportunity() to be called. + const auto state = state_.Load(); + if (!state.is_canceled() && state.worker_count() <= GetMaxConcurrency()) + return true; + + return WaitForParticipationOpportunity(); +} + +void JobTaskSource::Cancel(TaskSource::Transaction* transaction) { + // Sets the kCanceledMask bit on |state_| so that further calls to + // WillRunTask() never succeed. std::memory_order_relaxed is sufficient + // because this task source never needs to be re-enqueued after Cancel(). + state_.Cancel(); +} + +bool JobTaskSource::WaitForParticipationOpportunity() { + CheckedAutoLock auto_lock(lock_); + + // std::memory_order_relaxed is sufficient because no other state is + // synchronized with |state_| outside of |lock_|. + auto state = state_.Load(); + size_t max_concurrency = GetMaxConcurrency(); + + // Wait until either: + // A) |worker_count| is below or equal to max concurrency and state is not + // canceled. + // B) All other workers returned and |worker_count| is 1. + while (!((state.worker_count() <= max_concurrency && !state.is_canceled()) || + state.worker_count() == 1)) { + // std::memory_order_relaxed is sufficient because no other state is + // synchronized with |join_flag_| outside of |lock_|. + join_flag_.SetWaiting(); + + // To avoid unnecessarily waiting, if either condition A) or B) change + // |lock_| is taken and |worker_released_condition_| signaled if necessary: + // 1- In DidProcessTask(), after worker count is decremented. + // 2- In NotifyConcurrencyIncrease(), following a max_concurrency increase. + worker_released_condition_->Wait(); + state = state_.Load(); + max_concurrency = GetMaxConcurrency(); + } + // Case A: + if (state.worker_count() <= max_concurrency && !state.is_canceled()) + return true; + // Case B: + // Only the joining thread remains. + DCHECK_EQ(state.worker_count(), 1U); + DCHECK(state.is_canceled() || max_concurrency == 0U); + state_.DecrementWorkerCountFromJoiningThread(); + return false; +} + TaskSource::RunStatus JobTaskSource::WillRunTask() { - // When this call is caused by an increase of max concurrency followed by an - // associated NotifyConcurrencyIncrease(), the priority queue lock guarantees - // an happens-after relation with NotifyConcurrencyIncrease(). The memory - // operations on |worker_count| below and in DidProcessTask() use - // std::memory_order_release and std::memory_order_acquire respectively to - // establish a Release-Acquire ordering. This ensures that all memory - // side-effects made before this point, including an increase of max - // concurrency followed by NotifyConcurrencyIncrease() are visible to a - // DidProcessTask() call which is ordered after this one. const size_t max_concurrency = GetMaxConcurrency(); - size_t worker_count_before_add = - worker_count_.load(std::memory_order_relaxed); - - // std::memory_order_release on success to make the newest |max_concurrency| - // visible to a thread that calls DidProcessTask() containing a matching - // std::memory_order_acquire. - while (worker_count_before_add < max_concurrency && - !worker_count_.compare_exchange_weak( - worker_count_before_add, worker_count_before_add + 1, - std::memory_order_release, std::memory_order_relaxed)) { - } + // std::memory_order_release on success to establish Release-Acquire ordering + // with read operations (see Memory Ordering comment at top of the file). + const auto state_before_add = + state_.TryIncrementWorkerCountFromWorkerRelease(max_concurrency); + // Don't allow this worker to run the task if either: - // A) |worker_count_| is already at |max_concurrency|. - // B) |max_concurrency| was lowered below or to |worker_count_|. - // C) |worker_count_| was invalidated. - if (worker_count_before_add >= max_concurrency) { - // The caller is prevented from running a task from this TaskSource. + // A) |state_| was canceled. + // B) |worker_count| is already at |max_concurrency|. + // C) |max_concurrency| was lowered below or to |worker_count|. + // Case A: + if (state_before_add.is_canceled()) + return RunStatus::kDisallowed; + const size_t worker_count_before_add = state_before_add.worker_count(); + // Case B) or C): + if (worker_count_before_add >= max_concurrency) return RunStatus::kDisallowed; - } DCHECK_LT(worker_count_before_add, max_concurrency); return max_concurrency == worker_count_before_add + 1 @@ -96,15 +268,23 @@ TaskSource::RunStatus JobTaskSource::WillRunTask() { size_t JobTaskSource::GetRemainingConcurrency() const { // std::memory_order_relaxed is sufficient because no other state is // synchronized with GetRemainingConcurrency(). - const size_t worker_count = worker_count_.load(std::memory_order_relaxed); + const auto state = state_.Load(); const size_t max_concurrency = GetMaxConcurrency(); // Avoid underflows. - if (worker_count > max_concurrency) + if (state.is_canceled() || state.worker_count() > max_concurrency) return 0; - return max_concurrency - worker_count; + return max_concurrency - state.worker_count(); } void JobTaskSource::NotifyConcurrencyIncrease() { + { + // Lock is taken to access |join_flag_| below and signal + // |worker_released_condition_|. + CheckedAutoLock auto_lock(lock_); + if (join_flag_.ShouldWorkerSignal()) + worker_released_condition_->Signal(); + } + #if DCHECK_IS_ON() { AutoLock auto_lock(version_lock_); @@ -125,6 +305,14 @@ size_t JobTaskSource::GetMaxConcurrency() const { return max_concurrency_callback_.Run(); } +bool JobTaskSource::ShouldYield() { + // It is safe to read |join_flag_| without a lock since this + // variable is atomic, keeping in mind that threads may not immediately see + // the new value when it is updated. + return TS_UNCHECKED_READ(join_flag_).ShouldWorkerYield() || + state_.Load().is_canceled(); +} + #if DCHECK_IS_ON() size_t JobTaskSource::GetConcurrencyIncreaseVersion() const { @@ -151,45 +339,36 @@ bool JobTaskSource::WaitForConcurrencyIncreaseUpdate(size_t recorded_version) { #endif // DCHECK_IS_ON() Optional<Task> JobTaskSource::TakeTask(TaskSource::Transaction* transaction) { - DCHECK_GT(worker_count_.load(std::memory_order_relaxed), 0U); - DCHECK(worker_task_); - return base::make_optional<Task>(from_here_, worker_task_, TimeDelta()); + // JobTaskSource members are not lock-protected so no need to acquire a lock + // if |transaction| is nullptr. + DCHECK_GT(state_.Load().worker_count(), 0U); + DCHECK(primary_task_); + return base::make_optional<Task>(from_here_, primary_task_, TimeDelta()); } bool JobTaskSource::DidProcessTask(TaskSource::Transaction* transaction) { - size_t worker_count_before_sub = - worker_count_.load(std::memory_order_relaxed); - - // std::memory_order_acquire on |worker_count_| is necessary to establish - // Release-Acquire ordering (see WillRunTask()). - // When the task source needs to be queued, either because the current task - // yielded or because of NotifyConcurrencyIncrease(), one of the following is - // true: - // A) The JobTaskSource is already in the queue (no worker picked up the - // extra work yet): Incorrectly returning false is fine and the memory - // barrier may be ineffective. - // B) The JobTaskSource() is no longer in the queue: The Release-Acquire - // ordering with WillRunTask() established by |worker_count| ensures that - // the upcoming call for GetMaxConcurrency() happens-after any - // NotifyConcurrencyIncrease() that happened-before WillRunTask(). If - // this task completed because it yielded, this barrier guarantees that - // it sees an up-to-date concurrency value and correctly re-enqueues. - // - // Note that stale values the other way around (incorrectly re-enqueuing) are - // not an issue because the queues support empty task sources. - while (worker_count_before_sub != kInvalidWorkerCount && - !worker_count_.compare_exchange_weak( - worker_count_before_sub, worker_count_before_sub - 1, - std::memory_order_acquire, std::memory_order_relaxed)) { - } - if (worker_count_before_sub == kInvalidWorkerCount) + // Lock is needed to access |join_flag_| below and signal + // |worker_released_condition_|. If |transaction|, then |lock_| is already + // taken. + CheckedAutoLockMaybe auto_lock(transaction ? nullptr : &lock_); + AnnotateAcquiredLockAlias annotate(lock_, lock_); + + // std::memory_order_acquire to establish Release-Acquire ordering with + // WillRunTask() (see Memory Ordering comment at top of the file). + const auto state_before_sub = state_.DecrementWorkerCountFromWorkerAcquire(); + + if (join_flag_.ShouldWorkerSignal()) + worker_released_condition_->Signal(); + + // A canceled task source should never get re-enqueued. + if (state_before_sub.is_canceled()) return false; - DCHECK_GT(worker_count_before_sub, 0U); + DCHECK_GT(state_before_sub.worker_count(), 0U); // Re-enqueue the TaskSource if the task ran and the worker count is below the // max concurrency. - return worker_count_before_sub <= GetMaxConcurrency(); + return state_before_sub.worker_count() <= GetMaxConcurrency(); } SequenceSortKey JobTaskSource::GetSortKey() const { @@ -197,12 +376,7 @@ SequenceSortKey JobTaskSource::GetSortKey() const { } Optional<Task> JobTaskSource::Clear(TaskSource::Transaction* transaction) { - // Invalidate |worker_count_| so that further calls to WillRunTask() never - // succeed. std::memory_order_relaxed is sufficient because this task source - // never needs to be re-enqueued after Clear(). - size_t worker_count_before_store = - worker_count_.exchange(kInvalidWorkerCount, std::memory_order_relaxed); - DCHECK_GT(worker_count_before_store, 0U); + Cancel(); // Nothing is cleared since other workers might still racily run tasks. For // simplicity, the destructor will take care of it once all references are // released. diff --git a/chromium/base/task/thread_pool/job_task_source.h b/chromium/base/task/thread_pool/job_task_source.h index ddaca19b343..57838679e7a 100644 --- a/chromium/base/task/thread_pool/job_task_source.h +++ b/chromium/base/task/thread_pool/job_task_source.h @@ -38,10 +38,33 @@ class BASE_EXPORT JobTaskSource : public TaskSource { RepeatingCallback<size_t()> max_concurrency_callback, PooledTaskRunnerDelegate* delegate); + static experimental::JobHandle CreateJobHandle( + scoped_refptr<internal::JobTaskSource> task_source) { + return experimental::JobHandle(std::move(task_source)); + } + // Notifies this task source that max concurrency was increased, and the // number of worker should be adjusted. void NotifyConcurrencyIncrease(); + // Informs this JobTaskSource that the current thread would like to join and + // contribute to running |worker_task|. Returns true if the joining thread can + // contribute (RunJoinTask() can be called), or false if joining was completed + // and all other workers returned because either there's no work remaining or + // Job was cancelled. + bool WillJoin(); + + // Contributes to running |worker_task| and returns true if the joining thread + // can contribute again (RunJoinTask() can be called again), or false if + // joining was completed and all other workers returned because either there's + // no work remaining or Job was cancelled. This should be called only after + // WillJoin() or RunJoinTask() previously returned true. + bool RunJoinTask(); + + // Cancels this JobTaskSource, causing all workers to yield and WillRunTask() + // to return RunStatus::kDisallowed. + void Cancel(TaskSource::Transaction* transaction = nullptr); + // TaskSource: ExecutionEnvironment GetExecutionEnvironment() override; size_t GetRemainingConcurrency() const override; @@ -50,6 +73,12 @@ class BASE_EXPORT JobTaskSource : public TaskSource { // concurrently. size_t GetMaxConcurrency() const; + // Returns true if a worker should return from the worker task on the current + // thread ASAP. + bool ShouldYield(); + + PooledTaskRunnerDelegate* delegate() const { return delegate_; } + #if DCHECK_IS_ON() size_t GetConcurrencyIncreaseVersion() const; // Returns true if the concurrency version was updated above @@ -58,11 +87,98 @@ class BASE_EXPORT JobTaskSource : public TaskSource { #endif // DCHECK_IS_ON() private: - static constexpr size_t kInvalidWorkerCount = - std::numeric_limits<size_t>::max(); + // Atomic internal state to track the number of workers running a task from + // this JobTaskSource and whether this JobTaskSource is canceled. + class State { + public: + static constexpr size_t kCanceledMask = 1; + static constexpr size_t kWorkerCountBitOffset = 1; + static constexpr size_t kWorkerCountIncrement = 1 << kWorkerCountBitOffset; + + struct Value { + size_t worker_count() const { return value >> kWorkerCountBitOffset; } + // Returns true if canceled. + bool is_canceled() const { return value & kCanceledMask; } + + uint32_t value; + }; + + State(); + ~State(); + + // Sets as canceled using std::memory_order_relaxed. Returns the state + // before the operation. + Value Cancel(); + + // Increments the worker count by 1 if smaller than |max_concurrency| and if + // |!is_canceled()|, using std::memory_order_release, and returns the state + // before the operation. Equivalent to Load() otherwise. + Value TryIncrementWorkerCountFromWorkerRelease(size_t max_concurrency); + + // Decrements the worker count by 1 using std::memory_order_acquire. Returns + // the state before the operation. + Value DecrementWorkerCountFromWorkerAcquire(); + + // Increments the worker count by 1 using std::memory_order_relaxed. Returns + // the state before the operation. + Value IncrementWorkerCountFromJoiningThread(); + + // Decrements the worker count by 1 using std::memory_order_relaxed. Returns + // the state before the operation. + Value DecrementWorkerCountFromJoiningThread(); + + // Loads and returns the state, using std::memory_order_relaxed. + Value Load() const; + + private: + std::atomic<uint32_t> value_{0}; + }; + + // Atomic flag that indicates if the joining thread is currently waiting on + // another worker to yield or to signal. + class JoinFlag { + public: + static constexpr uint32_t kNotWaiting = 0; + static constexpr uint32_t kWaitingForWorkerToSignal = 1; + static constexpr uint32_t kWaitingForWorkerToYield = 3; + // kWaitingForWorkerToYield is 3 because the impl relies on the following + // property. + static_assert((kWaitingForWorkerToYield & kWaitingForWorkerToSignal) == + kWaitingForWorkerToSignal, + ""); + + JoinFlag(); + ~JoinFlag(); + + // Sets the status as kWaitingForWorkerToYield using + // std::memory_order_relaxed. + void SetWaiting(); + + // If the flag is kWaitingForWorkerToYield, returns true indicating that the + // worker should yield, and atomically updates to kWaitingForWorkerToSignal + // (using std::memory_order_relaxed) to ensure that a single worker yields + // in response to SetWaiting(). + bool ShouldWorkerYield(); + + // If the flag is kWaiting*, returns true indicating that the worker should + // signal, and atomically updates to kNotWaiting (using + // std::memory_order_relaxed) to ensure that a single worker signals in + // response to SetWaiting(). + bool ShouldWorkerSignal(); + + private: + std::atomic<uint32_t> value_{kNotWaiting}; + }; ~JobTaskSource() override; + // Called from the joining thread. Waits for the worker count to be below or + // equal to max concurrency (will happen when a worker calls + // DidProcessTask()). Returns true if the joining thread should run a task, or + // false if joining was completed and all other workers returned because + // either there's no work remaining or Job was cancelled. + bool WaitForParticipationOpportunity(); + // TaskSource: RunStatus WillRunTask() override; Optional<Task> TakeTask(TaskSource::Transaction* transaction) override; @@ -70,13 +186,23 @@ class BASE_EXPORT JobTaskSource : public TaskSource { bool DidProcessTask(TaskSource::Transaction* transaction) override; SequenceSortKey GetSortKey() const override; - // The current number of workers concurrently running tasks from this - // TaskSource. - std::atomic_size_t worker_count_{0U}; + // Current atomic state. + State state_; + // Normally, |join_flag_| is protected by |lock_|, except in ShouldYield() + // hence the use of atomics. + JoinFlag join_flag_ GUARDED_BY(lock_); + // Signaled when |join_flag_| is kWaiting* and a worker returns. + std::unique_ptr<ConditionVariable> worker_released_condition_ + GUARDED_BY(lock_); const Location from_here_; - base::RepeatingCallback<size_t()> max_concurrency_callback_; - base::RepeatingClosure worker_task_; + RepeatingCallback<size_t()> max_concurrency_callback_; + + // Worker task set by the job owner. + RepeatingCallback<void(experimental::JobDelegate*)> worker_task_; + // Task returned from TakeTask(), that calls |worker_task_| internally. + RepeatingClosure primary_task_; + const TimeTicks queue_time_; PooledTaskRunnerDelegate* delegate_; diff --git a/chromium/base/task/thread_pool/job_task_source_unittest.cc b/chromium/base/task/thread_pool/job_task_source_unittest.cc index 789cf5ab7a0..413771f2235 100644 --- a/chromium/base/task/thread_pool/job_task_source_unittest.cc +++ b/chromium/base/task/thread_pool/job_task_source_unittest.cc @@ -29,6 +29,8 @@ class MockPooledTaskRunnerDelegate : public PooledTaskRunnerDelegate { MOCK_CONST_METHOD1(ShouldYield, bool(const TaskSource* task_source)); MOCK_METHOD1(EnqueueJobTaskSource, bool(scoped_refptr<JobTaskSource> task_source)); + MOCK_METHOD1(RemoveJobTaskSource, + void(scoped_refptr<JobTaskSource> task_source)); MOCK_CONST_METHOD1(IsRunningPoolWithTraits, bool(const TaskTraits& traits)); MOCK_METHOD2(UpdatePriority, void(scoped_refptr<TaskSource> task_source, @@ -108,13 +110,17 @@ TEST_F(ThreadPoolJobTaskSourceTest, Clear) { EXPECT_EQ(registered_task_source_d.WillRunTask(), TaskSource::RunStatus::kAllowedNotSaturated); + EXPECT_FALSE(task_source->ShouldYield()); + { EXPECT_EQ(1U, task_source->GetRemainingConcurrency()); auto task = registered_task_source_c.Clear(); std::move(task->task).Run(); + registered_task_source_c.DidProcessTask(); EXPECT_EQ(0U, task_source->GetRemainingConcurrency()); } // The task source shouldn't allow any further tasks after Clear. + EXPECT_TRUE(task_source->ShouldYield()); EXPECT_EQ(RegisteredTaskSource::CreateForTesting(task_source).WillRunTask(), TaskSource::RunStatus::kDisallowed); @@ -122,6 +128,7 @@ TEST_F(ThreadPoolJobTaskSourceTest, Clear) { { auto task = registered_task_source_d.Clear(); std::move(task->task).Run(); + registered_task_source_d.DidProcessTask(); EXPECT_EQ(0U, task_source->GetRemainingConcurrency()); } @@ -129,15 +136,53 @@ TEST_F(ThreadPoolJobTaskSourceTest, Clear) { std::move(task_a->task).Run(); registered_task_source_a.DidProcessTask(); - // A valid outstanding RunStatus can also take & run a task. + // A valid outstanding RunStatus can also take and run a task. { auto task = registered_task_source_b.TakeTask(); std::move(task->task).Run(); registered_task_source_b.DidProcessTask(); } - // Sanity check. +} + +// Verifies that a job task source doesn't return an "allowed" RunStatus after +// Cancel() is called. +TEST_F(ThreadPoolJobTaskSourceTest, Cancel) { + auto job_task = base::MakeRefCounted<test::MockJobTask>( + DoNothing(), /* num_tasks_to_run */ 3); + scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource( + FROM_HERE, {ThreadPool(), TaskPriority::BEST_EFFORT}, + &pooled_task_runner_delegate_); + + auto registered_task_source_a = + RegisteredTaskSource::CreateForTesting(task_source); + EXPECT_EQ(registered_task_source_a.WillRunTask(), + TaskSource::RunStatus::kAllowedNotSaturated); + auto task_a = registered_task_source_a.TakeTask(); + + auto registered_task_source_b = + RegisteredTaskSource::CreateForTesting(task_source); + EXPECT_EQ(registered_task_source_b.WillRunTask(), + TaskSource::RunStatus::kAllowedNotSaturated); + + EXPECT_FALSE(task_source->ShouldYield()); + + task_source->Cancel(); + EXPECT_TRUE(task_source->ShouldYield()); + + // The task source shouldn't allow any further tasks after Cancel. EXPECT_EQ(RegisteredTaskSource::CreateForTesting(task_source).WillRunTask(), TaskSource::RunStatus::kDisallowed); + + // A task that was already acquired can still run. + std::move(task_a->task).Run(); + registered_task_source_a.DidProcessTask(); + + // A RegisteredTaskSource that's ready can also take and run a task. + { + auto task = registered_task_source_b.TakeTask(); + std::move(task->task).Run(); + registered_task_source_b.DidProcessTask(); + } } // Verifies that multiple tasks can run in parallel up to |max_concurrency|. @@ -183,6 +228,68 @@ TEST_F(ThreadPoolJobTaskSourceTest, RunTasksInParallel) { EXPECT_FALSE(registered_task_source_c.DidProcessTask()); } +// Verifies the normal flow of running the join task until completion. +TEST_F(ThreadPoolJobTaskSourceTest, RunJoinTask) { + auto job_task = base::MakeRefCounted<test::MockJobTask>( + DoNothing(), /* num_tasks_to_run */ 2); + scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource( + FROM_HERE, ThreadPool(), &pooled_task_runner_delegate_); + + EXPECT_TRUE(task_source->WillJoin()); + // Intentionally run |worker_task| twice to make sure RunJoinTask() calls + // it again. This can happen in production if the joining thread spuriously + // return and needs to run again. + EXPECT_TRUE(task_source->RunJoinTask()); + EXPECT_FALSE(task_source->RunJoinTask()); +} + +// Verifies that WillJoin() doesn't allow a joining thread to contribute +// after Cancel() is called. +TEST_F(ThreadPoolJobTaskSourceTest, CancelJoinTask) { + auto job_task = base::MakeRefCounted<test::MockJobTask>( + DoNothing(), /* num_tasks_to_run */ 2); + scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource( + FROM_HERE, ThreadPool(), &pooled_task_runner_delegate_); + + task_source->Cancel(); + EXPECT_FALSE(task_source->WillJoin()); +} + +// Verifies that RunJoinTask() doesn't allow a joining thread to contribute +// after Cancel() is called. +TEST_F(ThreadPoolJobTaskSourceTest, JoinCancelTask) { + auto job_task = base::MakeRefCounted<test::MockJobTask>( + DoNothing(), /* num_tasks_to_run */ 2); + scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource( + FROM_HERE, ThreadPool(), &pooled_task_runner_delegate_); + + EXPECT_TRUE(task_source->WillJoin()); + task_source->Cancel(); + EXPECT_FALSE(task_source->RunJoinTask()); +} + +// Verifies that the join task can run in parallel with worker tasks up to +// |max_concurrency|. +TEST_F(ThreadPoolJobTaskSourceTest, RunJoinTaskInParallel) { + auto job_task = base::MakeRefCounted<test::MockJobTask>( + DoNothing(), /* num_tasks_to_run */ 2); + scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource( + FROM_HERE, ThreadPool(), &pooled_task_runner_delegate_); + + auto registered_task_source = + RegisteredTaskSource::CreateForTesting(task_source); + EXPECT_EQ(registered_task_source.WillRunTask(), + TaskSource::RunStatus::kAllowedNotSaturated); + auto worker_task = registered_task_source.TakeTask(); + + EXPECT_TRUE(task_source->WillJoin()); + + std::move(worker_task->task).Run(); + EXPECT_FALSE(registered_task_source.DidProcessTask()); + + EXPECT_FALSE(task_source->RunJoinTask()); +} + // Verifies that a call to NotifyConcurrencyIncrease() calls the delegate // and allows to run additional tasks. TEST_F(ThreadPoolJobTaskSourceTest, NotifyConcurrencyIncrease) { @@ -342,13 +449,9 @@ TEST_F(ThreadPoolJobTaskSourceTest, InvalidDidProcessTask) { auto registered_task_source = RegisteredTaskSource::CreateForTesting(task_source); - EXPECT_EQ(registered_task_source.WillRunTask(), - TaskSource::RunStatus::kAllowedSaturated); - // Can not be called before TakeTask(). - EXPECT_DCHECK_DEATH(registered_task_source.DidProcessTask()); - auto task = registered_task_source.TakeTask(); - registered_task_source.DidProcessTask(); + // Can not be called before WillRunTask(). + EXPECT_DCHECK_DEATH(registered_task_source.DidProcessTask()); } } // namespace internal diff --git a/chromium/base/task/thread_pool/pooled_task_runner_delegate.h b/chromium/base/task/thread_pool/pooled_task_runner_delegate.h index e00f481c6a5..0ec1b87c10b 100644 --- a/chromium/base/task/thread_pool/pooled_task_runner_delegate.h +++ b/chromium/base/task/thread_pool/pooled_task_runner_delegate.h @@ -46,6 +46,10 @@ class BASE_EXPORT PooledTaskRunnerDelegate { virtual bool EnqueueJobTaskSource( scoped_refptr<JobTaskSource> task_source) = 0; + // Removes |task_source| from the priority queue. + virtual void RemoveJobTaskSource( + scoped_refptr<JobTaskSource> task_source) = 0; + // Invoked when RunsTasksInCurrentSequence() is called on a // PooledParallelTaskRunner. Returns true if the current thread is part of the // ThreadGroup associated with |traits|. diff --git a/chromium/base/task/thread_pool/priority_queue.cc b/chromium/base/task/thread_pool/priority_queue.cc index 28077b8740e..85eae1fe469 100644 --- a/chromium/base/task/thread_pool/priority_queue.cc +++ b/chromium/base/task/thread_pool/priority_queue.cc @@ -139,20 +139,18 @@ RegisteredTaskSource PriorityQueue::PopTaskSource() { } RegisteredTaskSource PriorityQueue::RemoveTaskSource( - scoped_refptr<TaskSource> task_source) { - DCHECK(task_source); - + const TaskSource& task_source) { if (IsEmpty()) return nullptr; - const HeapHandle heap_handle = task_source->heap_handle(); + const HeapHandle heap_handle = task_source.heap_handle(); if (!heap_handle.IsValid()) return nullptr; TaskSourceAndSortKey& task_source_and_sort_key = const_cast<PriorityQueue::TaskSourceAndSortKey&>( container_.at(heap_handle)); - DCHECK_EQ(task_source_and_sort_key.task_source().get(), task_source); + DCHECK_EQ(task_source_and_sort_key.task_source().get(), &task_source); RegisteredTaskSource registered_task_source = task_source_and_sort_key.take_task_source(); diff --git a/chromium/base/task/thread_pool/priority_queue.h b/chromium/base/task/thread_pool/priority_queue.h index fbe391e9384..d4d88842126 100644 --- a/chromium/base/task/thread_pool/priority_queue.h +++ b/chromium/base/task/thread_pool/priority_queue.h @@ -49,7 +49,7 @@ class BASE_EXPORT PriorityQueue { // RegisteredTaskSource which evaluates to true if successful, or false if // |task_source| is not currently in the PriorityQueue or the PriorityQueue is // empty. - RegisteredTaskSource RemoveTaskSource(scoped_refptr<TaskSource> task_source); + RegisteredTaskSource RemoveTaskSource(const TaskSource& task_source); // Updates the sort key of the TaskSource in |transaction| to // match its current traits. No-ops if the TaskSource is not in the diff --git a/chromium/base/task/thread_pool/priority_queue_unittest.cc b/chromium/base/task/thread_pool/priority_queue_unittest.cc index d5af9167457..30ad206ac98 100644 --- a/chromium/base/task/thread_pool/priority_queue_unittest.cc +++ b/chromium/base/task/thread_pool/priority_queue_unittest.cc @@ -55,7 +55,7 @@ class PriorityQueueWithSequencesTest : public testing::Test { } test::TaskEnvironment task_environment{ - test::ScopedTaskEnvironment::TimeSource::MOCK_TIME}; + test::TaskEnvironment::TimeSource::MOCK_TIME}; scoped_refptr<TaskSource> sequence_a = MakeSequenceWithTraitsAndTask( TaskTraits(ThreadPool(), TaskPriority::USER_VISIBLE)); @@ -144,34 +144,34 @@ TEST_F(PriorityQueueWithSequencesTest, RemoveSequence) { // Remove |sequence_a| from the PriorityQueue. |sequence_b| is still the // sequence with the highest priority. - EXPECT_TRUE(pq.RemoveTaskSource(sequence_a).Unregister()); + EXPECT_TRUE(pq.RemoveTaskSource(*sequence_a).Unregister()); EXPECT_EQ(sort_key_b, pq.PeekSortKey()); ExpectNumSequences(1U, 0U, 2U); // RemoveTaskSource() should return false if called on a sequence not in the // PriorityQueue. - EXPECT_FALSE(pq.RemoveTaskSource(sequence_a).Unregister()); + EXPECT_FALSE(pq.RemoveTaskSource(*sequence_a).Unregister()); ExpectNumSequences(1U, 0U, 2U); // Remove |sequence_b| from the PriorityQueue. |sequence_c| becomes the // sequence with the highest priority. - EXPECT_TRUE(pq.RemoveTaskSource(sequence_b).Unregister()); + EXPECT_TRUE(pq.RemoveTaskSource(*sequence_b).Unregister()); EXPECT_EQ(sort_key_c, pq.PeekSortKey()); ExpectNumSequences(1U, 0U, 1U); // Remove |sequence_d| from the PriorityQueue. |sequence_c| is still the // sequence with the highest priority. - EXPECT_TRUE(pq.RemoveTaskSource(sequence_d).Unregister()); + EXPECT_TRUE(pq.RemoveTaskSource(*sequence_d).Unregister()); EXPECT_EQ(sort_key_c, pq.PeekSortKey()); ExpectNumSequences(0U, 0U, 1U); // Remove |sequence_c| from the PriorityQueue, making it empty. - EXPECT_TRUE(pq.RemoveTaskSource(sequence_c).Unregister()); + EXPECT_TRUE(pq.RemoveTaskSource(*sequence_c).Unregister()); EXPECT_TRUE(pq.IsEmpty()); ExpectNumSequences(0U, 0U, 0U); // Return false if RemoveTaskSource() is called on an empty PriorityQueue. - EXPECT_FALSE(pq.RemoveTaskSource(sequence_c).Unregister()); + EXPECT_FALSE(pq.RemoveTaskSource(*sequence_c).Unregister()); ExpectNumSequences(0U, 0U, 0U); } diff --git a/chromium/base/task/thread_pool/sequence.cc b/chromium/base/task/thread_pool/sequence.cc index 3a4139c551e..ea7ce3053de 100644 --- a/chromium/base/task/thread_pool/sequence.cc +++ b/chromium/base/task/thread_pool/sequence.cc @@ -91,6 +91,7 @@ bool Sequence::DidProcessTask(TaskSource::Transaction* transaction) { // WillRunTask(). DCHECK(has_worker_); has_worker_ = false; + // See comment on TaskSource::task_runner_ for lifetime management details. if (queue_.empty()) { ReleaseTaskRunner(); return false; @@ -108,20 +109,17 @@ SequenceSortKey Sequence::GetSortKey() const { Optional<Task> Sequence::Clear(TaskSource::Transaction* transaction) { CheckedAutoLockMaybe auto_lock(transaction ? nullptr : &lock_); - has_worker_ = false; - return base::make_optional<Task>( - FROM_HERE, - base::BindOnce( - [](scoped_refptr<Sequence> self, base::queue<Task> queue) { - bool queue_was_empty = queue.empty(); - while (!queue.empty()) - queue.pop(); - if (!queue_was_empty) { - self->ReleaseTaskRunner(); - } - }, - scoped_refptr<Sequence>(this), std::move(queue_)), - TimeDelta()); + // See comment on TaskSource::task_runner_ for lifetime management details. + if (!queue_.empty() && !has_worker_) + ReleaseTaskRunner(); + return base::make_optional<Task>(FROM_HERE, + base::BindOnce( + [](base::queue<Task> queue) { + while (!queue.empty()) + queue.pop(); + }, + std::move(queue_)), + TimeDelta()); } void Sequence::ReleaseTaskRunner() { diff --git a/chromium/base/task/thread_pool/sequence.h b/chromium/base/task/thread_pool/sequence.h index cbbbda42645..ca505ae84db 100644 --- a/chromium/base/task/thread_pool/sequence.h +++ b/chromium/base/task/thread_pool/sequence.h @@ -104,8 +104,7 @@ class BASE_EXPORT Sequence : public TaskSource { bool DidProcessTask(TaskSource::Transaction* transaction) override; SequenceSortKey GetSortKey() const override; - // Releases reference to TaskRunner. This might cause this object to be - // deleted; therefore, no member access should be made after this method. + // Releases reference to TaskRunner. void ReleaseTaskRunner(); const SequenceToken token_ = SequenceToken::Create(); diff --git a/chromium/base/task/thread_pool/sequence_unittest.cc b/chromium/base/task/thread_pool/sequence_unittest.cc index 7257a20d6f1..c5166239151 100644 --- a/chromium/base/task/thread_pool/sequence_unittest.cc +++ b/chromium/base/task/thread_pool/sequence_unittest.cc @@ -184,7 +184,7 @@ TEST(ThreadPoolSequenceTest, GetSortKeyForeground) { // Verify that a DCHECK fires if DidProcessTask() is called on a sequence which // didn't return a Task. -TEST(ThreadPoolSequenceTest, DidProcessTaskWithoutTakeTask) { +TEST(ThreadPoolSequenceTest, DidProcessTaskWithoutWillRunTask) { scoped_refptr<Sequence> sequence = MakeRefCounted<Sequence>( TaskTraits(ThreadPool()), nullptr, TaskSourceExecutionMode::kParallel); Sequence::Transaction sequence_transaction(sequence->BeginTransaction()); @@ -193,7 +193,6 @@ TEST(ThreadPoolSequenceTest, DidProcessTaskWithoutTakeTask) { auto registered_task_source = RegisteredTaskSource::CreateForTesting(sequence); EXPECT_DCHECK_DEATH({ - registered_task_source.WillRunTask(); registered_task_source.DidProcessTask(&sequence_transaction); }); } diff --git a/chromium/base/task/thread_pool/service_thread.cc b/chromium/base/task/thread_pool/service_thread.cc index 400fbb0590e..a7e1d21061a 100644 --- a/chromium/base/task/thread_pool/service_thread.cc +++ b/chromium/base/task/thread_pool/service_thread.cc @@ -71,39 +71,27 @@ void ServiceThread::PerformHeartbeatLatencyReport() const { if (!task_tracker_) return; - static constexpr TaskTraits kReportedTraits[] = { - {ThreadPool(), TaskPriority::BEST_EFFORT}, - {ThreadPool(), TaskPriority::BEST_EFFORT, MayBlock()}, - {ThreadPool(), TaskPriority::USER_VISIBLE}, - {ThreadPool(), TaskPriority::USER_VISIBLE, MayBlock()}, - {ThreadPool(), TaskPriority::USER_BLOCKING}, - {ThreadPool(), TaskPriority::USER_BLOCKING, MayBlock()}}; - - // Only record latency for one set of TaskTraits per report to avoid bias in - // the order in which tasks are posted (should we record all at once) as well - // as to avoid spinning up many worker threads to process this report if the + // Only record latency for one TaskPriority per report to avoid bias in the + // order in which tasks are posted (should we record all at once) as well as + // to avoid spinning up many worker threads to process this report if the // thread pool is currently idle (each thread group keeps at least one idle // thread so a single task isn't an issue). // Invoke RandInt() out-of-line to ensure it's obtained before // TimeTicks::Now(). - const TaskTraits& profiled_traits = - kReportedTraits[RandInt(0, base::size(kReportedTraits) - 1)]; + const TaskPriority profiled_priority = static_cast<TaskPriority>( + RandInt(static_cast<int>(TaskPriority::LOWEST), + static_cast<int>(TaskPriority::HIGHEST))); // Post through the static API to time the full stack. Use a new Now() for // every set of traits in case PostTask() itself is slow. // Bonus: this approach also includes the overhead of BindOnce() in the // reported latency. - // TODO(jessemckenna): pass |profiled_traits| directly to - // RecordHeartbeatLatencyAndTasksRunWhileQueuingHistograms() once compiler - // error on NaCl is fixed - TaskPriority task_priority = profiled_traits.priority(); - bool may_block = profiled_traits.may_block(); base::PostTask( - FROM_HERE, profiled_traits, + FROM_HERE, {base::ThreadPool(), profiled_priority}, BindOnce( &TaskTracker::RecordHeartbeatLatencyAndTasksRunWhileQueuingHistograms, - Unretained(task_tracker_), task_priority, may_block, TimeTicks::Now(), + Unretained(task_tracker_), profiled_priority, TimeTicks::Now(), task_tracker_->GetNumTasksRun())); } diff --git a/chromium/base/task/thread_pool/service_thread_unittest.cc b/chromium/base/task/thread_pool/service_thread_unittest.cc index ce098456877..7b3d3c21031 100644 --- a/chromium/base/task/thread_pool/service_thread_unittest.cc +++ b/chromium/base/task/thread_pool/service_thread_unittest.cc @@ -65,15 +65,9 @@ TEST(ThreadPoolServiceThreadIntegrationTest, HeartbeatLatencyReport) { "ThreadPool.HeartbeatLatencyMicroseconds.Test." "UserBlockingTaskPriority", "ThreadPool.HeartbeatLatencyMicroseconds.Test." - "UserBlockingTaskPriority_MayBlock", - "ThreadPool.HeartbeatLatencyMicroseconds.Test." "UserVisibleTaskPriority", "ThreadPool.HeartbeatLatencyMicroseconds.Test." - "UserVisibleTaskPriority_MayBlock", - "ThreadPool.HeartbeatLatencyMicroseconds.Test." - "BackgroundTaskPriority", - "ThreadPool.HeartbeatLatencyMicroseconds.Test." - "BackgroundTaskPriority_MayBlock"}; + "BackgroundTaskPriority"}; // Each report hits a single histogram above (randomly selected). But 1000 // reports should touch all histograms at least once the vast majority of the diff --git a/chromium/base/task/thread_pool/task_source.cc b/chromium/base/task/thread_pool/task_source.cc index 271988c2400..9674346b962 100644 --- a/chromium/base/task/thread_pool/task_source.cc +++ b/chromium/base/task/thread_pool/task_source.cc @@ -131,7 +131,6 @@ Optional<Task> RegisteredTaskSource::TakeTask( DCHECK(!transaction || transaction->task_source() == get()); #if DCHECK_IS_ON() DCHECK_EQ(State::kReady, run_step_); - run_step_ = State::kTaskAcquired; #endif // DCHECK_IS_ON() return task_source_->TakeTask(transaction); } @@ -139,9 +138,6 @@ Optional<Task> RegisteredTaskSource::TakeTask( Optional<Task> RegisteredTaskSource::Clear( TaskSource::Transaction* transaction) { DCHECK(!transaction || transaction->task_source() == get()); -#if DCHECK_IS_ON() - run_step_ = State::kInitial; -#endif // DCHECK_IS_ON() return task_source_->Clear(transaction); } @@ -149,7 +145,7 @@ bool RegisteredTaskSource::DidProcessTask( TaskSource::Transaction* transaction) { DCHECK(!transaction || transaction->task_source() == get()); #if DCHECK_IS_ON() - DCHECK_EQ(State::kTaskAcquired, run_step_); + DCHECK_EQ(State::kReady, run_step_); run_step_ = State::kInitial; #endif // DCHECK_IS_ON() return task_source_->DidProcessTask(transaction); diff --git a/chromium/base/task/thread_pool/task_source.h b/chromium/base/task/thread_pool/task_source.h index e56b42eda69..4dfa0875885 100644 --- a/chromium/base/task/thread_pool/task_source.h +++ b/chromium/base/task/thread_pool/task_source.h @@ -55,9 +55,9 @@ struct BASE_EXPORT ExecutionEnvironment { // RegisteredTaskSource after obtaining it from the queue: // 1- Check whether a task can run with WillRunTask() (and register/enqueue the // task source again if not saturated). -// 2- Iff (1) determined that a task can run, access the next task with -// TakeTask(). -// 3- Execute the task. +// 2- (optional) Iff (1) determined that a task can run, access the next task +// with TakeTask(). +// 3- (optional) Execute the task. // 4- Inform the task source that a task was processed with DidProcessTask(), // and re-enqueue the task source iff requested. // When a task source is registered multiple times, many overlapping chains of @@ -86,10 +86,10 @@ class BASE_EXPORT TaskSource : public RefCountedThreadSafe<TaskSource> { enum class RunStatus { // TakeTask() cannot be called. kDisallowed, - // TakeTask() must be called, and the TaskSource has not reached its maximum + // TakeTask() may called, and the TaskSource has not reached its maximum // concurrency (i.e. the TaskSource still needs to be queued). kAllowedNotSaturated, - // TakeTask() must be called, and the TaskSource has reached its maximum + // TakeTask() may called, and the TaskSource has reached its maximum // concurrency (i.e. the TaskSource no longer needs to be queued). kAllowedSaturated, }; @@ -218,7 +218,7 @@ class BASE_EXPORT TaskSource : public RefCountedThreadSafe<TaskSource> { // A pointer to the TaskRunner that posts to this TaskSource, if any. The // derived class is responsible for calling AddRef() when a TaskSource from // which no Task is executing becomes non-empty and Release() when - // DidProcessTask() returns false. + // it becomes empty again (e.g. when DidProcessTask() returns false). TaskRunner* task_runner_; TaskSourceExecutionMode execution_mode_; @@ -270,11 +270,11 @@ class BASE_EXPORT RegisteredTaskSource { Optional<Task> TakeTask(TaskSource::Transaction* transaction = nullptr) WARN_UNUSED_RESULT; - // Must be called once the task was run. This resets this RegisteredTaskSource - // to its initial state so that WillRunTask() may be called again. - // |transaction| is optional and should only be provided if this operation is - // already part of a transaction. Returns true if the TaskSource should be - // queued after this operation. + // Must be called after WillRunTask() or once the task was run if TakeTask() + // was called. This resets this RegisteredTaskSource to its initial state so + // that WillRunTask() may be called again. |transaction| is optional and + // should only be provided if this operation is already part of a transaction. + // Returns true if the TaskSource should be queued after this operation. bool DidProcessTask(TaskSource::Transaction* transaction = nullptr); // Returns a task that clears this TaskSource to make it empty. |transaction| @@ -293,7 +293,6 @@ class BASE_EXPORT RegisteredTaskSource { enum class State { kInitial, // WillRunTask() may be called. kReady, // After WillRunTask() returned a valid RunStatus. - kTaskAcquired, // After TakeTask(). }; State run_step_ = State::kInitial; diff --git a/chromium/base/task/thread_pool/task_tracker.cc b/chromium/base/task/thread_pool/task_tracker.cc index d21422bc9fa..a74f14003b3 100644 --- a/chromium/base/task/thread_pool/task_tracker.cc +++ b/chromium/base/task/thread_pool/task_tracker.cc @@ -20,6 +20,7 @@ #include "base/sequence_token.h" #include "base/synchronization/condition_variable.h" #include "base/task/scoped_set_task_priority_for_current_thread.h" +#include "base/task/task_executor.h" #include "base/threading/sequence_local_storage_map.h" #include "base/threading/sequenced_task_runner_handle.h" #include "base/threading/thread_restrictions.h" @@ -27,6 +28,7 @@ #include "base/time/time.h" #include "base/trace_event/trace_event.h" #include "base/values.h" +#include "build/build_config.h" namespace base { namespace internal { @@ -75,59 +77,34 @@ void TaskTracingInfo::AppendAsTraceFormat(std::string* out) const { out->append(tmp); } -// Constructs a histogram to track latency which is logging to -// "ThreadPool.{histogram_name}.{histogram_label}.{task_type_suffix}". -HistogramBase* GetLatencyHistogram(StringPiece histogram_name, - StringPiece histogram_label, - StringPiece task_type_suffix) { - DCHECK(!histogram_name.empty()); - DCHECK(!histogram_label.empty()); - DCHECK(!task_type_suffix.empty()); - // Mimics the UMA_HISTOGRAM_HIGH_RESOLUTION_CUSTOM_TIMES macro. The minimums - // and maximums were chosen to place the 1ms mark at around the 70% range - // coverage for buckets giving us good info for tasks that have a latency - // below 1ms (most of them) and enough info to assess how bad the latency is - // for tasks that exceed this threshold. - const std::string histogram = JoinString( - {"ThreadPool", histogram_name, histogram_label, task_type_suffix}, "."); - return Histogram::FactoryMicrosecondsTimeGet( - histogram, TimeDelta::FromMicroseconds(1), - TimeDelta::FromMilliseconds(20), 50, - HistogramBase::kUmaTargetedHistogramFlag); -} - -// Constructs a histogram to track task count which is logging to -// "ThreadPool.{histogram_name}.{histogram_label}.{task_type_suffix}". -HistogramBase* GetCountHistogram(StringPiece histogram_name, - StringPiece histogram_label, - StringPiece task_type_suffix) { - DCHECK(!histogram_name.empty()); - DCHECK(!histogram_label.empty()); - DCHECK(!task_type_suffix.empty()); - // Mimics the UMA_HISTOGRAM_CUSTOM_COUNTS macro. - const std::string histogram = JoinString( - {"ThreadPool", histogram_name, histogram_label, task_type_suffix}, "."); - // 500 was chosen as the maximum number of tasks run while queuing because - // values this high would likely indicate an error, beyond which knowing the - // actual number of tasks is not informative. - return Histogram::FactoryGet(histogram, 1, 500, 50, - HistogramBase::kUmaTargetedHistogramFlag); -} - -// Returns a histogram stored in a 2D array indexed by task priority and -// whether it may block. -// TODO(jessemckenna): use the STATIC_HISTOGRAM_POINTER_GROUP macro from -// histogram_macros.h instead. -HistogramBase* GetHistogramForTaskTraits( - TaskTraits task_traits, - HistogramBase* const (*histograms)[2]) { - return histograms[static_cast<int>(task_traits.priority())] - [task_traits.may_block() || - task_traits.with_base_sync_primitives() - ? 1 - : 0]; +const char* GetTaskPrioritySuffix(TaskPriority priority) { + switch (priority) { + case TaskPriority::BEST_EFFORT: + return "BackgroundTaskPriority"; + case TaskPriority::USER_VISIBLE: + return "UserVisibleTaskPriority"; + case TaskPriority::USER_BLOCKING: + return "UserBlockingTaskPriority"; + } } +// Records |time_sample| to the histogram |histogram_name|.|priority suffix|, +// where |priority_suffix| is derived from |priority|. +// +// The minimums and maximums were chosen to place the 1ms mark at around the 70% +// range coverage for buckets giving us good info for tasks that have a latency +// below 1ms (most of them) and enough info to assess how bad the latency is for +// tasks that exceed this threshold. +#define STATIC_LATENCY_HISTOGRAM_POINTER_GROUP(histogram_name, priority, \ + time_sample) \ + STATIC_HISTOGRAM_POINTER_GROUP( \ + histogram_name, static_cast<int>(priority), \ + static_cast<int>(TaskPriority::HIGHEST) + 1, AddTime(time_sample), \ + Histogram::FactoryMicrosecondsTimeGet( \ + histogram_name, TimeDelta::FromMicroseconds(1), \ + TimeDelta::FromMilliseconds(20), 50, \ + HistogramBase::kUmaTargetedHistogramFlag)); + bool HasLogBestEffortTasksSwitch() { // The CommandLine might not be initialized if ThreadPool is initialized in a // dynamic library which doesn't have access to argc/argv. @@ -136,6 +113,84 @@ bool HasLogBestEffortTasksSwitch() { switches::kLogBestEffortTasks); } +// Needed for PostTaskHere and CurrentThread. This executor lives for the +// duration of a threadpool task invocation. +class EphemeralTaskExecutor : public TaskExecutor { + public: + // |sequenced_task_runner| and |single_thread_task_runner| must outlive this + // EphemeralTaskExecutor. + EphemeralTaskExecutor(SequencedTaskRunner* sequenced_task_runner, + SingleThreadTaskRunner* single_thread_task_runner, + const TaskTraits* sequence_traits) + : sequenced_task_runner_(sequenced_task_runner), + single_thread_task_runner_(single_thread_task_runner), + sequence_traits_(sequence_traits) { + SetTaskExecutorForCurrentThread(this); + } + + ~EphemeralTaskExecutor() override { + SetTaskExecutorForCurrentThread(nullptr); + } + + // TaskExecutor: + bool PostDelayedTask(const Location& from_here, + const TaskTraits& traits, + OnceClosure task, + TimeDelta delay) override { + CheckTraitsCompatibleWithSequenceTraits(traits); + return sequenced_task_runner_->PostDelayedTask(from_here, std::move(task), + delay); + } + + scoped_refptr<TaskRunner> CreateTaskRunner( + const TaskTraits& traits) override { + CheckTraitsCompatibleWithSequenceTraits(traits); + return sequenced_task_runner_; + } + + scoped_refptr<SequencedTaskRunner> CreateSequencedTaskRunner( + const TaskTraits& traits) override { + CheckTraitsCompatibleWithSequenceTraits(traits); + return sequenced_task_runner_; + } + + scoped_refptr<SingleThreadTaskRunner> CreateSingleThreadTaskRunner( + const TaskTraits& traits, + SingleThreadTaskRunnerThreadMode thread_mode) override { + CheckTraitsCompatibleWithSequenceTraits(traits); + return single_thread_task_runner_; + } + +#if defined(OS_WIN) + scoped_refptr<SingleThreadTaskRunner> CreateCOMSTATaskRunner( + const TaskTraits& traits, + SingleThreadTaskRunnerThreadMode thread_mode) override { + CheckTraitsCompatibleWithSequenceTraits(traits); + return single_thread_task_runner_; + } +#endif // defined(OS_WIN) + + private: + // Currently ignores |traits.priority()|. + void CheckTraitsCompatibleWithSequenceTraits(const TaskTraits& traits) { + if (traits.shutdown_behavior_set_explicitly()) { + DCHECK_EQ(traits.shutdown_behavior(), + sequence_traits_->shutdown_behavior()); + } + + DCHECK(!traits.may_block() || + traits.may_block() == sequence_traits_->may_block()); + + DCHECK(!traits.with_base_sync_primitives() || + traits.with_base_sync_primitives() == + sequence_traits_->with_base_sync_primitives()); + } + + SequencedTaskRunner* const sequenced_task_runner_; + SingleThreadTaskRunner* const single_thread_task_runner_; + const TaskTraits* const sequence_traits_; +}; + } // namespace // Atomic internal state used by TaskTracker to track items that are blocking @@ -228,78 +283,14 @@ class TaskTracker::State { DISALLOW_COPY_AND_ASSIGN(State); }; -// TODO(jessemckenna): Write a helper function to avoid code duplication below. TaskTracker::TaskTracker(StringPiece histogram_label) - : has_log_best_effort_tasks_switch_(HasLogBestEffortTasksSwitch()), + : histogram_label_(histogram_label), + has_log_best_effort_tasks_switch_(HasLogBestEffortTasksSwitch()), state_(new State), can_run_policy_(CanRunPolicy::kAll), flush_cv_(flush_lock_.CreateConditionVariable()), shutdown_lock_(&flush_lock_), - task_latency_histograms_{ - {GetLatencyHistogram("TaskLatencyMicroseconds", - histogram_label, - "BackgroundTaskPriority"), - GetLatencyHistogram("TaskLatencyMicroseconds", - histogram_label, - "BackgroundTaskPriority_MayBlock")}, - {GetLatencyHistogram("TaskLatencyMicroseconds", - histogram_label, - "UserVisibleTaskPriority"), - GetLatencyHistogram("TaskLatencyMicroseconds", - histogram_label, - "UserVisibleTaskPriority_MayBlock")}, - {GetLatencyHistogram("TaskLatencyMicroseconds", - histogram_label, - "UserBlockingTaskPriority"), - GetLatencyHistogram("TaskLatencyMicroseconds", - histogram_label, - "UserBlockingTaskPriority_MayBlock")}}, - heartbeat_latency_histograms_{ - {GetLatencyHistogram("HeartbeatLatencyMicroseconds", - histogram_label, - "BackgroundTaskPriority"), - GetLatencyHistogram("HeartbeatLatencyMicroseconds", - histogram_label, - "BackgroundTaskPriority_MayBlock")}, - {GetLatencyHistogram("HeartbeatLatencyMicroseconds", - histogram_label, - "UserVisibleTaskPriority"), - GetLatencyHistogram("HeartbeatLatencyMicroseconds", - histogram_label, - "UserVisibleTaskPriority_MayBlock")}, - {GetLatencyHistogram("HeartbeatLatencyMicroseconds", - histogram_label, - "UserBlockingTaskPriority"), - GetLatencyHistogram("HeartbeatLatencyMicroseconds", - histogram_label, - "UserBlockingTaskPriority_MayBlock")}}, - num_tasks_run_while_queuing_histograms_{ - {GetCountHistogram("NumTasksRunWhileQueuing", - histogram_label, - "BackgroundTaskPriority"), - GetCountHistogram("NumTasksRunWhileQueuing", - histogram_label, - "BackgroundTaskPriority_MayBlock")}, - {GetCountHistogram("NumTasksRunWhileQueuing", - histogram_label, - "UserVisibleTaskPriority"), - GetCountHistogram("NumTasksRunWhileQueuing", - histogram_label, - "UserVisibleTaskPriority_MayBlock")}, - {GetCountHistogram("NumTasksRunWhileQueuing", - histogram_label, - "UserBlockingTaskPriority"), - GetCountHistogram("NumTasksRunWhileQueuing", - histogram_label, - "UserBlockingTaskPriority_MayBlock")}}, - tracked_ref_factory_(this) { - // Confirm that all |task_latency_histograms_| have been initialized above. - for (TaskPriorityType i = 0; i < kNumTaskPriorities; ++i) { - for (uint8_t j = 0; j < kNumBlockingModes; ++j) { - DCHECK(task_latency_histograms_[i][j]); - } - } -} + tracked_ref_factory_(this) {} TaskTracker::~TaskTracker() = default; @@ -441,7 +432,7 @@ RegisteredTaskSource TaskTracker::RunAndPopNextTask( RegisteredTaskSource task_source) { DCHECK(task_source); - const bool can_run_worker_task = + const bool task_is_worker_task = BeforeRunTask(task_source->shutdown_behavior()); // Run the next task in |task_source|. @@ -449,7 +440,7 @@ RegisteredTaskSource TaskTracker::RunAndPopNextTask( TaskTraits traits{ThreadPool()}; { auto transaction = task_source->BeginTransaction(); - task = can_run_worker_task ? task_source.TakeTask(&transaction) + task = task_is_worker_task ? task_source.TakeTask(&transaction) : task_source.Clear(&transaction); traits = transaction.traits(); } @@ -458,13 +449,12 @@ RegisteredTaskSource TaskTracker::RunAndPopNextTask( // Run the |task| (whether it's a worker task or the Clear() closure). RunTask(std::move(task.value()), task_source.get(), traits); } - if (can_run_worker_task) { + if (task_is_worker_task) AfterRunTask(task_source->shutdown_behavior()); - const bool task_source_must_be_queued = task_source.DidProcessTask(); - // |task_source| should be reenqueued iff requested by DidProcessTask(). - if (task_source_must_be_queued) - return task_source; - } + const bool task_source_must_be_queued = task_source.DidProcessTask(); + // |task_source| should be reenqueued iff requested by DidProcessTask(). + if (task_source_must_be_queued) + return task_source; return nullptr; } @@ -477,37 +467,50 @@ bool TaskTracker::IsShutdownComplete() const { return shutdown_event_ && shutdown_event_->IsSignaled(); } -void TaskTracker::RecordLatencyHistogram( - LatencyHistogramType latency_histogram_type, - TaskTraits task_traits, - TimeTicks posted_time) const { - const TimeDelta task_latency = TimeTicks::Now() - posted_time; +void TaskTracker::RecordLatencyHistogram(TaskPriority priority, + TimeTicks posted_time) const { + if (histogram_label_.empty()) + return; - DCHECK(latency_histogram_type == LatencyHistogramType::TASK_LATENCY || - latency_histogram_type == LatencyHistogramType::HEARTBEAT_LATENCY); - const auto& histograms = - latency_histogram_type == LatencyHistogramType::TASK_LATENCY - ? task_latency_histograms_ - : heartbeat_latency_histograms_; - GetHistogramForTaskTraits(task_traits, histograms) - ->AddTimeMicrosecondsGranularity(task_latency); + auto get_latency_histogram_name = [this, priority]() { + return JoinString({"ThreadPool.TaskLatencyMicroseconds", histogram_label_, + GetTaskPrioritySuffix(priority)}, + "."); + }; + STATIC_LATENCY_HISTOGRAM_POINTER_GROUP(get_latency_histogram_name(), priority, + TimeTicks::Now() - posted_time); } void TaskTracker::RecordHeartbeatLatencyAndTasksRunWhileQueuingHistograms( - TaskPriority task_priority, - bool may_block, + TaskPriority priority, TimeTicks posted_time, int num_tasks_run_when_posted) const { - TaskTraits task_traits{ThreadPool()}; - if (may_block) - task_traits = TaskTraits(ThreadPool(), task_priority, MayBlock()); - else - task_traits = TaskTraits(ThreadPool(), task_priority); - RecordLatencyHistogram(LatencyHistogramType::HEARTBEAT_LATENCY, task_traits, - posted_time); - GetHistogramForTaskTraits(task_traits, - num_tasks_run_while_queuing_histograms_) - ->Add(GetNumTasksRun() - num_tasks_run_when_posted); + if (histogram_label_.empty()) + return; + + auto get_heartbeat_latency_histogram_name = [this, priority]() { + return JoinString({"ThreadPool.HeartbeatLatencyMicroseconds", + histogram_label_, GetTaskPrioritySuffix(priority)}, + "."); + }; + STATIC_LATENCY_HISTOGRAM_POINTER_GROUP(get_heartbeat_latency_histogram_name(), + priority, + TimeTicks::Now() - posted_time); + + auto get_num_tasks_run_while_queuing_histogram_name = [this, priority]() { + return JoinString({"ThreadPool.NumTasksRunWhileQueuing", histogram_label_, + GetTaskPrioritySuffix(priority)}, + "."); + }; + STATIC_HISTOGRAM_POINTER_GROUP( + get_num_tasks_run_while_queuing_histogram_name(), + static_cast<int>(priority), static_cast<int>(TaskPriority::HIGHEST) + 1, + Add(GetNumTasksRun() - num_tasks_run_when_posted), + // 500 was chosen as the maximum number of tasks run while queuing because + // values this high would likely indicate an error, beyond which knowing + // the actual number of tasks is not informative. + Histogram::FactoryGet(get_num_tasks_run_while_queuing_histogram_name(), 1, + 500, 50, HistogramBase::kUmaTargetedHistogramFlag)); } int TaskTracker::GetNumTasksRun() const { @@ -522,8 +525,7 @@ void TaskTracker::RunTask(Task task, TaskSource* task_source, const TaskTraits& traits) { DCHECK(task_source); - RecordLatencyHistogram(LatencyHistogramType::TASK_LATENCY, traits, - task.queue_time); + RecordLatencyHistogram(traits.priority(), task.queue_time); const auto environment = task_source->GetExecutionEnvironment(); @@ -557,6 +559,7 @@ void TaskTracker::RunTask(Task task, // Set up TaskRunnerHandle as expected for the scope of the task. Optional<SequencedTaskRunnerHandle> sequenced_task_runner_handle; Optional<ThreadTaskRunnerHandle> single_thread_task_runner_handle; + Optional<EphemeralTaskExecutor> ephemiral_task_executor; switch (task_source->execution_mode()) { case TaskSourceExecutionMode::kJob: case TaskSourceExecutionMode::kParallel: @@ -565,11 +568,18 @@ void TaskTracker::RunTask(Task task, DCHECK(task_source->task_runner()); sequenced_task_runner_handle.emplace( static_cast<SequencedTaskRunner*>(task_source->task_runner())); + ephemiral_task_executor.emplace( + static_cast<SequencedTaskRunner*>(task_source->task_runner()), + nullptr, &traits); break; case TaskSourceExecutionMode::kSingleThread: DCHECK(task_source->task_runner()); single_thread_task_runner_handle.emplace( static_cast<SingleThreadTaskRunner*>(task_source->task_runner())); + ephemiral_task_executor.emplace( + static_cast<SequencedTaskRunner*>(task_source->task_runner()), + static_cast<SingleThreadTaskRunner*>(task_source->task_runner()), + &traits); break; } diff --git a/chromium/base/task/thread_pool/task_tracker.h b/chromium/base/task/thread_pool/task_tracker.h index bb5296b7da1..59f0726b8eb 100644 --- a/chromium/base/task/thread_pool/task_tracker.h +++ b/chromium/base/task/thread_pool/task_tracker.h @@ -31,7 +31,6 @@ namespace base { class ConditionVariable; -class HistogramBase; namespace internal { @@ -54,7 +53,8 @@ enum class CanRunPolicy { // and records metrics and trace events. This class is thread-safe. class BASE_EXPORT TaskTracker { public: - // |histogram_label| is used as a suffix for histograms, it must not be empty. + // |histogram_label| is used to label histograms. No histograms are recorded + // if it is empty. TaskTracker(StringPiece histogram_label); virtual ~TaskTracker(); @@ -131,26 +131,15 @@ class BASE_EXPORT TaskTracker { // no tasks are blocking shutdown). bool IsShutdownComplete() const; - enum class LatencyHistogramType { - // Records the latency of each individual task posted through TaskTracker. - TASK_LATENCY, - // Records the latency of heartbeat tasks which are independent of current - // workload. These avoid a bias towards TASK_LATENCY reporting that high- - // priority tasks are "slower" than regular tasks because high-priority - // tasks tend to be correlated with heavy workloads. - HEARTBEAT_LATENCY, - }; - // Records two histograms // 1. ThreadPool.[label].HeartbeatLatencyMicroseconds.[suffix]: // Now() - posted_time // 2. ThreadPool.[label].NumTasksRunWhileQueuing.[suffix]: // GetNumTasksRun() - num_tasks_run_when_posted. // [label] is the histogram label provided to the constructor. - // [suffix] is derived from |task_priority| and |may_block|. + // [suffix] is derived from |task_priority|. void RecordHeartbeatLatencyAndTasksRunWhileQueuingHistograms( TaskPriority task_priority, - bool may_block, TimeTicks posted_time, int num_tasks_run_when_posted) const; @@ -213,10 +202,9 @@ class BASE_EXPORT TaskTracker { // manner. void CallFlushCallbackForTesting(); - // Records |Now() - posted_time| to the appropriate |latency_histogram_type| - // based on |task_traits|. - void RecordLatencyHistogram(LatencyHistogramType latency_histogram_type, - TaskTraits task_traits, + // Records |Now() - posted_time| to the + // ThreadPool.TaskLatencyMicroseconds.[label].[priority] histogram. + void RecordLatencyHistogram(TaskPriority priority, TimeTicks posted_time) const; void IncrementNumTasksRun(); @@ -230,6 +218,9 @@ class BASE_EXPORT TaskTracker { TaskAnnotator task_annotator_; + // Suffix for histograms recorded by this TaskTracker. + const std::string histogram_label_; + // Indicates whether logging information about TaskPriority::BEST_EFFORT tasks // was enabled with a command line switch. const bool has_log_best_effort_tasks_switch_; @@ -277,25 +268,6 @@ class BASE_EXPORT TaskTracker { // a task queued to histogram. std::atomic_int num_tasks_run_{0}; - // ThreadPool.TaskLatencyMicroseconds.*, - // ThreadPool.HeartbeatLatencyMicroseconds.*, and - // ThreadPool.NumTasksRunWhileQueuing.* histograms. The first index is - // a TaskPriority. The second index is 0 for non-blocking tasks, 1 for - // blocking tasks. Intentionally leaked. - // TODO(scheduler-dev): Consider using STATIC_HISTOGRAM_POINTER_GROUP for - // these. - using TaskPriorityType = std::underlying_type<TaskPriority>::type; - static constexpr TaskPriorityType kNumTaskPriorities = - static_cast<TaskPriorityType>(TaskPriority::HIGHEST) + 1; - static constexpr uint8_t kNumBlockingModes = 2; - HistogramBase* const task_latency_histograms_[kNumTaskPriorities] - [kNumBlockingModes]; - HistogramBase* const heartbeat_latency_histograms_[kNumTaskPriorities] - [kNumBlockingModes]; - HistogramBase* const - num_tasks_run_while_queuing_histograms_[kNumTaskPriorities] - [kNumBlockingModes]; - // Ensures all state (e.g. dangling cleaned up workers) is coalesced before // destroying the TaskTracker (e.g. in test environments). // Ref. https://crbug.com/827615. diff --git a/chromium/base/task/thread_pool/task_tracker_unittest.cc b/chromium/base/task/thread_pool/task_tracker_unittest.cc index fa231c9b7be..a3ba41cf559 100644 --- a/chromium/base/task/thread_pool/task_tracker_unittest.cc +++ b/chromium/base/task/thread_pool/task_tracker_unittest.cc @@ -19,7 +19,6 @@ #include "base/memory/ref_counted.h" #include "base/metrics/histogram_base.h" #include "base/metrics/histogram_samples.h" -#include "base/metrics/statistics_recorder.h" #include "base/sequence_token.h" #include "base/sequenced_task_runner.h" #include "base/single_thread_task_runner.h" @@ -37,6 +36,7 @@ #include "base/threading/scoped_blocking_call.h" #include "base/threading/sequenced_task_runner_handle.h" #include "base/threading/simple_thread.h" +#include "base/threading/thread_restrictions.h" #include "base/threading/thread_task_runner_handle.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -1244,8 +1244,6 @@ TEST(ThreadPoolTaskTrackerWaitAllowedTest, WaitAllowed) { // Verify that ThreadPool.TaskLatency.* histograms are correctly recorded // when a task runs. TEST(ThreadPoolTaskTrackerHistogramTest, TaskLatency) { - auto statistics_recorder = StatisticsRecorder::CreateTemporaryForTesting(); - TaskTracker tracker("Test"); struct { @@ -1257,28 +1255,28 @@ TEST(ThreadPoolTaskTrackerHistogramTest, TaskLatency) { "BackgroundTaskPriority"}, {{ThreadPool(), MayBlock(), TaskPriority::BEST_EFFORT}, "ThreadPool.TaskLatencyMicroseconds.Test." - "BackgroundTaskPriority_MayBlock"}, + "BackgroundTaskPriority"}, {{ThreadPool(), WithBaseSyncPrimitives(), TaskPriority::BEST_EFFORT}, "ThreadPool.TaskLatencyMicroseconds.Test." - "BackgroundTaskPriority_MayBlock"}, + "BackgroundTaskPriority"}, {{ThreadPool(), TaskPriority::USER_VISIBLE}, "ThreadPool.TaskLatencyMicroseconds.Test." "UserVisibleTaskPriority"}, {{ThreadPool(), MayBlock(), TaskPriority::USER_VISIBLE}, "ThreadPool.TaskLatencyMicroseconds.Test." - "UserVisibleTaskPriority_MayBlock"}, + "UserVisibleTaskPriority"}, {{ThreadPool(), WithBaseSyncPrimitives(), TaskPriority::USER_VISIBLE}, "ThreadPool.TaskLatencyMicroseconds.Test." - "UserVisibleTaskPriority_MayBlock"}, + "UserVisibleTaskPriority"}, {{ThreadPool(), TaskPriority::USER_BLOCKING}, "ThreadPool.TaskLatencyMicroseconds.Test." "UserBlockingTaskPriority"}, {{ThreadPool(), MayBlock(), TaskPriority::USER_BLOCKING}, "ThreadPool.TaskLatencyMicroseconds.Test." - "UserBlockingTaskPriority_MayBlock"}, + "UserBlockingTaskPriority"}, {{ThreadPool(), WithBaseSyncPrimitives(), TaskPriority::USER_BLOCKING}, "ThreadPool.TaskLatencyMicroseconds.Test." - "UserBlockingTaskPriority_MayBlock"}}; + "UserBlockingTaskPriority"}}; for (const auto& test : kTests) { Task task(FROM_HERE, DoNothing(), TimeDelta()); diff --git a/chromium/base/task/thread_pool/test_utils.cc b/chromium/base/task/thread_pool/test_utils.cc index a02e24bf3a6..d6236373f84 100644 --- a/chromium/base/task/thread_pool/test_utils.cc +++ b/chromium/base/task/thread_pool/test_utils.cc @@ -239,6 +239,11 @@ bool MockPooledTaskRunnerDelegate::EnqueueJobTaskSource( return true; } +void MockPooledTaskRunnerDelegate::RemoveJobTaskSource( + scoped_refptr<JobTaskSource> task_source) { + thread_group_->RemoveTaskSource(*task_source); +} + bool MockPooledTaskRunnerDelegate::IsRunningPoolWithTraits( const TaskTraits& traits) const { // |thread_group_| must be initialized with SetThreadGroup() before diff --git a/chromium/base/task/thread_pool/test_utils.h b/chromium/base/task/thread_pool/test_utils.h index 1d85ee8cea2..ebdfbe28467 100644 --- a/chromium/base/task/thread_pool/test_utils.h +++ b/chromium/base/task/thread_pool/test_utils.h @@ -62,6 +62,7 @@ class MockPooledTaskRunnerDelegate : public PooledTaskRunnerDelegate { bool PostTaskWithSequence(Task task, scoped_refptr<Sequence> sequence) override; bool EnqueueJobTaskSource(scoped_refptr<JobTaskSource> task_source) override; + void RemoveJobTaskSource(scoped_refptr<JobTaskSource> task_source) override; bool ShouldYield(const TaskSource* task_source) const override; bool IsRunningPoolWithTraits(const TaskTraits& traits) const override; void UpdatePriority(scoped_refptr<TaskSource> task_source, diff --git a/chromium/base/task/thread_pool/thread_group.cc b/chromium/base/task/thread_pool/thread_group.cc index 7f6dcef2630..8de5b518fde 100644 --- a/chromium/base/task/thread_pool/thread_group.cc +++ b/chromium/base/task/thread_pool/thread_group.cc @@ -139,9 +139,9 @@ ThreadGroup::GetNumAdditionalWorkersForForegroundTaskSourcesLockRequired() } RegisteredTaskSource ThreadGroup::RemoveTaskSource( - scoped_refptr<TaskSource> task_source) { + const TaskSource& task_source) { CheckedAutoLock auto_lock(lock_); - return priority_queue_.RemoveTaskSource(std::move(task_source)); + return priority_queue_.RemoveTaskSource(task_source); } void ThreadGroup::ReEnqueueTaskSourceLockRequired( diff --git a/chromium/base/task/thread_pool/thread_group.h b/chromium/base/task/thread_pool/thread_group.h index c00755413f5..4db7bd5f66d 100644 --- a/chromium/base/task/thread_pool/thread_group.h +++ b/chromium/base/task/thread_pool/thread_group.h @@ -65,7 +65,7 @@ class BASE_EXPORT ThreadGroup { // RegisteredTaskSource that evaluats to true if successful, or false if // |task_source| is not currently in |priority_queue_|, such as when a worker // is running a task from it. - RegisteredTaskSource RemoveTaskSource(scoped_refptr<TaskSource> task_source); + RegisteredTaskSource RemoveTaskSource(const TaskSource& task_source); // Updates the position of the TaskSource in |transaction| in this // ThreadGroup's PriorityQueue based on the TaskSource's current traits. diff --git a/chromium/base/task/thread_pool/thread_group_impl.cc b/chromium/base/task/thread_pool/thread_group_impl.cc index 378493afc78..0c17b47d31b 100644 --- a/chromium/base/task/thread_pool/thread_group_impl.cc +++ b/chromium/base/task/thread_pool/thread_group_impl.cc @@ -41,25 +41,6 @@ #include "base/win/windows_version.h" #endif // defined(OS_WIN) -// Data from deprecated UMA histograms: -// -// ThreadPool.NumTasksBetweenWaits.(Browser/Renderer).Foreground, August 2019 -// Number of tasks between two waits by a foreground worker thread in a -// browser/renderer process. -// -// Windows (browser/renderer) -// 1 at 87th percentile / 92th percentile -// 2 at 95th percentile / 98th percentile -// 5 at 99th percentile / 100th percentile -// Mac (browser/renderer) -// 1 at 81th percentile / 90th percentile -// 2 at 92th percentile / 97th percentile -// 5 at 98th percentile / 100th percentile -// Android (browser/renderer) -// 1 at 92th percentile / 96th percentile -// 2 at 97th percentile / 98th percentile -// 5 at 99th percentile / 100th percentile - namespace base { namespace internal { @@ -342,40 +323,57 @@ ThreadGroupImpl::ThreadGroupImpl(StringPiece histogram_label, priority_hint_(priority_hint), idle_workers_stack_cv_for_testing_(lock_.CreateConditionVariable()), // Mimics the UMA_HISTOGRAM_LONG_TIMES macro. - detach_duration_histogram_(Histogram::FactoryTimeGet( - JoinString({kDetachDurationHistogramPrefix, histogram_label}, ""), - TimeDelta::FromMilliseconds(1), - TimeDelta::FromHours(1), - 50, - HistogramBase::kUmaTargetedHistogramFlag)), + detach_duration_histogram_( + histogram_label.empty() + ? nullptr + : Histogram::FactoryTimeGet( + JoinString( + {kDetachDurationHistogramPrefix, histogram_label}, + ""), + TimeDelta::FromMilliseconds(1), + TimeDelta::FromHours(1), + 50, + HistogramBase::kUmaTargetedHistogramFlag)), // Mimics the UMA_HISTOGRAM_COUNTS_1000 macro. When a worker runs more // than 1000 tasks before detaching, there is no need to know the exact // number of tasks that ran. - num_tasks_before_detach_histogram_(Histogram::FactoryGet( - JoinString({kNumTasksBeforeDetachHistogramPrefix, histogram_label}, - ""), - 1, - 1000, - 50, - HistogramBase::kUmaTargetedHistogramFlag)), + num_tasks_before_detach_histogram_( + histogram_label.empty() + ? nullptr + : Histogram::FactoryGet( + JoinString( + {kNumTasksBeforeDetachHistogramPrefix, histogram_label}, + ""), + 1, + 1000, + 50, + HistogramBase::kUmaTargetedHistogramFlag)), // Mimics the UMA_HISTOGRAM_COUNTS_100 macro. A ThreadGroup is // expected to run between zero and a few tens of workers. // When it runs more than 100 worker, there is no need to know the exact // number of workers that ran. - num_workers_histogram_(Histogram::FactoryGet( - JoinString({kNumWorkersHistogramPrefix, histogram_label}, ""), - 1, - 100, - 50, - HistogramBase::kUmaTargetedHistogramFlag)), - num_active_workers_histogram_(Histogram::FactoryGet( - JoinString({kNumActiveWorkersHistogramPrefix, histogram_label}, ""), - 1, - 100, - 50, - HistogramBase::kUmaTargetedHistogramFlag)), + num_workers_histogram_( + histogram_label.empty() + ? nullptr + : Histogram::FactoryGet( + JoinString({kNumWorkersHistogramPrefix, histogram_label}, + ""), + 1, + 100, + 50, + HistogramBase::kUmaTargetedHistogramFlag)), + num_active_workers_histogram_( + histogram_label.empty() + ? nullptr + : Histogram::FactoryGet( + JoinString( + {kNumActiveWorkersHistogramPrefix, histogram_label}, + ""), + 1, + 100, + 50, + HistogramBase::kUmaTargetedHistogramFlag)), tracked_ref_factory_(this) { - DCHECK(!histogram_label.empty()); DCHECK(!thread_group_label_.empty()); } @@ -484,10 +482,6 @@ void ThreadGroupImpl::WaitForWorkersCleanedUpForTesting(size_t n) { } void ThreadGroupImpl::JoinForTesting() { -#if DCHECK_IS_ON() - join_for_testing_started_.Set(); -#endif - decltype(workers_) workers_copy; { CheckedAutoLock auto_lock(lock_); @@ -496,6 +490,8 @@ void ThreadGroupImpl::JoinForTesting() { DCHECK_GT(workers_.size(), size_t(0)) << "Joined an unstarted thread group."; + join_for_testing_started_ = true; + // Ensure WorkerThreads in |workers_| do not attempt to cleanup while // being joined. worker_cleanup_disallowed_for_testing_ = true; @@ -531,10 +527,13 @@ size_t ThreadGroupImpl::NumberOfIdleWorkersForTesting() const { void ThreadGroupImpl::ReportHeartbeatMetrics() const { CheckedAutoLock auto_lock(lock_); - num_workers_histogram_->Add(workers_.size()); - - num_active_workers_histogram_->Add(workers_.size() - - idle_workers_stack_.Size()); + if (num_workers_histogram_) { + num_workers_histogram_->Add(workers_.size()); + } + if (num_active_workers_histogram_) { + num_active_workers_histogram_->Add(workers_.size() - + idle_workers_stack_.Size()); + } } ThreadGroupImpl::WorkerThreadDelegateImpl::WorkerThreadDelegateImpl( @@ -710,10 +709,13 @@ bool ThreadGroupImpl::WorkerThreadDelegateImpl::CanCleanupLockRequired( void ThreadGroupImpl::WorkerThreadDelegateImpl::CleanupLockRequired( WorkerThread* worker) { + DCHECK(!outer_->join_for_testing_started_); DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_); - outer_->num_tasks_before_detach_histogram_->Add( - worker_only().num_tasks_since_last_detach); + if (outer_->num_tasks_before_detach_histogram_) { + outer_->num_tasks_before_detach_histogram_->Add( + worker_only().num_tasks_since_last_detach); + } outer_->cleanup_timestamps_.push(subtle::TimeTicksNowIgnoringOverride()); worker->Cleanup(); outer_->idle_workers_stack_.Remove(worker); @@ -756,7 +758,7 @@ void ThreadGroupImpl::WorkerThreadDelegateImpl::OnMainExit( // |workers_| by the time the thread is about to exit. (except in the cases // where the thread group is no longer going to be used - in which case, // it's fine for there to be invalid workers in the thread group. - if (!shutdown_complete && !outer_->join_for_testing_started_.IsSet()) { + if (!shutdown_complete && !outer_->join_for_testing_started_) { DCHECK(!outer_->idle_workers_stack_.Contains(worker)); DCHECK(!ContainsWorker(outer_->workers_, worker)); } @@ -944,6 +946,7 @@ void ThreadGroupImpl::MaintainAtLeastOneIdleWorkerLockRequired( scoped_refptr<WorkerThread> ThreadGroupImpl::CreateAndRegisterWorkerLockRequired( ScopedWorkersExecutor* executor) { + DCHECK(!join_for_testing_started_); DCHECK_LT(workers_.size(), max_tasks_); DCHECK_LT(workers_.size(), kMaxNumberOfWorkers); DCHECK(idle_workers_stack_.IsEmpty()); @@ -962,8 +965,10 @@ ThreadGroupImpl::CreateAndRegisterWorkerLockRequired( DCHECK_LE(workers_.size(), max_tasks_); if (!cleanup_timestamps_.empty()) { - detach_duration_histogram_->AddTime(subtle::TimeTicksNowIgnoringOverride() - - cleanup_timestamps_.top()); + if (detach_duration_histogram_) { + detach_duration_histogram_->AddTime( + subtle::TimeTicksNowIgnoringOverride() - cleanup_timestamps_.top()); + } cleanup_timestamps_.pop(); } @@ -1011,7 +1016,7 @@ void ThreadGroupImpl::DidUpdateCanRunPolicy() { void ThreadGroupImpl::EnsureEnoughWorkersLockRequired( BaseScopedWorkersExecutor* base_executor) { // Don't do anything if the thread group isn't started. - if (max_tasks_ == 0) + if (max_tasks_ == 0 || UNLIKELY(join_for_testing_started_)) return; ScopedWorkersExecutor* executor = diff --git a/chromium/base/task/thread_pool/thread_group_impl.h b/chromium/base/task/thread_pool/thread_group_impl.h index 3df96cfdef0..e0782bf2614 100644 --- a/chromium/base/task/thread_pool/thread_group_impl.h +++ b/chromium/base/task/thread_pool/thread_group_impl.h @@ -20,7 +20,6 @@ #include "base/memory/ref_counted.h" #include "base/optional.h" #include "base/strings/string_piece.h" -#include "base/synchronization/atomic_flag.h" #include "base/synchronization/condition_variable.h" #include "base/synchronization/waitable_event.h" #include "base/task/thread_pool/task.h" @@ -329,10 +328,8 @@ class BASE_EXPORT ThreadGroupImpl : public ThreadGroup { std::unique_ptr<ConditionVariable> num_workers_cleaned_up_for_testing_cv_ GUARDED_BY(lock_); -#if DCHECK_IS_ON() // Set at the start of JoinForTesting(). - AtomicFlag join_for_testing_started_; -#endif + bool join_for_testing_started_ GUARDED_BY(lock_) = false; // ThreadPool.DetachDuration.[thread group name] histogram. Intentionally // leaked. diff --git a/chromium/base/task/thread_pool/thread_group_unittest.cc b/chromium/base/task/thread_pool/thread_group_unittest.cc index cb4481c3cf1..3ed430a60db 100644 --- a/chromium/base/task/thread_pool/thread_group_unittest.cc +++ b/chromium/base/task/thread_pool/thread_group_unittest.cc @@ -53,6 +53,7 @@ using ThreadGroupNativeType = #endif constexpr size_t kMaxTasks = 4; +constexpr size_t kTooManyTasks = 1000; // By default, tests allow half of the thread group to be used by best-effort // tasks. constexpr size_t kMaxBestEffortTasks = kMaxTasks / 2; @@ -654,6 +655,50 @@ TEST_P(ThreadGroupTest, ScheduleJobTaskSourceMultipleTime) { task_tracker_.FlushForTesting(); } +// Verify that Cancel() on a job stops running the worker task and causes +// current workers to yield. +TEST_P(ThreadGroupTest, CancelJobTaskSource) { + StartThreadGroup(); + + CheckedLock tasks_running_lock; + std::unique_ptr<ConditionVariable> tasks_running_cv = + tasks_running_lock.CreateConditionVariable(); + bool tasks_running = false; + + // Schedule a big number of tasks. + auto job_task = base::MakeRefCounted<test::MockJobTask>( + BindLambdaForTesting([&](experimental::JobDelegate* delegate) { + { + CheckedAutoLock auto_lock(tasks_running_lock); + tasks_running = true; + } + tasks_running_cv->Signal(); + + while (!delegate->ShouldYield()) { + } + }), + /* num_tasks_to_run */ kTooManyTasks); + scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource( + FROM_HERE, {ThreadPool()}, &mock_pooled_task_runner_delegate_); + + mock_pooled_task_runner_delegate_.EnqueueJobTaskSource(task_source); + experimental::JobHandle job_handle = + internal::JobTaskSource::CreateJobHandle(task_source); + + // Wait for at least 1 task to start running. + { + CheckedAutoLock auto_lock(tasks_running_lock); + while (!tasks_running) + tasks_running_cv->Wait(); + } + + // Cancels pending tasks and unblocks running ones. + job_handle.Cancel(); + + // This should not block since the job got cancelled. + task_tracker_.FlushForTesting(); +} + // Verify that calling JobTaskSource::NotifyConcurrencyIncrease() (re-)schedule // tasks with the intended concurrency. TEST_P(ThreadGroupTest, JobTaskSourceConcurrencyIncrease) { @@ -733,6 +778,37 @@ TEST_P(ThreadGroupTest, ScheduleEmptyJobTaskSource) { task_tracker_.FlushForTesting(); } +// Verify that Join() on a job contributes to max concurrency and waits for all +// workers to return. +TEST_P(ThreadGroupTest, JoinJobTaskSource) { + StartThreadGroup(); + + WaitableEvent threads_continue; + RepeatingClosure threads_continue_barrier = BarrierClosure( + kMaxTasks + 1, + BindOnce(&WaitableEvent::Signal, Unretained(&threads_continue))); + + auto job_task = base::MakeRefCounted<test::MockJobTask>( + BindLambdaForTesting([&](experimental::JobDelegate*) { + threads_continue_barrier.Run(); + test::WaitWithoutBlockingObserver(&threads_continue); + }), + /* num_tasks_to_run */ kMaxTasks + 1); + scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource( + FROM_HERE, {ThreadPool()}, &mock_pooled_task_runner_delegate_); + + mock_pooled_task_runner_delegate_.EnqueueJobTaskSource(task_source); + experimental::JobHandle job_handle = + internal::JobTaskSource::CreateJobHandle(task_source); + job_handle.Join(); + // All worker tasks should complete before Join() returns. + EXPECT_EQ(0U, job_task->GetMaxConcurrency()); + thread_group_->JoinForTesting(); + EXPECT_EQ(1U, task_source->HasOneRef()); + // Prevent TearDown() from calling JoinForTesting() again. + thread_group_ = nullptr; +} + // Verify that the maximum number of BEST_EFFORT tasks that can run concurrently // in a thread group does not affect JobTaskSource with a priority that was // increased from BEST_EFFORT to USER_BLOCKING. diff --git a/chromium/base/task/thread_pool/thread_pool_impl.cc b/chromium/base/task/thread_pool/thread_pool_impl.cc index 64d580d08ad..ff1bfa78211 100644 --- a/chromium/base/task/thread_pool/thread_pool_impl.cc +++ b/chromium/base/task/thread_pool/thread_pool_impl.cc @@ -77,20 +77,23 @@ ThreadPoolImpl::ThreadPoolImpl(StringPiece histogram_label, &delayed_task_manager_), has_disable_best_effort_switch_(HasDisableBestEffortTasksSwitch()), tracked_ref_factory_(this) { - DCHECK(!histogram_label.empty()); - foreground_thread_group_ = std::make_unique<ThreadGroupImpl>( - JoinString( - {histogram_label, kForegroundPoolEnvironmentParams.name_suffix}, "."), + histogram_label.empty() + ? std::string() + : JoinString( + {histogram_label, kForegroundPoolEnvironmentParams.name_suffix}, + "."), kForegroundPoolEnvironmentParams.name_suffix, kForegroundPoolEnvironmentParams.priority_hint, task_tracker_->GetTrackedRef(), tracked_ref_factory_.GetTrackedRef()); if (CanUseBackgroundPriorityForWorkerThread()) { background_thread_group_ = std::make_unique<ThreadGroupImpl>( - JoinString( - {histogram_label, kBackgroundPoolEnvironmentParams.name_suffix}, - "."), + histogram_label.empty() + ? std::string() + : JoinString({histogram_label, + kBackgroundPoolEnvironmentParams.name_suffix}, + "."), kBackgroundPoolEnvironmentParams.name_suffix, kBackgroundPoolEnvironmentParams.priority_hint, task_tracker_->GetTrackedRef(), tracked_ref_factory_.GetTrackedRef()); @@ -423,6 +426,14 @@ bool ThreadPoolImpl::EnqueueJobTaskSource( return true; } +void ThreadPoolImpl::RemoveJobTaskSource( + scoped_refptr<JobTaskSource> task_source) { + auto transaction = task_source->BeginTransaction(); + ThreadGroup* const current_thread_group = + GetThreadGroupForTraits(transaction.traits()); + current_thread_group->RemoveTaskSource(*task_source); +} + bool ThreadPoolImpl::IsRunningPoolWithTraits(const TaskTraits& traits) const { return GetThreadGroupForTraits(traits)->IsBoundToCurrentThread(); } @@ -455,7 +466,7 @@ void ThreadPoolImpl::UpdatePriority(scoped_refptr<TaskSource> task_source, // |task_source| is changing thread groups; remove it from its current // thread group and reenqueue it. auto registered_task_source = - current_thread_group->RemoveTaskSource(task_source); + current_thread_group->RemoveTaskSource(*task_source); if (registered_task_source) { DCHECK(task_source); new_thread_group->PushTaskSourceAndWakeUpWorkers( diff --git a/chromium/base/task/thread_pool/thread_pool_impl.h b/chromium/base/task/thread_pool/thread_pool_impl.h index 049ee6de692..8b3a6760ce9 100644 --- a/chromium/base/task/thread_pool/thread_pool_impl.h +++ b/chromium/base/task/thread_pool/thread_pool_impl.h @@ -60,8 +60,8 @@ class BASE_EXPORT ThreadPoolImpl : public ThreadPoolInstance, TaskTracker; #endif - // Creates a ThreadPoolImpl with a production TaskTracker. - //|histogram_label| is used to label histograms, it must not be empty. + // Creates a ThreadPoolImpl with a production TaskTracker. |histogram_label| + // is used to label histograms. No histograms are recorded if it is empty. explicit ThreadPoolImpl(StringPiece histogram_label); // For testing only. Creates a ThreadPoolImpl with a custom TaskTracker. @@ -103,6 +103,7 @@ class BASE_EXPORT ThreadPoolImpl : public ThreadPoolInstance, // PooledTaskRunnerDelegate: bool EnqueueJobTaskSource(scoped_refptr<JobTaskSource> task_source) override; + void RemoveJobTaskSource(scoped_refptr<JobTaskSource> task_source) override; void UpdatePriority(scoped_refptr<TaskSource> task_source, TaskPriority priority) override; diff --git a/chromium/base/task_runner.cc b/chromium/base/task_runner.cc index e2787f3d1ac..06aca8a68db 100644 --- a/chromium/base/task_runner.cc +++ b/chromium/base/task_runner.cc @@ -54,13 +54,14 @@ bool TaskRunner::PostTaskAndReply(const Location& from_here, from_here, std::move(task), std::move(reply)); } -bool TaskRunner::PostPromiseInternal( - const scoped_refptr<internal::AbstractPromise>& promise, - base::TimeDelta delay) { - return PostDelayedTask(promise->from_here(), - BindOnce(&internal::AbstractPromise::Execute, - internal::PromiseHolder(promise)), - delay); +bool TaskRunner::PostPromiseInternal(WrappedPromise promise, + base::TimeDelta delay) { + Location from_here = promise.from_here(); + return PostDelayedTask( + from_here, + BindOnce([](WrappedPromise promise) { promise.Execute(); }, + std::move(promise)), + delay); } TaskRunner::TaskRunner() = default; diff --git a/chromium/base/task_runner.h b/chromium/base/task_runner.h index 471f23cd7bb..2bd5d4eb2bf 100644 --- a/chromium/base/task_runner.h +++ b/chromium/base/task_runner.h @@ -11,14 +11,11 @@ #include "base/callback.h" #include "base/location.h" #include "base/memory/ref_counted.h" +#include "base/task/promise/abstract_promise.h" #include "base/time/time.h" namespace base { -namespace internal { -class AbstractPromise; -} // namespace internal - struct TaskRunnerTraits; // A TaskRunner is an object that runs posted tasks (in the form of @@ -139,9 +136,7 @@ class BASE_EXPORT TaskRunner // TODO(alexclarke): This should become pure virtual and replace // PostDelayedTask. NB passing by reference to reduce binary size. - bool PostPromiseInternal( - const scoped_refptr<internal::AbstractPromise>& promise, - base::TimeDelta delay); + bool PostPromiseInternal(WrappedPromise promise, base::TimeDelta delay); protected: friend struct TaskRunnerTraits; diff --git a/chromium/base/test/BUILD.gn b/chromium/base/test/BUILD.gn index 1b94239db7b..b71bb86a384 100644 --- a/chromium/base/test/BUILD.gn +++ b/chromium/base/test/BUILD.gn @@ -27,8 +27,6 @@ static_library("test_config") { static_library("test_support") { testonly = true sources = [ - "../memory/fake_memory_pressure_monitor.cc", - "../memory/fake_memory_pressure_monitor.h", "../task/sequence_manager/test/fake_task.cc", "../task/sequence_manager/test/fake_task.h", "../task/sequence_manager/test/mock_time_domain.cc", @@ -228,6 +226,7 @@ static_library("test_support") { set_sources_assignment_filter([]) sources += [ "test_file_util_mac.cc" ] set_sources_assignment_filter(sources_assignment_filter) + deps += [ ":google_test_runner_shared_headers" ] } if (is_mac) { @@ -434,7 +433,7 @@ if (is_linux) { ] args = [] outputs = [ - "$root_out_dir/fontconfig_caches/df1acc8c-39d5-4a8b-8507-b1a7396ac3ac-le64.cache-7", + "$root_out_dir/fontconfig_caches/fb5c91b2895aa445d23aebf7f9e2189c-le64.cache-7", "$root_out_dir/test_fonts/.uuid", ] } @@ -455,6 +454,7 @@ if (is_fuchsia || is_linux) { if (is_android) { generate_jni("base_unittests_jni_headers") { + testonly = true sources = [ "android/java/src/org/chromium/base/ContentUriTestUtils.java", "android/java/src/org/chromium/base/JavaHandlerThreadHelpers.java", @@ -462,6 +462,7 @@ if (is_android) { } generate_jni("test_support_jni_headers") { + testonly = true sources = [ "android/java/src/org/chromium/base/MainReturnCodeResult.java", "android/java/src/org/chromium/base/MultiprocessTestClientLauncher.java", @@ -477,7 +478,7 @@ if (is_android) { "//base:base_java", "//base:base_java_test_support", "//testing/android/native_test:native_main_runner_java", - "//third_party/android_deps:com_android_support_support_annotations_java", + "//third_party/android_deps:androidx_annotation_annotation_java", "//third_party/jsr-305:jsr_305_javalib", ] srcjar_deps = [ ":test_support_java_aidl" ] @@ -513,6 +514,29 @@ if (is_android) { } } +if (is_ios) { + source_set("google_test_runner_shared_headers") { + sources = [ + "ios/google_test_runner_delegate.h", + ] + } + + source_set("google_test_runner") { + sources = [ + "ios/google_test_runner.mm", + ] + deps = [ + ":google_test_runner_shared_headers", + "//base", + ] + libs = [ "UIKit.framework" ] + configs += [ + "//build/config/compiler:enable_arc", + "//build/config/ios:xctest_config", + ] + } +} + # Trivial executable which outputs space-delimited argv to stdout, # used for testing. executable("test_child_process") { diff --git a/chromium/base/threading/platform_thread_unittest.cc b/chromium/base/threading/platform_thread_unittest.cc index 2ef69fe32f0..96ea6629fc7 100644 --- a/chromium/base/threading/platform_thread_unittest.cc +++ b/chromium/base/threading/platform_thread_unittest.cc @@ -289,9 +289,7 @@ TEST(PlatformThreadTest, MAYBE_SetCurrentThreadPriority) { #if defined(OS_WIN) // Test changing a created thread's priority, with the // kWindowsThreadModeBackground feature enabled. -// Flaky: https://crbug.com/931706 -TEST(PlatformThreadTest, - DISABLED_SetCurrentThreadPriorityWithThreadModeBackground) { +TEST(PlatformThreadTest, SetCurrentThreadPriorityWithThreadModeBackground) { test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitAndEnableFeature( features::kWindowsThreadModeBackground); @@ -301,9 +299,8 @@ TEST(PlatformThreadTest, // Test changing a created thread's priority, with the // kWindowsThreadModeBackground feature enabled, in an IDLE_PRIORITY_CLASS // process (regression test for https://crbug.com/901483). -// Flaky: https://crbug.com/931706 TEST(PlatformThreadTest, - DISABLED_SetCurrentThreadPriorityWithThreadModeBackgroundIdleProcess) { + SetCurrentThreadPriorityWithThreadModeBackgroundIdleProcess) { ::SetPriorityClass(Process::Current().Handle(), IDLE_PRIORITY_CLASS); test::ScopedFeatureList scoped_feature_list; diff --git a/chromium/base/threading/platform_thread_win.cc b/chromium/base/threading/platform_thread_win.cc index 0fef2a8d69c..2889b253b75 100644 --- a/chromium/base/threading/platform_thread_win.cc +++ b/chromium/base/threading/platform_thread_win.cc @@ -29,9 +29,9 @@ namespace base { namespace { -// The value returned by ::GetThreadPriority() after background thread mode is -// enabled on Windows 8+. -constexpr int kWin8AboveBackgroundThreadModePriority = -4; +// The most common value returned by ::GetThreadPriority() after background +// thread mode is enabled on Windows 7. +constexpr int kWin7BackgroundThreadModePriority = 4; // The information on how to set the thread name comes from // a MSDN article: http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx @@ -401,17 +401,45 @@ void PlatformThread::SetCurrentThreadPriorityImpl(ThreadPriority priority) { // static ThreadPriority PlatformThread::GetCurrentThreadPriority() { + static_assert( + THREAD_PRIORITY_IDLE < 0, + "THREAD_PRIORITY_IDLE is >= 0 and will incorrectly cause errors."); + static_assert( + THREAD_PRIORITY_LOWEST < 0, + "THREAD_PRIORITY_LOWEST is >= 0 and will incorrectly cause errors."); + static_assert(THREAD_PRIORITY_BELOW_NORMAL < 0, + "THREAD_PRIORITY_BELOW_NORMAL is >= 0 and will incorrectly " + "cause errors."); + static_assert( + THREAD_PRIORITY_NORMAL == 0, + "The logic below assumes that THREAD_PRIORITY_NORMAL is zero. If it is " + "not, ThreadPriority::BACKGROUND may be incorrectly detected."); + static_assert(THREAD_PRIORITY_ABOVE_NORMAL >= 0, + "THREAD_PRIORITY_ABOVE_NORMAL is < 0 and will incorrectly be " + "translated to ThreadPriority::BACKGROUND."); + static_assert(THREAD_PRIORITY_HIGHEST >= 0, + "THREAD_PRIORITY_HIGHEST is < 0 and will incorrectly be " + "translated to ThreadPriority::BACKGROUND."); + static_assert(THREAD_PRIORITY_TIME_CRITICAL >= 0, + "THREAD_PRIORITY_TIME_CRITICAL is < 0 and will incorrectly be " + "translated to ThreadPriority::BACKGROUND."); + static_assert(THREAD_PRIORITY_ERROR_RETURN >= 0, + "THREAD_PRIORITY_ERROR_RETURN is < 0 and will incorrectly be " + "translated to ThreadPriority::BACKGROUND."); + const int priority = ::GetThreadPriority(PlatformThread::CurrentHandle().platform_handle()); + // Negative values represent a background priority. We have observed -3, -4, + // -6 when THREAD_MODE_BACKGROUND_* is used. THREAD_PRIORITY_IDLE, + // THREAD_PRIORITY_LOWEST and THREAD_PRIORITY_BELOW_NORMAL are other possible + // negative values. + if (priority < THREAD_PRIORITY_NORMAL) + return ThreadPriority::BACKGROUND; + switch (priority) { - case THREAD_PRIORITY_IDLE: - case internal::kWin7BackgroundThreadModePriority: + case kWin7BackgroundThreadModePriority: DCHECK_EQ(win::GetVersion(), win::Version::WIN7); - FALLTHROUGH; - case kWin8AboveBackgroundThreadModePriority: - case THREAD_PRIORITY_LOWEST: - case THREAD_PRIORITY_BELOW_NORMAL: return ThreadPriority::BACKGROUND; case THREAD_PRIORITY_NORMAL: return ThreadPriority::NORMAL; @@ -421,10 +449,10 @@ ThreadPriority PlatformThread::GetCurrentThreadPriority() { case THREAD_PRIORITY_TIME_CRITICAL: return ThreadPriority::REALTIME_AUDIO; case THREAD_PRIORITY_ERROR_RETURN: - DPCHECK(false) << "GetThreadPriority error"; + DPCHECK(false) << "::GetThreadPriority error"; } - NOTREACHED() << "GetCurrentThreadPriority returned " << priority << "."; + NOTREACHED() << "::GetThreadPriority returned " << priority << "."; return ThreadPriority::NORMAL; } diff --git a/chromium/base/threading/platform_thread_win.h b/chromium/base/threading/platform_thread_win.h index d1bf4205de4..879d5062667 100644 --- a/chromium/base/threading/platform_thread_win.h +++ b/chromium/base/threading/platform_thread_win.h @@ -25,10 +25,6 @@ BASE_EXPORT extern const Feature kWindowsThreadModeBackground; namespace internal { -// The value returned by ::GetThreadPriority() after background thread mode is -// enabled on Windows 7. Exposed for unit tests. -constexpr int kWin7BackgroundThreadModePriority = 4; - // Assert that the memory priority of |thread| is |memory_priority|. No-op on // Windows 7 because ::GetThreadInformation() is not available. Exposed for unit // tests. diff --git a/chromium/base/threading/platform_thread_win_unittest.cc b/chromium/base/threading/platform_thread_win_unittest.cc index 15c9939da46..de1d2cab595 100644 --- a/chromium/base/threading/platform_thread_win_unittest.cc +++ b/chromium/base/threading/platform_thread_win_unittest.cc @@ -24,9 +24,7 @@ namespace base { // behavior which we suspect is a Windows kernel bug. If this test starts // failing, the mitigation for https://crbug.com/901483 in // PlatformThread::SetCurrentThreadPriority() should be revisited. -// Fails on various windows bots: https://crbug.com/931720. -TEST(PlatformThreadWinTest, - DISABLED_SetBackgroundThreadModeFailsInIdlePriorityProcess) { +TEST(PlatformThreadWinTest, SetBackgroundThreadModeFailsInIdlePriorityProcess) { PlatformThreadHandle::Handle thread_handle = PlatformThread::CurrentHandle().platform_handle(); @@ -53,23 +51,19 @@ TEST(PlatformThreadWinTest, EXPECT_TRUE(::SetThreadPriority(thread_handle, THREAD_MODE_BACKGROUND_BEGIN)); // On Win8, GetThreadPriority() stays NORMAL. On Win7, it can stay NORMAL or - // switch to one of the 2 priorities that are usually observed after entering + // switch to one of the various priorities that are observed after entering // thread mode background in a NORMAL_PRIORITY_CLASS process. On all Windows - // verisons, memory priority becomes VERY_LOW. + // versions, memory priority becomes VERY_LOW. // // Note: this documents the aforementioned kernel bug. Ideally this would // *not* be the case. const float priority_after_thread_mode_background_begin = ::GetThreadPriority(thread_handle); if (win::GetVersion() == win::Version::WIN7) { - constexpr std::array<int, 3> kExpectedWin7Priorities( - {// Priority if GetThreadPriority() is not affected. - THREAD_PRIORITY_NORMAL, - // Priorities if GetThreadPriority() behaves like in a - // NORMAL_PRIORITY_CLASS process. - THREAD_PRIORITY_IDLE, internal::kWin7BackgroundThreadModePriority}); - EXPECT_THAT(kExpectedWin7Priorities, - testing::Contains(priority_after_thread_mode_background_begin)); + if (priority_after_thread_mode_background_begin != THREAD_PRIORITY_NORMAL) { + EXPECT_EQ(ThreadPriority::BACKGROUND, + base::PlatformThread::GetCurrentThreadPriority()); + } } else { EXPECT_EQ(priority_after_thread_mode_background_begin, THREAD_PRIORITY_NORMAL); diff --git a/chromium/base/threading/sequenced_task_runner_handle.cc b/chromium/base/threading/sequenced_task_runner_handle.cc index 8a7e129cbe9..2bfc0cf42ee 100644 --- a/chromium/base/threading/sequenced_task_runner_handle.cc +++ b/chromium/base/threading/sequenced_task_runner_handle.cc @@ -23,8 +23,10 @@ LazyInstance<ThreadLocalPointer<SequencedTaskRunnerHandle>>::Leaky const scoped_refptr<SequencedTaskRunner>& SequencedTaskRunnerHandle::Get() { const SequencedTaskRunnerHandle* current = sequenced_task_runner_tls.Pointer()->Get(); - CHECK(current) << "Error: This caller requires a sequenced context (i.e. the " - "current task needs to run from a SequencedTaskRunner)."; + CHECK(current) + << "Error: This caller requires a sequenced context (i.e. the current " + "task needs to run from a SequencedTaskRunner). If you're in a test " + "refer to //docs/threading_and_tasks_testing.md."; return current->task_runner_; } diff --git a/chromium/base/threading/thread.cc b/chromium/base/threading/thread.cc index a411bb8a0e2..eecd3491890 100644 --- a/chromium/base/threading/thread.cc +++ b/chromium/base/threading/thread.cc @@ -4,14 +4,22 @@ #include "base/threading/thread.h" +#include <type_traits> + #include "base/bind.h" #include "base/bind_helpers.h" #include "base/lazy_instance.h" #include "base/location.h" #include "base/logging.h" #include "base/memory/ptr_util.h" +#include "base/memory/scoped_refptr.h" +#include "base/message_loop/message_loop_current.h" +#include "base/message_loop/message_pump.h" #include "base/run_loop.h" #include "base/synchronization/waitable_event.h" +#include "base/task/sequence_manager/sequence_manager_impl.h" +#include "base/task/sequence_manager/task_queue.h" +#include "base/task/simple_task_executor.h" #include "base/third_party/dynamic_annotations/dynamic_annotations.h" #include "base/threading/thread_id_name_manager.h" #include "base/threading/thread_local.h" @@ -38,6 +46,58 @@ namespace { base::LazyInstance<base::ThreadLocalBoolean>::Leaky lazy_tls_bool = LAZY_INSTANCE_INITIALIZER; +class SequenceManagerThreadDelegate : public Thread::Delegate { + public: + explicit SequenceManagerThreadDelegate( + MessagePumpType message_pump_type, + OnceCallback<std::unique_ptr<MessagePump>()> message_pump_factory) + : sequence_manager_( + sequence_manager::internal::SequenceManagerImpl::CreateUnbound( + sequence_manager::SequenceManager::Settings::Builder() + .SetMessagePumpType(message_pump_type) + .Build())), + default_task_queue_(sequence_manager_->CreateTaskQueue( + sequence_manager::TaskQueue::Spec("default_tq"))), + message_pump_factory_(std::move(message_pump_factory)) { + sequence_manager_->SetDefaultTaskRunner(default_task_queue_->task_runner()); + } + + ~SequenceManagerThreadDelegate() override = default; + + scoped_refptr<SingleThreadTaskRunner> GetDefaultTaskRunner() override { + // Surprisingly this might not be default_task_queue_->task_runner() which + // we set in the constructor. The Thread::Init() method could create a + // SequenceManager on top of the current one and call + // SequenceManager::SetDefaultTaskRunner which would propagate the new + // TaskRunner down to our SequenceManager. Turns out, code actually relies + // on this and somehow relies on + // SequenceManagerThreadDelegate::GetDefaultTaskRunner returning this new + // TaskRunner. So instead of returning default_task_queue_->task_runner() we + // need to query the SequenceManager for it. + // The underlying problem here is that Subclasses of Thread can do crazy + // stuff in Init() but they are not really in control of what happens in the + // Thread::Delegate, as this is passed in on calling StartWithOptions which + // could happen far away from where the Thread is created. We should + // consider getting rid of StartWithOptions, and pass them as a constructor + // argument instead. + return sequence_manager_->GetTaskRunner(); + } + + void BindToCurrentThread(TimerSlack timer_slack) override { + sequence_manager_->BindToMessagePump( + std::move(message_pump_factory_).Run()); + sequence_manager_->SetTimerSlack(timer_slack); + simple_task_executor_.emplace(GetDefaultTaskRunner()); + } + + private: + std::unique_ptr<sequence_manager::internal::SequenceManagerImpl> + sequence_manager_; + scoped_refptr<sequence_manager::TaskQueue> default_task_queue_; + OnceCallback<std::unique_ptr<MessagePump>()> message_pump_factory_; + base::Optional<SimpleTaskExecutor> simple_task_executor_; +}; + } // namespace Thread::Options::Options() = default; @@ -100,11 +160,13 @@ bool Thread::StartWithOptions(const Options& options) { DCHECK(!options.message_pump_factory); delegate_ = WrapUnique(options.delegate); } else if (options.message_pump_factory) { - delegate_ = std::make_unique<internal::MessageLoopThreadDelegate>( - MessageLoop::CreateUnbound(options.message_pump_factory.Run())); + delegate_ = std::make_unique<SequenceManagerThreadDelegate>( + MessagePumpType::CUSTOM, options.message_pump_factory); } else { - delegate_ = std::make_unique<internal::MessageLoopThreadDelegate>( - MessageLoop::CreateUnbound(options.message_pump_type)); + delegate_ = std::make_unique<SequenceManagerThreadDelegate>( + options.message_pump_type, + BindOnce([](MessagePumpType type) { return MessagePump::Create(type); }, + options.message_pump_type)); } start_event_.Reset(); @@ -291,9 +353,10 @@ void Thread::ThreadMain() { #if defined(OS_WIN) std::unique_ptr<win::ScopedCOMInitializer> com_initializer; if (com_status_ != NONE) { - com_initializer.reset((com_status_ == STA) ? - new win::ScopedCOMInitializer() : - new win::ScopedCOMInitializer(win::ScopedCOMInitializer::kMTA)); + com_initializer.reset( + (com_status_ == STA) + ? new win::ScopedCOMInitializer() + : new win::ScopedCOMInitializer(win::ScopedCOMInitializer::kMTA)); } #endif @@ -337,24 +400,4 @@ void Thread::ThreadQuitHelper() { SetThreadWasQuitProperly(true); } -namespace internal { - -MessageLoopThreadDelegate::MessageLoopThreadDelegate( - std::unique_ptr<MessageLoop> message_loop) - : message_loop_(std::move(message_loop)) {} - -MessageLoopThreadDelegate::~MessageLoopThreadDelegate() {} - -scoped_refptr<SingleThreadTaskRunner> -MessageLoopThreadDelegate::GetDefaultTaskRunner() { - return message_loop_->task_runner(); -} - -void MessageLoopThreadDelegate::BindToCurrentThread(TimerSlack timer_slack) { - message_loop_->BindToCurrentThread(); - message_loop_->SetTimerSlack(timer_slack); -} - -} // namespace internal - } // namespace base diff --git a/chromium/base/threading/thread.h b/chromium/base/threading/thread.h index 73a444f178a..312caab25a2 100644 --- a/chromium/base/threading/thread.h +++ b/chromium/base/threading/thread.h @@ -13,8 +13,6 @@ #include "base/base_export.h" #include "base/callback.h" #include "base/macros.h" -#include "base/message_loop/message_loop.h" -#include "base/message_loop/message_loop_current.h" #include "base/message_loop/message_pump_type.h" #include "base/message_loop/timer_slack.h" #include "base/sequence_checker.h" @@ -331,24 +329,6 @@ class BASE_EXPORT Thread : PlatformThread::Delegate { DISALLOW_COPY_AND_ASSIGN(Thread); }; -namespace internal { - -class BASE_EXPORT MessageLoopThreadDelegate : public Thread::Delegate { - public: - explicit MessageLoopThreadDelegate(std::unique_ptr<MessageLoop> message_loop); - - ~MessageLoopThreadDelegate() override; - - // Thread::Delegate: - scoped_refptr<SingleThreadTaskRunner> GetDefaultTaskRunner() override; - void BindToCurrentThread(TimerSlack timer_slack) override; - - private: - std::unique_ptr<MessageLoop> message_loop_; -}; - -} // namespace internal - } // namespace base #endif // BASE_THREADING_THREAD_H_ diff --git a/chromium/base/threading/thread_perftest.cc b/chromium/base/threading/thread_perftest.cc index e19b18bf523..5e1e65bc7c3 100644 --- a/chromium/base/threading/thread_perftest.cc +++ b/chromium/base/threading/thread_perftest.cc @@ -12,6 +12,7 @@ #include "base/command_line.h" #include "base/location.h" #include "base/memory/ptr_util.h" +#include "base/message_loop/message_loop_current.h" #include "base/single_thread_task_runner.h" #include "base/strings/stringprintf.h" #include "base/synchronization/condition_variable.h" diff --git a/chromium/base/threading/thread_restrictions.h b/chromium/base/threading/thread_restrictions.h index 052978192ab..c93f1fcaec0 100644 --- a/chromium/base/threading/thread_restrictions.h +++ b/chromium/base/threading/thread_restrictions.h @@ -179,9 +179,6 @@ class HistoryReportJniBridge; namespace gpu { class GpuChannelHost; } -namespace leveldb { -class LevelDBMojoProxy; -} namespace leveldb_env { class DBTracker; } @@ -189,6 +186,7 @@ namespace media { class AudioInputDevice; class AudioOutputDevice; class BlockingUrlProtocol; +class PaintCanvasVideoRenderer; } namespace memory_instrumentation { class OSMetrics; @@ -196,6 +194,9 @@ class OSMetrics; namespace midi { class TaskService; // https://crbug.com/796830 } +namespace module_installer { +class ScopedAllowModulePakLoad; +} namespace mojo { class CoreLibraryInitializer; class SyncCallRestrictions; @@ -204,6 +205,7 @@ class ScopedIPCSupport; } } namespace printing { +class PrintJobWorker; class PrinterQuery; } namespace rlz_lib { @@ -262,6 +264,10 @@ class WebMainLoop; class WebSubThread; } +namespace weblayer { +class ProfileImpl; +} + namespace webrtc { class DesktopConfigurationMonitor; } @@ -349,11 +355,14 @@ class BASE_EXPORT ScopedAllowBlocking { friend class cronet::CronetPrefsManager; friend class cronet::CronetURLRequestContext; friend class memory_instrumentation::OSMetrics; + friend class module_installer::ScopedAllowModulePakLoad; friend class mojo::CoreLibraryInitializer; + friend class printing::PrintJobWorker; friend class resource_coordinator::TabManagerDelegate; // crbug.com/778703 friend class ui::MaterialDesignController; friend class web::WebSubThread; friend class StackSamplingProfiler; + friend class weblayer::ProfileImpl; ScopedAllowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF; ~ScopedAllowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF; @@ -407,7 +416,6 @@ class BASE_EXPORT ScopedAllowBaseSyncPrimitives { friend class functions::ExecScriptScopedAllowBaseSyncPrimitives; friend class history_report::HistoryReportJniBridge; friend class internal::TaskTracker; - friend class leveldb::LevelDBMojoProxy; friend class leveldb_env::DBTracker; friend class media::BlockingUrlProtocol; friend class mojo::core::ScopedIPCSupport; @@ -475,6 +483,7 @@ class BASE_EXPORT ScopedAllowBaseSyncPrimitivesOutsideBlockingScope { friend class content::SynchronousCompositorSyncCallBridge; friend class media::AudioInputDevice; friend class media::AudioOutputDevice; + friend class media::PaintCanvasVideoRenderer; friend class mojo::SyncCallRestrictions; friend class net::NetworkConfigWatcherMacThread; friend class viz::HostGpuMemoryBufferManager; diff --git a/chromium/base/threading/thread_task_runner_handle.cc b/chromium/base/threading/thread_task_runner_handle.cc index 27a3f84eee6..4a9ac88f61c 100644 --- a/chromium/base/threading/thread_task_runner_handle.cc +++ b/chromium/base/threading/thread_task_runner_handle.cc @@ -26,9 +26,10 @@ base::LazyInstance<base::ThreadLocalPointer<ThreadTaskRunnerHandle>>::Leaky const scoped_refptr<SingleThreadTaskRunner>& ThreadTaskRunnerHandle::Get() { const ThreadTaskRunnerHandle* current = thread_task_runner_tls.Pointer()->Get(); - CHECK(current) << "Error: This caller requires a single-threaded context " - "(i.e. the current task needs to run from a " - "SingleThreadTaskRunner)."; + CHECK(current) + << "Error: This caller requires a single-threaded context (i.e. the " + "current task needs to run from a SingleThreadTaskRunner). If you're " + "in a test refer to //docs/threading_and_tasks_testing.md."; return current->task_runner_; } diff --git a/chromium/base/threading/thread_unittest.cc b/chromium/base/threading/thread_unittest.cc index bdb57cc19fe..5f1722877ad 100644 --- a/chromium/base/threading/thread_unittest.cc +++ b/chromium/base/threading/thread_unittest.cc @@ -17,16 +17,21 @@ #include "base/run_loop.h" #include "base/single_thread_task_runner.h" #include "base/synchronization/waitable_event.h" +#include "base/task/post_task.h" #include "base/task/sequence_manager/sequence_manager_impl.h" +#include "base/task/task_executor.h" +#include "base/test/bind_test_util.h" #include "base/test/gtest_util.h" #include "base/third_party/dynamic_annotations/dynamic_annotations.h" #include "base/threading/platform_thread.h" #include "base/time/time.h" #include "build/build_config.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" using base::Thread; +using ::testing::NotNull; typedef PlatformTest ThreadTest; @@ -522,6 +527,44 @@ TEST_F(ThreadTest, FlushForTesting) { a.FlushForTesting(); } +TEST_F(ThreadTest, GetTaskExecutorForCurrentThread) { + Thread a("GetTaskExecutorForCurrentThread"); + ASSERT_TRUE(a.Start()); + + base::WaitableEvent event; + + a.task_runner()->PostTask( + FROM_HERE, base::BindLambdaForTesting([&]() { + EXPECT_THAT(base::GetTaskExecutorForCurrentThread(), NotNull()); + event.Signal(); + })); + + event.Wait(); + a.Stop(); +} + +TEST_F(ThreadTest, CurrentThread) { + Thread a("CurrentThread"); + ASSERT_TRUE(a.Start()); + + base::WaitableEvent event; + + a.task_runner()->PostTask( + FROM_HERE, base::BindLambdaForTesting([&]() { + EXPECT_EQ(a.task_runner(), + base::CreateSingleThreadTaskRunner({base::CurrentThread()})); + + // There's only a single task runner so base::TaskPriority is ignored. + EXPECT_EQ(a.task_runner(), base::CreateSingleThreadTaskRunner( + {base::CurrentThread(), + base::TaskPriority::BEST_EFFORT})); + event.Signal(); + })); + + event.Wait(); + a.Stop(); +} + namespace { class SequenceManagerThreadDelegate : public Thread::Delegate { diff --git a/chromium/base/time/time.cc b/chromium/base/time/time.cc index 3e46f17575c..9ac9d4a21db 100644 --- a/chromium/base/time/time.cc +++ b/chromium/base/time/time.cc @@ -135,52 +135,6 @@ int64_t TimeDelta::InNanoseconds() const { return delta_ * Time::kNanosecondsPerMicrosecond; } -namespace time_internal { - -int64_t SaturatedAdd(int64_t value, TimeDelta delta) { - // Treat Min/Max() as +/- infinity (additions involving two infinities are - // only valid if signs match). - if (delta.is_max()) { - CHECK_GT(value, std::numeric_limits<int64_t>::min()); - return std::numeric_limits<int64_t>::max(); - } else if (delta.is_min()) { - CHECK_LT(value, std::numeric_limits<int64_t>::max()); - return std::numeric_limits<int64_t>::min(); - } - - CheckedNumeric<int64_t> rv(value); - rv += delta.delta_; - if (rv.IsValid()) - return rv.ValueOrDie(); - // Positive RHS overflows. Negative RHS underflows. - if (delta.delta_ < 0) - return std::numeric_limits<int64_t>::min(); - return std::numeric_limits<int64_t>::max(); -} - -int64_t SaturatedSub(int64_t value, TimeDelta delta) { - // Treat Min/Max() as +/- infinity (subtractions involving two infinities are - // only valid if signs are opposite). - if (delta.is_max()) { - CHECK_LT(value, std::numeric_limits<int64_t>::max()); - return std::numeric_limits<int64_t>::min(); - } else if (delta.is_min()) { - CHECK_GT(value, std::numeric_limits<int64_t>::min()); - return std::numeric_limits<int64_t>::max(); - } - - CheckedNumeric<int64_t> rv(value); - rv -= delta.delta_; - if (rv.IsValid()) - return rv.ValueOrDie(); - // Negative RHS overflows. Positive RHS underflows. - if (delta.delta_ < 0) - return std::numeric_limits<int64_t>::max(); - return std::numeric_limits<int64_t>::min(); -} - -} // namespace time_internal - std::ostream& operator<<(std::ostream& os, TimeDelta time_delta) { return os << time_delta.InSecondsF() << " s"; } diff --git a/chromium/base/time/time.h b/chromium/base/time/time.h index 6abe4112645..aed3de765b1 100644 --- a/chromium/base/time/time.h +++ b/chromium/base/time/time.h @@ -109,8 +109,8 @@ namespace time_internal { // as infinity and will always saturate the return value (infinity math applies // if |value| also is at either limit of its spectrum). The int64_t argument and // return value are in terms of a microsecond timebase. -BASE_EXPORT int64_t SaturatedAdd(int64_t value, TimeDelta delta); -BASE_EXPORT int64_t SaturatedSub(int64_t value, TimeDelta delta); +BASE_EXPORT constexpr int64_t SaturatedAdd(int64_t value, TimeDelta delta); +BASE_EXPORT constexpr int64_t SaturatedSub(int64_t value, TimeDelta delta); } // namespace time_internal @@ -121,6 +121,9 @@ class BASE_EXPORT TimeDelta { constexpr TimeDelta() : delta_(0) {} // Converts units of time to TimeDeltas. + // WARNING: Floating point arithmetic is such that FromXXXD(t.InXXXF()) may + // not precisely equal |t|. Hence, floating point values should not be used + // for storage. static constexpr TimeDelta FromDays(int days); static constexpr TimeDelta FromHours(int hours); static constexpr TimeDelta FromMinutes(int minutes); @@ -208,6 +211,9 @@ class BASE_EXPORT TimeDelta { // towards zero, std::trunc() behavior). The InXYZFloored() versions round to // lesser integers (std::floor() behavior). The XYZRoundedUp() versions round // up to greater integers (std::ceil() behavior). + // WARNING: Floating point arithmetic is such that FromXXXD(t.InXXXF()) may + // not precisely equal |t|. Hence, floating point values should not be used + // for storage. int InDays() const; int InDaysFloored() const; int InHours() const; @@ -221,30 +227,25 @@ class BASE_EXPORT TimeDelta { double InMicrosecondsF() const; int64_t InNanoseconds() const; - // Computations with other deltas. Can easily be made constexpr with C++17 but - // hard to do until then per limitations around - // __builtin_(add|sub)_overflow in safe_math_clang_gcc_impl.h : - // https://chromium-review.googlesource.com/c/chromium/src/+/873352#message-59594ab70827795a67e0780404adf37b4b6c2f14 - TimeDelta operator+(TimeDelta other) const { + // Computations with other deltas. + constexpr TimeDelta operator+(TimeDelta other) const { return TimeDelta(time_internal::SaturatedAdd(delta_, other)); } - TimeDelta operator-(TimeDelta other) const { + constexpr TimeDelta operator-(TimeDelta other) const { return TimeDelta(time_internal::SaturatedSub(delta_, other)); } - TimeDelta& operator+=(TimeDelta other) { + constexpr TimeDelta& operator+=(TimeDelta other) { return *this = (*this + other); } - TimeDelta& operator-=(TimeDelta other) { + constexpr TimeDelta& operator-=(TimeDelta other) { return *this = (*this - other); } constexpr TimeDelta operator-() const { return TimeDelta(-delta_); } - // Computations with numeric types. operator*() isn't constexpr because of a - // limitation around __builtin_mul_overflow (but operator/(1.0/a) works for - // |a|'s of "reasonable" size -- i.e. that don't risk overflow). + // Computations with numeric types. template <typename T> - TimeDelta operator*(T a) const { + constexpr TimeDelta operator*(T a) const { CheckedNumeric<int64_t> rv(delta_); rv *= a; if (rv.IsValid()) @@ -267,7 +268,7 @@ class BASE_EXPORT TimeDelta { return TimeDelta(std::numeric_limits<int64_t>::max()); } template <typename T> - TimeDelta& operator*=(T a) { + constexpr TimeDelta& operator*=(T a) { return *this = (*this * a); } template <typename T> @@ -276,9 +277,11 @@ class BASE_EXPORT TimeDelta { } constexpr int64_t operator/(TimeDelta a) const { return delta_ / a.delta_; } + constexpr TimeDelta operator%(TimeDelta a) const { return TimeDelta(delta_ % a.delta_); } + TimeDelta& operator%=(TimeDelta other) { return *this = (*this % other); } // Comparison operators. constexpr bool operator==(TimeDelta other) const { @@ -301,8 +304,10 @@ class BASE_EXPORT TimeDelta { } private: - friend int64_t time_internal::SaturatedAdd(int64_t value, TimeDelta delta); - friend int64_t time_internal::SaturatedSub(int64_t value, TimeDelta delta); + friend constexpr int64_t time_internal::SaturatedAdd(int64_t value, + TimeDelta delta); + friend constexpr int64_t time_internal::SaturatedSub(int64_t value, + TimeDelta delta); // Constructs a delta given the duration in microseconds. This is private // to avoid confusion by callers with an integer constructor. Use @@ -321,7 +326,7 @@ class BASE_EXPORT TimeDelta { }; template <typename T> -TimeDelta operator*(T a, TimeDelta td) { +constexpr TimeDelta operator*(T a, TimeDelta td) { return td * a; } @@ -333,6 +338,34 @@ BASE_EXPORT std::ostream& operator<<(std::ostream& os, TimeDelta time_delta); // TimeBase members via those classes. namespace time_internal { +constexpr int64_t SaturatedAdd(int64_t value, TimeDelta delta) { + // Treat Min/Max() as +/- infinity (additions involving two infinities are + // only valid if signs match). + if (delta.is_max()) { + CHECK_GT(value, std::numeric_limits<int64_t>::min()); + return std::numeric_limits<int64_t>::max(); + } else if (delta.is_min()) { + CHECK_LT(value, std::numeric_limits<int64_t>::max()); + return std::numeric_limits<int64_t>::min(); + } + + return base::ClampAdd(value, delta.delta_); +} + +constexpr int64_t SaturatedSub(int64_t value, TimeDelta delta) { + // Treat Min/Max() as +/- infinity (subtractions involving two infinities are + // only valid if signs are opposite). + if (delta.is_max()) { + CHECK_LT(value, std::numeric_limits<int64_t>::max()); + return std::numeric_limits<int64_t>::min(); + } else if (delta.is_min()) { + CHECK_GT(value, std::numeric_limits<int64_t>::min()); + return std::numeric_limits<int64_t>::max(); + } + + return base::ClampSub(value, delta.delta_); +} + // TimeBase-------------------------------------------------------------------- // Provides value storage and comparison/math operations common to all time @@ -377,11 +410,11 @@ class TimeBase { // Returns the maximum/minimum times, which should be greater/less than than // any reasonable time with which we might compare it. - static TimeClass Max() { + static constexpr TimeClass Max() { return TimeClass(std::numeric_limits<int64_t>::max()); } - static TimeClass Min() { + static constexpr TimeClass Min() { return TimeClass(std::numeric_limits<int64_t>::min()); } @@ -403,51 +436,39 @@ class TimeBase { return TimeDelta::FromMicroseconds(us_); } - TimeClass& operator=(TimeClass other) { + constexpr TimeClass& operator=(TimeClass other) { us_ = other.us_; return *(static_cast<TimeClass*>(this)); } // Compute the difference between two times. - TimeDelta operator-(TimeClass other) const { + constexpr TimeDelta operator-(TimeClass other) const { return TimeDelta::FromMicroseconds(us_ - other.us_); } // Return a new time modified by some delta. - TimeClass operator+(TimeDelta delta) const { + constexpr TimeClass operator+(TimeDelta delta) const { return TimeClass(time_internal::SaturatedAdd(us_, delta)); } - TimeClass operator-(TimeDelta delta) const { + constexpr TimeClass operator-(TimeDelta delta) const { return TimeClass(time_internal::SaturatedSub(us_, delta)); } // Modify by some time delta. - TimeClass& operator+=(TimeDelta delta) { + constexpr TimeClass& operator+=(TimeDelta delta) { return static_cast<TimeClass&>(*this = (*this + delta)); } - TimeClass& operator-=(TimeDelta delta) { + constexpr TimeClass& operator-=(TimeDelta delta) { return static_cast<TimeClass&>(*this = (*this - delta)); } // Comparison operators - bool operator==(TimeClass other) const { - return us_ == other.us_; - } - bool operator!=(TimeClass other) const { - return us_ != other.us_; - } - bool operator<(TimeClass other) const { - return us_ < other.us_; - } - bool operator<=(TimeClass other) const { - return us_ <= other.us_; - } - bool operator>(TimeClass other) const { - return us_ > other.us_; - } - bool operator>=(TimeClass other) const { - return us_ >= other.us_; - } + constexpr bool operator==(TimeClass other) const { return us_ == other.us_; } + constexpr bool operator!=(TimeClass other) const { return us_ != other.us_; } + constexpr bool operator<(TimeClass other) const { return us_ < other.us_; } + constexpr bool operator<=(TimeClass other) const { return us_ <= other.us_; } + constexpr bool operator>(TimeClass other) const { return us_ > other.us_; } + constexpr bool operator>=(TimeClass other) const { return us_ >= other.us_; } protected: constexpr explicit TimeBase(int64_t us) : us_(us) {} @@ -458,8 +479,8 @@ class TimeBase { } // namespace time_internal -template<class TimeClass> -inline TimeClass operator+(TimeDelta delta, TimeClass t) { +template <class TimeClass> +inline constexpr TimeClass operator+(TimeDelta delta, TimeClass t) { return t + delta; } @@ -816,13 +837,7 @@ constexpr TimeDelta TimeDelta::Min() { // static constexpr TimeDelta TimeDelta::FromDouble(double value) { - // TODO(crbug.com/612601): Use saturated_cast<int64_t>(value) once we sort out - // the Min() behavior. - return value > std::numeric_limits<int64_t>::max() - ? Max() - : value < std::numeric_limits<int64_t>::min() - ? Min() - : TimeDelta(static_cast<int64_t>(value)); + return TimeDelta(saturated_cast<int64_t>(value)); } // static diff --git a/chromium/base/time/time_exploded_posix.cc b/chromium/base/time/time_exploded_posix.cc index 0655703a1f4..7683d13d2a1 100644 --- a/chromium/base/time/time_exploded_posix.cc +++ b/chromium/base/time/time_exploded_posix.cc @@ -26,8 +26,9 @@ #if defined(OS_FUCHSIA) #include <fuchsia/deprecatedtimezone/cpp/fidl.h> +#include <lib/sys/cpp/component_context.h> +#include "base/fuchsia/default_context.h" #include "base/fuchsia/fuchsia_logging.h" -#include "base/fuchsia/service_directory_client.h" #include "base/no_destructor.h" #include "base/numerics/clamped_math.h" #endif @@ -72,11 +73,16 @@ void SysTimeToTimeStruct(SysTime t, struct tm* timestruct, bool is_local) { #elif defined(OS_FUCHSIA) typedef time_t SysTime; +fuchsia::deprecatedtimezone::TimezoneSyncPtr ConnectTimeZoneServiceSync() { + fuchsia::deprecatedtimezone::TimezoneSyncPtr timezone; + base::fuchsia::ComponentContextForCurrentProcess()->svc()->Connect( + timezone.NewRequest()); + return timezone; +} + SysTime GetTimezoneOffset(SysTime utc_time) { static base::NoDestructor<fuchsia::deprecatedtimezone::TimezoneSyncPtr> - timezone( - base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() - ->ConnectToServiceSync<fuchsia::deprecatedtimezone::Timezone>()); + timezone(ConnectTimeZoneServiceSync()); int64_t milliseconds_since_epoch = base::ClampMul(utc_time, base::Time::kMillisecondsPerSecond); diff --git a/chromium/base/time/time_unittest.cc b/chromium/base/time/time_unittest.cc index 5e8bad99065..fb00f8bf866 100644 --- a/chromium/base/time/time_unittest.cc +++ b/chromium/base/time/time_unittest.cc @@ -1352,6 +1352,10 @@ TEST(TimeDelta, MaxConversions) { .is_max(), ""); + static_assert( + TimeDelta::FromMicrosecondsD(max_d).is_max(), + "Make sure that 2^63 correctly gets clamped to `max` (crbug.com/612601)"); + // Floating point arithmetic resulting in infinity isn't constexpr in C++14. EXPECT_TRUE( TimeDelta::FromMillisecondsD(std::numeric_limits<double>::infinity()) diff --git a/chromium/base/time/time_win_unittest.cc b/chromium/base/time/time_win_unittest.cc index 7460bd17c34..a21b982da5e 100644 --- a/chromium/base/time/time_win_unittest.cc +++ b/chromium/base/time/time_win_unittest.cc @@ -259,13 +259,11 @@ TEST(TimeTicks, TSCTicksPerSecond) { // Read the CPU frequency from the registry. base::win::RegKey processor_key( HKEY_LOCAL_MACHINE, - STRING16_LITERAL("Hardware\\Description\\System\\CentralProcessor\\0"), - KEY_QUERY_VALUE); + L"Hardware\\Description\\System\\CentralProcessor\\0", KEY_QUERY_VALUE); ASSERT_TRUE(processor_key.Valid()); DWORD processor_mhz_from_registry; ASSERT_EQ(ERROR_SUCCESS, - processor_key.ReadValueDW(STRING16_LITERAL("~MHz"), - &processor_mhz_from_registry)); + processor_key.ReadValueDW(L"~MHz", &processor_mhz_from_registry)); // Expect the measured TSC frequency to be similar to the processor // frequency from the registry (0.5% error). diff --git a/chromium/base/trace_event/OWNERS b/chromium/base/trace_event/OWNERS index aa3ab84aa9f..8d95238ff95 100644 --- a/chromium/base/trace_event/OWNERS +++ b/chromium/base/trace_event/OWNERS @@ -1,4 +1,3 @@ -chiniforooshan@chromium.org eseckler@chromium.org oysteine@chromium.org primiano@chromium.org diff --git a/chromium/base/trace_event/blame_context.cc b/chromium/base/trace_event/blame_context.cc index 3c5f32ab464..e7599efa83f 100644 --- a/chromium/base/trace_event/blame_context.cc +++ b/chromium/base/trace_event/blame_context.cc @@ -40,6 +40,8 @@ BlameContext::~BlameContext() { void BlameContext::Enter() { DCHECK(WasInitialized()); + if (LIKELY(!*category_group_enabled_)) + return; TRACE_EVENT_API_ADD_TRACE_EVENT(TRACE_EVENT_PHASE_ENTER_CONTEXT, category_group_enabled_, name_, scope_, id_, nullptr, TRACE_EVENT_FLAG_HAS_ID); @@ -47,6 +49,8 @@ void BlameContext::Enter() { void BlameContext::Leave() { DCHECK(WasInitialized()); + if (LIKELY(!*category_group_enabled_)) + return; TRACE_EVENT_API_ADD_TRACE_EVENT(TRACE_EVENT_PHASE_LEAVE_CONTEXT, category_group_enabled_, name_, scope_, id_, nullptr, TRACE_EVENT_FLAG_HAS_ID); @@ -55,7 +59,7 @@ void BlameContext::Leave() { void BlameContext::TakeSnapshot() { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(WasInitialized()); - if (!*category_group_enabled_) + if (LIKELY(!*category_group_enabled_)) return; std::unique_ptr<trace_event::TracedValue> snapshot( new trace_event::TracedValue); diff --git a/chromium/base/trace_event/builtin_categories.h b/chromium/base/trace_event/builtin_categories.h index 6717a2bd93b..1ac3513eb4f 100644 --- a/chromium/base/trace_event/builtin_categories.h +++ b/chromium/base/trace_event/builtin_categories.h @@ -15,6 +15,9 @@ // your code and you get a static assert, this is the right place to register // the name. If the name is going to be used only for testing, please add it to // |kIgnoredCategoriesForTesting| instead. +// +// Prefer to use '_' to separate word of category name, like content_capture. +// // Parameter |X| must be a *macro* that takes a single |name| string argument, // denoting a category name. #define INTERNAL_TRACE_LIST_BUILTIN_CATEGORIES(X) \ @@ -52,6 +55,7 @@ X("cma") \ X("compositor") \ X("content") \ + X("content_capture") \ X("devtools") \ X("devtools.timeline") \ X("devtools.timeline.async") \ @@ -139,6 +143,7 @@ X("vk") \ X("wayland") \ X("webaudio") \ + X("weblayer") \ X("WebCore") \ X("webrtc") \ X("xr") \ @@ -177,6 +182,7 @@ X(TRACE_DISABLED_BY_DEFAULT("gpu.device")) \ X(TRACE_DISABLED_BY_DEFAULT("gpu.service")) \ X(TRACE_DISABLED_BY_DEFAULT("ipc.flow")) \ + X(TRACE_DISABLED_BY_DEFAULT("java-heap-profiler")) \ X(TRACE_DISABLED_BY_DEFAULT("layer-element")) \ X(TRACE_DISABLED_BY_DEFAULT("lifecycles")) \ X(TRACE_DISABLED_BY_DEFAULT("loading")) \ diff --git a/chromium/base/trace_event/malloc_dump_provider.cc b/chromium/base/trace_event/malloc_dump_provider.cc index e89597c1d6b..7e42cfc20bb 100644 --- a/chromium/base/trace_event/malloc_dump_provider.cc +++ b/chromium/base/trace_event/malloc_dump_provider.cc @@ -134,7 +134,10 @@ bool MallocDumpProvider::OnMemoryDump(const MemoryDumpArgs& args, // TODO(fuchsia): Port, see https://crbug.com/706592. #else struct mallinfo info = mallinfo(); - DCHECK_GE(info.arena + info.hblkhd, info.uordblks); +#if !defined(ADDRESS_SANITIZER) && !defined(THREAD_SANITIZER) + // Sanitizers override mallinfo. + DCHECK_GT(static_cast<int>(info.uordblks), 0); +#endif // In case of Android's jemalloc |arena| is 0 and the outer pages size is // reported by |hblkhd|. In case of dlmalloc the total is given by diff --git a/chromium/base/trace_event/memory_dump_manager.cc b/chromium/base/trace_event/memory_dump_manager.cc index 8b1bd7a0a58..d79d405ac9f 100644 --- a/chromium/base/trace_event/memory_dump_manager.cc +++ b/chromium/base/trace_event/memory_dump_manager.cc @@ -529,7 +529,8 @@ MemoryDumpManager::ProcessMemoryDumpAsyncState::ProcessMemoryDumpAsyncState( dump_thread_task_runner(std::move(dump_thread_task_runner)) { pending_dump_providers.reserve(dump_providers.size()); pending_dump_providers.assign(dump_providers.rbegin(), dump_providers.rend()); - MemoryDumpArgs args = {req_args.level_of_detail, req_args.dump_guid}; + MemoryDumpArgs args = {req_args.level_of_detail, req_args.determinism, + req_args.dump_guid}; process_memory_dump = std::make_unique<ProcessMemoryDump>(args); } diff --git a/chromium/base/trace_event/memory_dump_manager_unittest.cc b/chromium/base/trace_event/memory_dump_manager_unittest.cc index b33b677beb9..c7b413d46a5 100644 --- a/chromium/base/trace_event/memory_dump_manager_unittest.cc +++ b/chromium/base/trace_event/memory_dump_manager_unittest.cc @@ -54,6 +54,14 @@ MATCHER(IsLightDump, "") { return arg.level_of_detail == MemoryDumpLevelOfDetail::LIGHT; } +MATCHER(IsDeterministicDump, "") { + return arg.determinism == MemoryDumpDeterminism::FORCE_GC; +} + +MATCHER(IsNotDeterministicDump, "") { + return arg.determinism == MemoryDumpDeterminism::NONE; +} + namespace { const char* kMDPName = "TestDumpProvider"; @@ -193,12 +201,14 @@ class MemoryDumpManagerTest : public testing::Test { // memory dump is complete. Returns: // - return value: the |success| from the CreateProcessDump() callback. bool RequestProcessDumpAndWait(MemoryDumpType dump_type, - MemoryDumpLevelOfDetail level_of_detail) { + MemoryDumpLevelOfDetail level_of_detail, + MemoryDumpDeterminism determinism) { RunLoop run_loop; bool success = false; static uint64_t test_guid = 1; test_guid++; - MemoryDumpRequestArgs request_args{test_guid, dump_type, level_of_detail}; + MemoryDumpRequestArgs request_args{test_guid, dump_type, level_of_detail, + determinism}; // The signature of the callback delivered by MemoryDumpManager is: // void ProcessMemoryDumpCallback( @@ -276,7 +286,8 @@ TEST_F(MemoryDumpManagerTest, SingleDumper) { EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(3); for (int i = 0; i < 3; ++i) { EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED)); + MemoryDumpLevelOfDetail::DETAILED, + MemoryDumpDeterminism::NONE)); } DisableTracing(); @@ -288,7 +299,8 @@ TEST_F(MemoryDumpManagerTest, SingleDumper) { EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0); for (int i = 0; i < 3; ++i) { EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED)); + MemoryDumpLevelOfDetail::DETAILED, + MemoryDumpDeterminism::NONE)); } DisableTracing(); } @@ -302,7 +314,8 @@ TEST_F(MemoryDumpManagerTest, CheckMemoryDumpArgs) { EnableForTracing(); EXPECT_CALL(mdp, OnMemoryDump(IsDetailedDump(), _)); EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED)); + MemoryDumpLevelOfDetail::DETAILED, + MemoryDumpDeterminism::NONE)); DisableTracing(); mdm_->UnregisterDumpProvider(&mdp); @@ -312,7 +325,34 @@ TEST_F(MemoryDumpManagerTest, CheckMemoryDumpArgs) { EnableForTracing(); EXPECT_CALL(mdp, OnMemoryDump(IsLightDump(), _)); EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::LIGHT)); + MemoryDumpLevelOfDetail::LIGHT, + MemoryDumpDeterminism::NONE)); + DisableTracing(); + mdm_->UnregisterDumpProvider(&mdp); +} + +// Checks that requesting deterministic dumps actually propagates +// the deterministic option properly to OnMemoryDump() call on dump providers. +TEST_F(MemoryDumpManagerTest, CheckMemoryDumpArgsDeterministic) { + MockMemoryDumpProvider mdp; + + RegisterDumpProvider(&mdp, ThreadTaskRunnerHandle::Get()); + EnableForTracing(); + EXPECT_CALL(mdp, OnMemoryDump(IsDeterministicDump(), _)); + EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, + MemoryDumpLevelOfDetail::DETAILED, + MemoryDumpDeterminism::FORCE_GC)); + DisableTracing(); + mdm_->UnregisterDumpProvider(&mdp); + + // Check that requesting dumps with deterministic option set to false + // actually propagates to OnMemoryDump() call on dump providers. + RegisterDumpProvider(&mdp, ThreadTaskRunnerHandle::Get()); + EnableForTracing(); + EXPECT_CALL(mdp, OnMemoryDump(IsNotDeterministicDump(), _)); + EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, + MemoryDumpLevelOfDetail::LIGHT, + MemoryDumpDeterminism::NONE)); DisableTracing(); mdm_->UnregisterDumpProvider(&mdp); } @@ -328,7 +368,8 @@ TEST_F(MemoryDumpManagerTest, MultipleDumpers) { EXPECT_CALL(mdp1, OnMemoryDump(_, _)); EXPECT_CALL(mdp2, OnMemoryDump(_, _)).Times(0); EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED)); + MemoryDumpLevelOfDetail::DETAILED, + MemoryDumpDeterminism::NONE)); DisableTracing(); // Invert: enable mdp2 and disable mdp1. @@ -338,7 +379,8 @@ TEST_F(MemoryDumpManagerTest, MultipleDumpers) { EXPECT_CALL(mdp1, OnMemoryDump(_, _)).Times(0); EXPECT_CALL(mdp2, OnMemoryDump(_, _)); EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED)); + MemoryDumpLevelOfDetail::DETAILED, + MemoryDumpDeterminism::NONE)); DisableTracing(); // Enable both mdp1 and mdp2. @@ -347,7 +389,8 @@ TEST_F(MemoryDumpManagerTest, MultipleDumpers) { EXPECT_CALL(mdp1, OnMemoryDump(_, _)); EXPECT_CALL(mdp2, OnMemoryDump(_, _)); EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED)); + MemoryDumpLevelOfDetail::DETAILED, + MemoryDumpDeterminism::NONE)); DisableTracing(); } @@ -368,7 +411,8 @@ TEST_F(MemoryDumpManagerTest, MAYBE_RegistrationConsistency) { EXPECT_CALL(mdp, OnMemoryDump(_, _)); EnableForTracing(); EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED)); + MemoryDumpLevelOfDetail::DETAILED, + MemoryDumpDeterminism::NONE)); DisableTracing(); } @@ -378,7 +422,8 @@ TEST_F(MemoryDumpManagerTest, MAYBE_RegistrationConsistency) { EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0); EnableForTracing(); EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED)); + MemoryDumpLevelOfDetail::DETAILED, + MemoryDumpDeterminism::NONE)); DisableTracing(); } @@ -389,7 +434,8 @@ TEST_F(MemoryDumpManagerTest, MAYBE_RegistrationConsistency) { EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0); EnableForTracing(); EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED)); + MemoryDumpLevelOfDetail::DETAILED, + MemoryDumpDeterminism::NONE)); DisableTracing(); } @@ -401,7 +447,8 @@ TEST_F(MemoryDumpManagerTest, MAYBE_RegistrationConsistency) { EXPECT_CALL(mdp, OnMemoryDump(_, _)); EnableForTracing(); EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED)); + MemoryDumpLevelOfDetail::DETAILED, + MemoryDumpDeterminism::NONE)); DisableTracing(); } } @@ -439,7 +486,8 @@ TEST_F(MemoryDumpManagerTest, RespectTaskRunnerAffinity) { while (!threads.empty()) { EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED)); + MemoryDumpLevelOfDetail::DETAILED, + MemoryDumpDeterminism::NONE)); // Unregister a MDP and destroy one thread at each iteration to check the // live unregistration logic. The unregistration needs to happen on the same @@ -485,13 +533,15 @@ TEST_F(MemoryDumpManagerTest, PostTaskForSequencedTaskRunner) { task_runner1->set_enabled(false); EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED)); + MemoryDumpLevelOfDetail::DETAILED, + MemoryDumpDeterminism::NONE)); EXPECT_EQ(1u, task_runner1->no_of_post_tasks()); EXPECT_EQ(1u, task_runner2->no_of_post_tasks()); task_runner1->set_enabled(true); EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED)); + MemoryDumpLevelOfDetail::DETAILED, + MemoryDumpDeterminism::NONE)); EXPECT_EQ(2u, task_runner1->no_of_post_tasks()); EXPECT_EQ(2u, task_runner2->no_of_post_tasks()); DisableTracing(); @@ -522,7 +572,8 @@ TEST_F(MemoryDumpManagerTest, DisableFailingDumpers) { const int kNumDumps = 2 * GetMaxConsecutiveFailuresCount(); for (int i = 0; i < kNumDumps; i++) { EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED)); + MemoryDumpLevelOfDetail::DETAILED, + MemoryDumpDeterminism::NONE)); } DisableTracing(); @@ -553,7 +604,8 @@ TEST_F(MemoryDumpManagerTest, RegisterDumperWhileDumping) { for (int i = 0; i < 4; i++) { EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED)); + MemoryDumpLevelOfDetail::DETAILED, + MemoryDumpDeterminism::NONE)); } DisableTracing(); @@ -584,7 +636,8 @@ TEST_F(MemoryDumpManagerTest, UnregisterDumperWhileDumping) { for (int i = 0; i < 4; i++) { EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED)); + MemoryDumpLevelOfDetail::DETAILED, + MemoryDumpDeterminism::NONE)); } DisableTracing(); @@ -632,7 +685,8 @@ TEST_F(MemoryDumpManagerTest, UnregisterDumperFromThreadWhileDumping) { EnableForTracing(); EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED)); + MemoryDumpLevelOfDetail::DETAILED, + MemoryDumpDeterminism::NONE)); ASSERT_EQ(1, on_memory_dump_call_count); DisableTracing(); @@ -679,7 +733,8 @@ TEST_F(MemoryDumpManagerTest, TearDownThreadWhileDumping) { EnableForTracing(); EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED)); + MemoryDumpLevelOfDetail::DETAILED, + MemoryDumpDeterminism::NONE)); ASSERT_EQ(1, on_memory_dump_call_count); DisableTracing(); @@ -692,7 +747,8 @@ TEST_F(MemoryDumpManagerTest, TriggerDumpWithoutTracing) { RegisterDumpProvider(&mdp, nullptr); EXPECT_CALL(mdp, OnMemoryDump(_, _)); EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED)); + MemoryDumpLevelOfDetail::DETAILED, + MemoryDumpDeterminism::NONE)); } TEST_F(MemoryDumpManagerTest, BackgroundWhitelisting) { @@ -707,7 +763,8 @@ TEST_F(MemoryDumpManagerTest, BackgroundWhitelisting) { EXPECT_CALL(backgroundMdp, OnMemoryDump(_, _)).Times(1); EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::SUMMARY_ONLY, - MemoryDumpLevelOfDetail::BACKGROUND)); + MemoryDumpLevelOfDetail::BACKGROUND, + MemoryDumpDeterminism::NONE)); DisableTracing(); } @@ -769,7 +826,8 @@ TEST_F(MemoryDumpManagerTest, UnregisterAndDeleteDumpProviderSoonDuringDump) { EnableForTracing(); for (int i = 0; i < 2; ++i) { EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED)); + MemoryDumpLevelOfDetail::DETAILED, + MemoryDumpDeterminism::NONE)); } DisableTracing(); } @@ -822,11 +880,14 @@ TEST_F(MemoryDumpManagerTest, NoStackOverflowWithTooManyMDPs) { stopped_thread->Stop(); EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED)); + MemoryDumpLevelOfDetail::DETAILED, + MemoryDumpDeterminism::NONE)); EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::BACKGROUND)); + MemoryDumpLevelOfDetail::BACKGROUND, + MemoryDumpDeterminism::NONE)); EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::SUMMARY_ONLY, - MemoryDumpLevelOfDetail::BACKGROUND)); + MemoryDumpLevelOfDetail::BACKGROUND, + MemoryDumpDeterminism::NONE)); } } // namespace trace_event diff --git a/chromium/base/trace_event/memory_dump_request_args.h b/chromium/base/trace_event/memory_dump_request_args.h index c50a1cb2cfc..d62a0dc84c8 100644 --- a/chromium/base/trace_event/memory_dump_request_args.h +++ b/chromium/base/trace_event/memory_dump_request_args.h @@ -58,6 +58,12 @@ enum class MemoryDumpLevelOfDetail : uint32_t { LAST = DETAILED }; +// Tells the MemoryDumpProvider(s) if they should try to make the result +// more deterministic by forcing garbage collection. +// Keep this consistent with memory_instrumentation.mojo and +// memory_instrumentation_struct_traits.{h,cc} +enum class MemoryDumpDeterminism : uint32_t { NONE, FORCE_GC }; + // Keep this consistent with memory_instrumentation.mojo and // memory_instrumentation_struct_traits.{h,cc} struct BASE_EXPORT MemoryDumpRequestArgs { @@ -68,6 +74,7 @@ struct BASE_EXPORT MemoryDumpRequestArgs { MemoryDumpType dump_type; MemoryDumpLevelOfDetail level_of_detail; + MemoryDumpDeterminism determinism; }; // Args for ProcessMemoryDump and passed to OnMemoryDump calls for memory dump @@ -75,6 +82,8 @@ struct BASE_EXPORT MemoryDumpRequestArgs { struct MemoryDumpArgs { // Specifies how detailed the dumps should be. MemoryDumpLevelOfDetail level_of_detail; + // Specifies whether the dumps should be more deterministic. + MemoryDumpDeterminism determinism; // Globally unique identifier. In multi-process dumps, all processes issue a // local dump with the same guid. This allows the trace importers to diff --git a/chromium/base/trace_event/memory_infra_background_whitelist.cc b/chromium/base/trace_event/memory_infra_background_whitelist.cc index 3874f4521ea..4c88b5ca4a5 100644 --- a/chromium/base/trace_event/memory_infra_background_whitelist.cc +++ b/chromium/base/trace_event/memory_infra_background_whitelist.cc @@ -67,8 +67,8 @@ const char* const kDumpProviderWhitelist[] = { // A list of string names that are allowed for the memory allocator dumps in // background mode. const char* const kAllocatorDumpNameWhitelist[] = { - "blink_gc", - "blink_gc/allocated_objects", + "blink_gc/main/heap", + "blink_gc/workers/heap/worker_0x?", "blink_objects/AdSubframe", "blink_objects/AudioHandler", "blink_objects/ContextLifecycleStateObserver", @@ -344,6 +344,8 @@ const char* const kAllocatorDumpNameWhitelist[] = { "sync/0x?/model_type/MANAGED_USER_WHITELIST", "sync/0x?/model_type/MOUNTAIN_SHARE", "sync/0x?/model_type/NIGORI", + "sync/0x?/model_type/OS_PREFERENCE", + "sync/0x?/model_type/OS_PRIORITY_PREFERENCE", "sync/0x?/model_type/PASSWORD", "sync/0x?/model_type/PREFERENCE", "sync/0x?/model_type/PRINTER", diff --git a/chromium/base/trace_event/trace_config.cc b/chromium/base/trace_event/trace_config.cc index 061100c23ad..5b4493f1bd6 100644 --- a/chromium/base/trace_event/trace_config.cc +++ b/chromium/base/trace_event/trace_config.cc @@ -28,6 +28,8 @@ const char kRecordContinuously[] = "record-continuously"; const char kRecordAsMuchAsPossible[] = "record-as-much-as-possible"; const char kTraceToConsole[] = "trace-to-console"; const char kEnableSystrace[] = "enable-systrace"; +constexpr int kEnableSystraceLength = sizeof(kEnableSystrace) - 1; + const char kEnableArgumentFilter[] = "enable-argument-filter"; // String parameters that can be used to parse the trace config string. @@ -35,6 +37,7 @@ const char kRecordModeParam[] = "record_mode"; const char kTraceBufferSizeInEvents[] = "trace_buffer_size_in_events"; const char kTraceBufferSizeInKb[] = "trace_buffer_size_in_kb"; const char kEnableSystraceParam[] = "enable_systrace"; +const char kSystraceEventsParam[] = "enable_systrace_events"; const char kEnableArgumentFilterParam[] = "enable_argument_filter"; // String parameters that is used to parse memory dump config in trace config @@ -163,7 +166,7 @@ void TraceConfig::ProcessFilterConfig::ToDict(Value* dict) const { std::set<base::ProcessId> ordered_set(included_process_ids_.begin(), included_process_ids_.end()); for (auto process_id : ordered_set) - list->GetList().emplace_back(static_cast<int>(process_id)); + list->Append(static_cast<int>(process_id)); } bool TraceConfig::ProcessFilterConfig::IsEnabled( @@ -298,6 +301,7 @@ TraceConfig& TraceConfig::operator=(const TraceConfig& rhs) { memory_dump_config_ = rhs.memory_dump_config_; event_filters_ = rhs.event_filters_; histogram_names_ = rhs.histogram_names_; + systrace_events_ = rhs.systrace_events_; return *this; } @@ -355,6 +359,7 @@ void TraceConfig::Clear() { process_filter_config_.Clear(); event_filters_.clear(); histogram_names_.clear(); + systrace_events_.clear(); } void TraceConfig::InitializeDefault() { @@ -406,6 +411,15 @@ void TraceConfig::InitializeFromConfigDict(const Value& dict) { else SetDefaultMemoryDumpConfig(); } + + systrace_events_.clear(); + if (enable_systrace_) { + const Value* systrace_events = dict.FindListKey(kSystraceEventsParam); + if (systrace_events) { + for (const Value& value : systrace_events->GetList()) + systrace_events_.insert(value.GetString()); + } + } } void TraceConfig::InitializeFromConfigString(StringPiece config_string) { @@ -425,6 +439,7 @@ void TraceConfig::InitializeFromStrings(StringPiece category_filter_string, trace_buffer_size_in_events_ = 0; trace_buffer_size_in_kb_ = 0; enable_systrace_ = false; + systrace_events_.clear(); enable_argument_filter_ = false; if (!trace_options_string.empty()) { std::vector<std::string> split = @@ -438,8 +453,27 @@ void TraceConfig::InitializeFromStrings(StringPiece category_filter_string, record_mode_ = ECHO_TO_CONSOLE; } else if (token == kRecordAsMuchAsPossible) { record_mode_ = RECORD_AS_MUCH_AS_POSSIBLE; - } else if (token == kEnableSystrace) { + } else if (token.find(kEnableSystrace) == 0) { + // Find optional events list. + const size_t length = token.length(); + if (length == kEnableSystraceLength) { + // Use all predefined categories. + enable_systrace_ = true; + continue; + } + const auto system_events_not_trimmed = + token.substr(kEnableSystraceLength); + const auto system_events = + TrimString(system_events_not_trimmed, kWhitespaceASCII, TRIM_ALL); + if (system_events[0] != '=') { + LOG(ERROR) << "Failed to parse " << token; + continue; + } enable_systrace_ = true; + const std::vector<std::string> split_systrace_events = SplitString( + system_events.substr(1), " ", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY); + for (const std::string& systrace_event : split_systrace_events) + systrace_events_.insert(systrace_event); } else if (token == kEnableArgumentFilter) { enable_argument_filter_ = true; } @@ -622,9 +656,22 @@ Value TraceConfig::ToValue() const { dict.SetKey(kHistogramNamesParam, Value(std::move(histogram_names))); } + if (enable_systrace_) { + if (!systrace_events_.empty()) { + std::vector<Value> systrace_events; + for (const std::string& systrace_event : systrace_events_) + systrace_events.emplace_back(systrace_event); + dict.SetKey(kSystraceEventsParam, Value(std::move(systrace_events))); + } + } + return dict; } +void TraceConfig::EnableSystraceEvent(const std::string& systrace_event) { + systrace_events_.insert(systrace_event); +} + void TraceConfig::EnableHistogram(const std::string& histogram_name) { histogram_names_.insert(histogram_name); } @@ -647,10 +694,24 @@ std::string TraceConfig::ToTraceOptionsString() const { default: NOTREACHED(); } - if (enable_systrace_) - ret = ret + "," + kEnableSystrace; - if (enable_argument_filter_) - ret = ret + "," + kEnableArgumentFilter; + if (enable_systrace_) { + ret += ","; + ret += kEnableSystrace; + bool first_param = true; + for (const std::string& systrace_event : systrace_events_) { + if (first_param) { + ret += "="; + first_param = false; + } else { + ret += " "; + } + ret = ret + systrace_event; + } + } + if (enable_argument_filter_) { + ret += ","; + ret += kEnableArgumentFilter; + } return ret; } diff --git a/chromium/base/trace_event/trace_config.h b/chromium/base/trace_event/trace_config.h index 9798c7f6ae9..3e3d0078a7f 100644 --- a/chromium/base/trace_event/trace_config.h +++ b/chromium/base/trace_event/trace_config.h @@ -242,6 +242,7 @@ class BASE_EXPORT TraceConfig { } void SetTraceBufferSizeInKb(size_t size) { trace_buffer_size_in_kb_ = size; } void EnableSystrace() { enable_systrace_ = true; } + void EnableSystraceEvent(const std::string& systrace_event); void EnableArgumentFilter() { enable_argument_filter_ = true; } void EnableHistogram(const std::string& histogram_name); @@ -255,6 +256,11 @@ class BASE_EXPORT TraceConfig { // Write the string representation of the CategoryFilter part. std::string ToCategoryFilterString() const; + // Write the string representation of the trace options part (record mode, + // systrace, argument filtering). Does not include category filters, event + // filters, or memory dump configs. + std::string ToTraceOptionsString() const; + // Returns true if at least one category in the list is enabled by this // trace config. This is used to determine if the category filters are // enabled in the TRACE_* macros. @@ -286,6 +292,10 @@ class BASE_EXPORT TraceConfig { event_filters_ = filter_configs; } + const std::unordered_set<std::string>& systrace_events() const { + return systrace_events_; + } + const std::unordered_set<std::string>& histogram_names() const { return histogram_names_; } @@ -294,6 +304,7 @@ class BASE_EXPORT TraceConfig { FRIEND_TEST_ALL_PREFIXES(TraceConfigTest, TraceConfigFromValidLegacyFormat); FRIEND_TEST_ALL_PREFIXES(TraceConfigTest, TraceConfigFromInvalidLegacyStrings); + FRIEND_TEST_ALL_PREFIXES(TraceConfigTest, SystraceEventsSerialization); // The default trace config, used when none is provided. // Allows all non-disabled-by-default categories through, except if they end @@ -317,8 +328,6 @@ class BASE_EXPORT TraceConfig { void SetEventFiltersFromConfigList(const Value& event_filters); Value ToValue() const; - std::string ToTraceOptionsString() const; - TraceRecordMode record_mode_; size_t trace_buffer_size_in_events_ = 0; // 0 specifies default size size_t trace_buffer_size_in_kb_ = 0; // 0 specifies default size @@ -332,6 +341,7 @@ class BASE_EXPORT TraceConfig { EventFilters event_filters_; std::unordered_set<std::string> histogram_names_; + std::unordered_set<std::string> systrace_events_; }; } // namespace trace_event diff --git a/chromium/base/trace_event/trace_config_unittest.cc b/chromium/base/trace_event/trace_config_unittest.cc index c7e98a4ce64..16cd67ecd9e 100644 --- a/chromium/base/trace_event/trace_config_unittest.cc +++ b/chromium/base/trace_event/trace_config_unittest.cc @@ -674,5 +674,25 @@ TEST(TraceConfigTest, LegacyStringToMemoryDumpConfig) { tc.memory_dump_config().heap_profiler_options.breakdown_threshold_bytes); } +TEST(TraceConfigTest, SystraceEventsSerialization) { + TraceConfig tc(MemoryDumpManager::kTraceCategory, ""); + tc.EnableSystrace(); + EXPECT_EQ(0U, tc.systrace_events().size()); + tc.EnableSystraceEvent("power"); // As a events category + tc.EnableSystraceEvent("timer:tick_stop"); // As an event + EXPECT_EQ(2U, tc.systrace_events().size()); + + const TraceConfig tc1(MemoryDumpManager::kTraceCategory, + tc.ToTraceOptionsString()); + EXPECT_EQ(2U, tc1.systrace_events().size()); + EXPECT_TRUE(tc1.systrace_events().count("power")); + EXPECT_TRUE(tc1.systrace_events().count("timer:tick_stop")); + + const TraceConfig tc2(tc.ToString()); + EXPECT_EQ(2U, tc2.systrace_events().size()); + EXPECT_TRUE(tc2.systrace_events().count("power")); + EXPECT_TRUE(tc2.systrace_events().count("timer:tick_stop")); +} + } // namespace trace_event } // namespace base diff --git a/chromium/base/trace_event/trace_log.cc b/chromium/base/trace_event/trace_log.cc index 39c19c8e46b..1e0cd1413c4 100644 --- a/chromium/base/trace_event/trace_log.cc +++ b/chromium/base/trace_event/trace_log.cc @@ -378,7 +378,7 @@ TraceLog::TraceLog() num_traces_recorded_(0), process_sort_index_(0), process_id_hash_(0), - process_id_(0), + process_id_(base::kNullProcessId), trace_options_(kInternalRecordUntilFull), trace_config_(TraceConfig()), thread_shared_chunk_index_(0), diff --git a/chromium/base/trace_event/trace_log.h b/chromium/base/trace_event/trace_log.h index 63be5aa4649..e0c850ca41e 100644 --- a/chromium/base/trace_event/trace_log.h +++ b/chromium/base/trace_event/trace_log.h @@ -301,6 +301,7 @@ class BASE_EXPORT TraceLog : public MemoryDumpProvider { TraceEventHandle handle); int process_id() const { return process_id_; } + const std::string& process_name() const { return process_name_; } uint64_t MangleEventId(uint64_t id); diff --git a/chromium/base/trace_event/traced_value.cc b/chromium/base/trace_event/traced_value.cc index b1ea5aabeac..5b6467a4a15 100644 --- a/chromium/base/trace_event/traced_value.cc +++ b/chromium/base/trace_event/traced_value.cc @@ -341,7 +341,7 @@ class PickleWriter final : public TracedValue::Writer { stack.push_back(cur_dict); cur_dict = cur_dict->SetKey(ReadKeyName(it), std::move(new_dict)); } else { - cur_list->GetList().push_back(std::move(new_dict)); + cur_list->Append(std::move(new_dict)); // |new_dict| is invalidated at this point, so |cur_dict| needs to // be reset. cur_dict = &cur_list->GetList().back(); @@ -369,7 +369,7 @@ class PickleWriter final : public TracedValue::Writer { cur_list = cur_dict->SetKey(ReadKeyName(it), std::move(new_list)); cur_dict = nullptr; } else { - cur_list->GetList().push_back(std::move(new_list)); + cur_list->Append(std::move(new_list)); stack.push_back(cur_list); // |cur_list| is invalidated at this point by the Append, so it // needs to be reset. @@ -383,7 +383,7 @@ class PickleWriter final : public TracedValue::Writer { if (cur_dict) { cur_dict->SetBoolKey(ReadKeyName(it), value); } else { - cur_list->GetList().emplace_back(value); + cur_list->Append(value); } } break; @@ -393,7 +393,7 @@ class PickleWriter final : public TracedValue::Writer { if (cur_dict) { cur_dict->SetIntKey(ReadKeyName(it), value); } else { - cur_list->GetList().emplace_back(value); + cur_list->Append(value); } } break; @@ -403,7 +403,7 @@ class PickleWriter final : public TracedValue::Writer { if (cur_dict) { cur_dict->SetDoubleKey(ReadKeyName(it), value); } else { - cur_list->GetList().emplace_back(value); + cur_list->Append(value); } } break; @@ -413,7 +413,7 @@ class PickleWriter final : public TracedValue::Writer { if (cur_dict) { cur_dict->SetStringKey(ReadKeyName(it), std::move(value)); } else { - cur_list->GetList().emplace_back(std::move(value)); + cur_list->Append(std::move(value)); } } break; diff --git a/chromium/base/unguessable_token.h b/chromium/base/unguessable_token.h index 222177a9a1e..895dbc46c4c 100644 --- a/chromium/base/unguessable_token.h +++ b/chromium/base/unguessable_token.h @@ -20,26 +20,30 @@ namespace base { struct UnguessableTokenHash; // UnguessableToken is, like Token, a randomly chosen 128-bit value. Unlike -// Token however, a new UnguessableToken must always be generated at runtime -// from a cryptographically strong random source (or copied or serialized and +// Token, a new UnguessableToken is always generated at runtime from a +// cryptographically strong random source (or copied or serialized and // deserialized from another such UnguessableToken). It can be used as part of a // larger aggregate type, or as an ID in and of itself. // -// UnguessableToken can be used to implement "Capability-Based Security". -// In other words, UnguessableToken can be used when the resource associated -// with the ID needs to be protected against manipulation by other untrusted -// agents in the system, and there is no other convenient way to verify the -// authority of the agent to do so (because the resource is part of a table -// shared across processes, for instance). In such a scheme, knowledge of the -// token value in and of itself is sufficient proof of authority to carry out -// an operation against the associated resource. +// An UnguessableToken is a strong *bearer token*. Bearer tokens are like HTTP +// cookies: if a caller has the token, the callee thereby considers the caller +// authorized to request the operation the callee performs. +// +// UnguessableToken can be used when the resource associated with the ID needs +// to be protected against manipulation by other untrusted agents in the system, +// and there is no other convenient way to verify the authority of the agent to +// do so (because the resource is part of a table shared across processes, for +// instance). In such a scheme, knowledge of the token value in and of itself is +// sufficient proof of authority to carry out an operation on the associated +// resource. // // Use Create() for creating new UnguessableTokens. // // NOTE: It is illegal to send empty UnguessableTokens across processes, and -// sending/receiving empty tokens should be treated as a security issue. -// If there is a valid scenario for sending "no token" across processes, -// base::Optional should be used instead of an empty token. +// sending/receiving empty tokens should be treated as a security issue. If +// there is a valid scenario for sending "no token" across processes, use +// base::Optional instead of an empty token. + class BASE_EXPORT UnguessableToken { public: // Create a unique UnguessableToken. diff --git a/chromium/base/util/memory_pressure/BUILD.gn b/chromium/base/util/memory_pressure/BUILD.gn index 6065c9a7a4f..16bb4784b54 100644 --- a/chromium/base/util/memory_pressure/BUILD.gn +++ b/chromium/base/util/memory_pressure/BUILD.gn @@ -41,3 +41,16 @@ source_set("unittests") { "//testing/gtest", ] } + +static_library("test_support") { + testonly = true + sources = [ + "fake_memory_pressure_monitor.cc", + "fake_memory_pressure_monitor.h", + ] + + public_deps = [ + ":memory_pressure", + "//base", + ] +} diff --git a/chromium/base/memory/fake_memory_pressure_monitor.cc b/chromium/base/util/memory_pressure/fake_memory_pressure_monitor.cc index 59fd3ef66eb..b9273c5a7ac 100644 --- a/chromium/base/memory/fake_memory_pressure_monitor.cc +++ b/chromium/base/util/memory_pressure/fake_memory_pressure_monitor.cc @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/memory/fake_memory_pressure_monitor.h" +#include "base/util/memory_pressure/fake_memory_pressure_monitor.h" -namespace base { +namespace util { namespace test { FakeMemoryPressureMonitor::FakeMemoryPressureMonitor() - : MemoryPressureMonitor(), + : MultiSourceMemoryPressureMonitor(), memory_pressure_level_(MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_NONE) {} FakeMemoryPressureMonitor::~FakeMemoryPressureMonitor() {} @@ -30,4 +30,4 @@ void FakeMemoryPressureMonitor::SetDispatchCallback( } } // namespace test -} // namespace base +} // namespace util diff --git a/chromium/base/memory/fake_memory_pressure_monitor.h b/chromium/base/util/memory_pressure/fake_memory_pressure_monitor.h index d012876d54e..e972d08c001 100644 --- a/chromium/base/memory/fake_memory_pressure_monitor.h +++ b/chromium/base/util/memory_pressure/fake_memory_pressure_monitor.h @@ -2,17 +2,23 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_MEMORY_FAKE_MEMORY_PRESSURE_MONITOR_H_ -#define BASE_MEMORY_FAKE_MEMORY_PRESSURE_MONITOR_H_ +#ifndef BASE_UTIL_MEMORY_PRESSURE_FAKE_MEMORY_PRESSURE_MONITOR_H_ +#define BASE_UTIL_MEMORY_PRESSURE_FAKE_MEMORY_PRESSURE_MONITOR_H_ #include "base/macros.h" -#include "base/memory/memory_pressure_monitor.h" +#include "base/util/memory_pressure/multi_source_memory_pressure_monitor.h" -namespace base { +namespace util { namespace test { -class FakeMemoryPressureMonitor : public base::MemoryPressureMonitor { +class FakeMemoryPressureMonitor + : public ::util::MultiSourceMemoryPressureMonitor { public: + using MemoryPressureLevel = + ::util::MultiSourceMemoryPressureMonitor::MemoryPressureLevel; + using DispatchCallback = + ::util::MultiSourceMemoryPressureMonitor::DispatchCallback; + FakeMemoryPressureMonitor(); ~FakeMemoryPressureMonitor() override; @@ -29,6 +35,6 @@ class FakeMemoryPressureMonitor : public base::MemoryPressureMonitor { }; } // namespace test -} // namespace base +} // namespace util -#endif // BASE_MEMORY_FAKE_MEMORY_PRESSURE_MONITOR_H_ +#endif // BASE_UTIL_MEMORY_PRESSURE_FAKE_MEMORY_PRESSURE_MONITOR_H_ diff --git a/chromium/base/util/memory_pressure/multi_source_memory_pressure_monitor.cc b/chromium/base/util/memory_pressure/multi_source_memory_pressure_monitor.cc index e8c59d1ea70..2b9a2c43eb0 100644 --- a/chromium/base/util/memory_pressure/multi_source_memory_pressure_monitor.cc +++ b/chromium/base/util/memory_pressure/multi_source_memory_pressure_monitor.cc @@ -17,16 +17,23 @@ MultiSourceMemoryPressureMonitor::MultiSourceMemoryPressureMonitor() dispatch_callback_(base::BindRepeating( &base::MemoryPressureListener::NotifyMemoryPressure)), aggregator_(this) { - // This can't be in the parameter list because |sequence_checker_| wouldn't be - // available, which would be needed by the |system_evaluator_|'s constructor's - // call to CreateVoter(). +} + +MultiSourceMemoryPressureMonitor::~MultiSourceMemoryPressureMonitor() { + // Destroy system evaluator early while the remaining members of this class + // still exist. MultiSourceMemoryPressureMonitor implements + // MemoryPressureVoteAggregator::Delegate, and + // delegate_->OnMemoryPressureLevelChanged() gets indirectly called during + // ~SystemMemoryPressureEvaluator(). + system_evaluator_.reset(); +} + +void MultiSourceMemoryPressureMonitor::Start() { system_evaluator_ = SystemMemoryPressureEvaluator::CreateDefaultSystemEvaluator(this); StartMetricsTimer(); } -MultiSourceMemoryPressureMonitor::~MultiSourceMemoryPressureMonitor() = default; - void MultiSourceMemoryPressureMonitor::StartMetricsTimer() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); // Unretained is safe here since this task is running on a timer owned by this diff --git a/chromium/base/util/memory_pressure/multi_source_memory_pressure_monitor.h b/chromium/base/util/memory_pressure/multi_source_memory_pressure_monitor.h index 3787ed9d4d9..078d58a4320 100644 --- a/chromium/base/util/memory_pressure/multi_source_memory_pressure_monitor.h +++ b/chromium/base/util/memory_pressure/multi_source_memory_pressure_monitor.h @@ -30,6 +30,9 @@ class MultiSourceMemoryPressureMonitor MultiSourceMemoryPressureMonitor(); ~MultiSourceMemoryPressureMonitor() override; + // Start monitoring memory pressure using the platform-specific voter. + void Start(); + // MemoryPressureMonitor implementation. MemoryPressureLevel GetCurrentPressureLevel() const override; void SetDispatchCallback(const DispatchCallback& callback) override; diff --git a/chromium/base/util/memory_pressure/multi_source_memory_pressure_monitor_unittest.cc b/chromium/base/util/memory_pressure/multi_source_memory_pressure_monitor_unittest.cc index 8c119714736..d796e2168c3 100644 --- a/chromium/base/util/memory_pressure/multi_source_memory_pressure_monitor_unittest.cc +++ b/chromium/base/util/memory_pressure/multi_source_memory_pressure_monitor_unittest.cc @@ -14,6 +14,7 @@ namespace util { TEST(MultiSourceMemoryPressureMonitorTest, RunDispatchCallback) { base::test::TaskEnvironment task_environment; MultiSourceMemoryPressureMonitor monitor; + monitor.Start(); auto* aggregator = monitor.aggregator_for_testing(); bool callback_called = false; diff --git a/chromium/base/util/memory_pressure/system_memory_pressure_evaluator_win_unittest.cc b/chromium/base/util/memory_pressure/system_memory_pressure_evaluator_win_unittest.cc index 8ef9d6a09b5..3e0297366b6 100644 --- a/chromium/base/util/memory_pressure/system_memory_pressure_evaluator_win_unittest.cc +++ b/chromium/base/util/memory_pressure/system_memory_pressure_evaluator_win_unittest.cc @@ -146,7 +146,8 @@ class WinSystemMemoryPressureEvaluatorTest : public testing::Test { evaluator->CalculateCurrentPressureLevel()); } - base::MessageLoopForUI message_loop_; + base::test::SingleThreadTaskEnvironment task_environment_{ + base::test::SingleThreadTaskEnvironment::MainThreadType::UI}; }; // Tests the fundamental direct calculation of memory pressure with automatic diff --git a/chromium/base/values.cc b/chromium/base/values.cc index 02e92b7e082..6886b846e8d 100644 --- a/chromium/base/values.cc +++ b/chromium/base/values.cc @@ -58,7 +58,7 @@ std::unique_ptr<Value> CopyListWithoutEmptyChildren(const Value& list) { for (const auto& entry : list.GetList()) { std::unique_ptr<Value> child_copy = CopyWithoutEmptyChildren(entry); if (child_copy) - copy.GetList().push_back(std::move(*child_copy)); + copy.Append(std::move(*child_copy)); } return copy.GetList().empty() ? nullptr : std::make_unique<Value>(std::move(copy)); @@ -147,6 +147,18 @@ std::unique_ptr<Value> Value::ToUniquePtrValue(Value val) { return std::make_unique<Value>(std::move(val)); } +// static +const DictionaryValue& Value::AsDictionaryValue(const Value& val) { + CHECK(val.is_dict()); + return static_cast<const DictionaryValue&>(val); +} + +// static +const ListValue& Value::AsListValue(const Value& val) { + CHECK(val.is_list()); + return static_cast<const ListValue&>(val); +} + Value::Value(Value&& that) noexcept { InternalMoveConstructFrom(std::move(that)); } @@ -234,7 +246,7 @@ Value::Value(const DictStorage& in_dict) : type_(Type::DICTIONARY), dict_() { Value::Value(DictStorage&& in_dict) noexcept : type_(Type::DICTIONARY), dict_(std::move(in_dict)) {} -Value::Value(const ListStorage& in_list) : type_(Type::LIST), list_() { +Value::Value(span<const Value> in_list) : type_(Type::LIST), list_() { list_.reserve(in_list.size()); for (const auto& val : in_list) list_.emplace_back(val.Clone()); @@ -335,11 +347,79 @@ Value::ListStorage& Value::GetList() { return list_; } -const Value::ListStorage& Value::GetList() const { +span<const Value> Value::GetList() const { CHECK(is_list()); return list_; } +Value::ListStorage Value::TakeList() { + CHECK(is_list()); + return std::exchange(list_, ListStorage()); +} + +void Value::Append(bool value) { + CHECK(is_list()); + list_.emplace_back(value); +} + +void Value::Append(int value) { + CHECK(is_list()); + list_.emplace_back(value); +} + +void Value::Append(double value) { + CHECK(is_list()); + list_.emplace_back(value); +} + +void Value::Append(const char* value) { + CHECK(is_list()); + list_.emplace_back(value); +} + +void Value::Append(StringPiece value) { + CHECK(is_list()); + list_.emplace_back(value); +} + +void Value::Append(std::string&& value) { + CHECK(is_list()); + list_.emplace_back(std::move(value)); +} + +void Value::Append(const char16* value) { + CHECK(is_list()); + list_.emplace_back(value); +} + +void Value::Append(StringPiece16 value) { + CHECK(is_list()); + list_.emplace_back(value); +} + +void Value::Append(Value&& value) { + CHECK(is_list()); + list_.emplace_back(std::move(value)); +} + +bool Value::EraseListIter(ListStorage::const_iterator iter) { + CHECK(is_list()); + if (iter == list_.end()) + return false; + + list_.erase(iter); + return true; +} + +bool Value::EraseListIter(CheckedContiguousConstIterator<Value> iter) { + const auto offset = iter - static_cast<const Value*>(this)->GetList().begin(); + return EraseListIter(list_.begin() + offset); +} + +size_t Value::EraseListValue(const Value& val) { + return EraseListValueIf([&val](const Value& other) { return val == other; }); +} + Value* Value::FindKey(StringPiece key) { return const_cast<Value*>(static_cast<const Value*>(this)->FindKey(key)); } @@ -1535,7 +1615,7 @@ std::unique_ptr<ListValue> ListValue::From(std::unique_ptr<Value> value) { } ListValue::ListValue() : Value(Type::LIST) {} -ListValue::ListValue(const ListStorage& in_list) : Value(in_list) {} +ListValue::ListValue(span<const Value> in_list) : Value(in_list) {} ListValue::ListValue(ListStorage&& in_list) noexcept : Value(std::move(in_list)) {} diff --git a/chromium/base/values.h b/chromium/base/values.h index 38e4f37d68a..ccf87bfd88c 100644 --- a/chromium/base/values.h +++ b/chromium/base/values.h @@ -32,6 +32,7 @@ #include <vector> #include "base/base_export.h" +#include "base/containers/checked_iterators.h" #include "base/containers/flat_map.h" #include "base/containers/span.h" #include "base/macros.h" @@ -111,6 +112,8 @@ class BASE_EXPORT Value { // Adaptors for converting from the old way to the new way and vice versa. static Value FromUniquePtrValue(std::unique_ptr<Value> val); static std::unique_ptr<Value> ToUniquePtrValue(Value val); + static const DictionaryValue& AsDictionaryValue(const Value& val); + static const ListValue& AsListValue(const Value& val); Value(Value&& that) noexcept; Value() noexcept {} // A null value @@ -144,7 +147,7 @@ class BASE_EXPORT Value { explicit Value(const DictStorage& in_dict); explicit Value(DictStorage&& in_dict) noexcept; - explicit Value(const ListStorage& in_list); + explicit Value(span<const Value> in_list); explicit Value(ListStorage&& in_list) noexcept; Value& operator=(Value&& that) noexcept; @@ -176,7 +179,46 @@ class BASE_EXPORT Value { const BlobStorage& GetBlob() const; ListStorage& GetList(); - const ListStorage& GetList() const; + span<const Value> GetList() const; + + // Transfers ownership of the the underlying list to the caller. Subsequent + // calls to GetList() will return an empty list. + // Note: This CHECKs that type() is Type::LIST. + ListStorage TakeList(); + + // Appends |value| to the end of the list. + // Note: These CHECK that type() is Type::LIST. + void Append(bool value); + void Append(int value); + void Append(double value); + void Append(const char* value); + void Append(StringPiece value); + void Append(std::string&& value); + void Append(const char16* value); + void Append(StringPiece16 value); + void Append(Value&& value); + + // Erases the Value pointed to by |iter|. Returns false if |iter| is out of + // bounds. + // Note: This CHECKs that type() is Type::LIST. + bool EraseListIter(ListStorage::const_iterator iter); + bool EraseListIter(CheckedContiguousConstIterator<Value> iter); + + // Erases all Values that compare equal to |val|. Returns the number of + // deleted Values. + // Note: This CHECKs that type() is Type::LIST. + size_t EraseListValue(const Value& val); + + // Erases all Values for which |pred| returns true. Returns the number of + // deleted Values. + // Note: This CHECKs that type() is Type::LIST. + template <typename Predicate> + size_t EraseListValueIf(Predicate pred) { + CHECK(is_list()); + const size_t old_size = list_.size(); + base::EraseIf(list_, pred); + return old_size - list_.size(); + } // |FindKey| looks up |key| in the underlying dictionary. If found, it returns // a pointer to the element. Otherwise it returns nullptr. @@ -723,7 +765,7 @@ class BASE_EXPORT ListValue : public Value { static std::unique_ptr<ListValue> From(std::unique_ptr<Value> value); ListValue(); - explicit ListValue(const ListStorage& in_list); + explicit ListValue(span<const Value> in_list); explicit ListValue(ListStorage&& in_list) noexcept; // Clears the contents of this ListValue @@ -803,24 +845,25 @@ class BASE_EXPORT ListValue : public Value { // DEPRECATED, use GetList()::erase() instead. iterator Erase(iterator iter, std::unique_ptr<Value>* out_value); + using Value::Append; // Appends a Value to the end of the list. - // DEPRECATED, use GetList()::push_back() instead. + // DEPRECATED, use Value::Append() instead. void Append(std::unique_ptr<Value> in_value); // Convenience forms of Append. - // DEPRECATED, use GetList()::emplace_back() instead. + // DEPRECATED, use Value::Append() instead. void AppendBoolean(bool in_value); void AppendInteger(int in_value); void AppendDouble(double in_value); void AppendString(StringPiece in_value); void AppendString(const string16& in_value); - // DEPRECATED, use GetList()::emplace_back() in a loop instead. + // DEPRECATED, use Value::Append() in a loop instead. void AppendStrings(const std::vector<std::string>& in_values); void AppendStrings(const std::vector<string16>& in_values); // Appends a Value if it's not already present. Returns true if successful, // or false if the value was already - // DEPRECATED, use std::find() with GetList()::push_back() instead. + // DEPRECATED, use std::find() with Value::Append() instead. bool AppendIfNotPresent(std::unique_ptr<Value> in_value); // Insert a Value at index. diff --git a/chromium/base/values_unittest.cc b/chromium/base/values_unittest.cc index c8be7e5fd35..2ae7ae833a7 100644 --- a/chromium/base/values_unittest.cc +++ b/chromium/base/values_unittest.cc @@ -458,6 +458,133 @@ TEST(ValuesTest, MoveList) { EXPECT_EQ(123, blank.GetList().back().GetInt()); } +TEST(ValuesTest, TakeList) { + // Prepare a list with a value of each type. + ListValue value; + value.Append(Value(Value::Type::NONE)); + value.Append(Value(Value::Type::BOOLEAN)); + value.Append(Value(Value::Type::INTEGER)); + value.Append(Value(Value::Type::DOUBLE)); + value.Append(Value(Value::Type::STRING)); + value.Append(Value(Value::Type::BINARY)); + value.Append(Value(Value::Type::LIST)); + value.Append(Value(Value::Type::DICTIONARY)); + + // Take ownership of the list and make sure its contents are what we expect. + auto list = value.TakeList(); + EXPECT_EQ(8u, list.size()); + EXPECT_TRUE(list[0].is_none()); + EXPECT_TRUE(list[1].is_bool()); + EXPECT_TRUE(list[2].is_int()); + EXPECT_TRUE(list[3].is_double()); + EXPECT_TRUE(list[4].is_string()); + EXPECT_TRUE(list[5].is_blob()); + EXPECT_TRUE(list[6].is_list()); + EXPECT_TRUE(list[7].is_dict()); + + // Validate that |value| no longer contains values. + EXPECT_TRUE(value.GetList().empty()); +} + +TEST(ValuesTest, Append) { + ListValue value; + value.Append(true); + EXPECT_TRUE(value.GetList().back().is_bool()); + + value.Append(123); + EXPECT_TRUE(value.GetList().back().is_int()); + + value.Append(3.14); + EXPECT_TRUE(value.GetList().back().is_double()); + + std::string str = "foo"; + value.Append(str.c_str()); + EXPECT_TRUE(value.GetList().back().is_string()); + + value.Append(StringPiece(str)); + EXPECT_TRUE(value.GetList().back().is_string()); + + value.Append(std::move(str)); + EXPECT_TRUE(value.GetList().back().is_string()); + + string16 str16 = ASCIIToUTF16("bar"); + value.Append(str16.c_str()); + EXPECT_TRUE(value.GetList().back().is_string()); + + value.Append(base::StringPiece16(str16)); + EXPECT_TRUE(value.GetList().back().is_string()); + + value.Append(Value()); + EXPECT_TRUE(value.GetList().back().is_none()); + + value.Append(Value(Value::Type::DICTIONARY)); + EXPECT_TRUE(value.GetList().back().is_dict()); + + value.Append(Value(Value::Type::LIST)); + EXPECT_TRUE(value.GetList().back().is_list()); +} + +TEST(ValuesTest, EraseListIter) { + ListValue value; + value.Append(1); + value.Append(2); + value.Append(3); + + EXPECT_TRUE(value.EraseListIter(value.GetList().begin() + 1)); + EXPECT_EQ(2u, value.GetList().size()); + EXPECT_EQ(1, value.GetList()[0].GetInt()); + EXPECT_EQ(3, value.GetList()[1].GetInt()); + + EXPECT_TRUE(value.EraseListIter(value.GetList().begin())); + EXPECT_EQ(1u, value.GetList().size()); + EXPECT_EQ(3, value.GetList()[0].GetInt()); + + EXPECT_TRUE(value.EraseListIter(value.GetList().begin())); + EXPECT_TRUE(value.GetList().empty()); + + EXPECT_FALSE(value.EraseListIter(value.GetList().begin())); +} + +TEST(ValuesTest, EraseListValue) { + ListValue value; + value.Append(1); + value.Append(2); + value.Append(2); + value.Append(3); + + EXPECT_EQ(2u, value.EraseListValue(Value(2))); + EXPECT_EQ(2u, value.GetList().size()); + EXPECT_EQ(1, value.GetList()[0].GetInt()); + EXPECT_EQ(3, value.GetList()[1].GetInt()); + + EXPECT_EQ(1u, value.EraseListValue(Value(1))); + EXPECT_EQ(1u, value.GetList().size()); + EXPECT_EQ(3, value.GetList()[0].GetInt()); + + EXPECT_EQ(1u, value.EraseListValue(Value(3))); + EXPECT_TRUE(value.GetList().empty()); + + EXPECT_EQ(0u, value.EraseListValue(Value(3))); +} + +TEST(ValuesTest, EraseListValueIf) { + ListValue value; + value.Append(1); + value.Append(2); + value.Append(2); + value.Append(3); + + EXPECT_EQ(3u, value.EraseListValueIf( + [](const auto& val) { return val >= Value(2); })); + EXPECT_EQ(1u, value.GetList().size()); + EXPECT_EQ(1, value.GetList()[0].GetInt()); + + EXPECT_EQ(1u, value.EraseListValueIf([](const auto& val) { return true; })); + EXPECT_TRUE(value.GetList().empty()); + + EXPECT_EQ(0u, value.EraseListValueIf([](const auto& val) { return true; })); +} + TEST(ValuesTest, FindKey) { Value::DictStorage storage; storage.emplace("foo", std::make_unique<Value>("bar")); @@ -1183,7 +1310,7 @@ TEST(ValuesTest, Basic) { DictionaryValue new_bookmark; new_bookmark.SetKey("name", Value("Froogle")); new_bookmark.SetKey("url", Value("http://froogle.com")); - toolbar_bookmarks->GetList().push_back(std::move(new_bookmark)); + toolbar_bookmarks->Append(std::move(new_bookmark)); Value* bookmark_list = settings.FindPath("global.toolbar.bookmarks"); ASSERT_TRUE(bookmark_list); diff --git a/chromium/base/version.cc b/chromium/base/version.cc index 3ba39d40caf..2ee8793c03a 100644 --- a/chromium/base/version.cc +++ b/chromium/base/version.cc @@ -151,7 +151,7 @@ int Version::CompareTo(const Version& other) const { return CompareVersionComponents(components_, other.components_); } -const std::string Version::GetString() const { +std::string Version::GetString() const { DCHECK(IsValid()); std::string version_str; size_t count = components_.size(); diff --git a/chromium/base/version.h b/chromium/base/version.h index 272cbe82c79..9199449de69 100644 --- a/chromium/base/version.h +++ b/chromium/base/version.h @@ -56,7 +56,7 @@ class BASE_EXPORT Version { int CompareToWildcardString(StringPiece wildcard_string) const; // Return the string representation of this version. - const std::string GetString() const; + std::string GetString() const; const std::vector<uint32_t>& components() const { return components_; } diff --git a/chromium/base/win/com_init_check_hook.h b/chromium/base/win/com_init_check_hook.h index 304e62a42b5..f143adee869 100644 --- a/chromium/base/win/com_init_check_hook.h +++ b/chromium/base/win/com_init_check_hook.h @@ -21,9 +21,8 @@ namespace win { // binaries contain a convenient 2 byte hotpatch noop. This doesn't exist in // 64-bit binaries. -#if DCHECK_IS_ON() && defined(ARCH_CPU_X86_FAMILY) && \ - defined(ARCH_CPU_32_BITS) && !defined(GOOGLE_CHROME_BUILD) && \ - !defined(OFFICIAL_BUILD) && \ +#if DCHECK_IS_ON() && defined(ARCH_CPU_X86_FAMILY) && \ + defined(ARCH_CPU_32_BITS) && !defined(OFFICIAL_BUILD) && \ !defined(COM_INIT_CHECK_HOOK_DISABLED) // See crbug/737090 for details. #define COM_INIT_CHECK_HOOK_ENABLED #endif diff --git a/chromium/base/win/embedded_i18n/create_string_rc.py b/chromium/base/win/embedded_i18n/create_string_rc.py index e046b6fe39c..934131c2af2 100755 --- a/chromium/base/win/embedded_i18n/create_string_rc.py +++ b/chromium/base/win/embedded_i18n/create_string_rc.py @@ -575,7 +575,7 @@ def main(): parser.error('A brand was specified (' + brand + ') but no mode ' 'specific strings were given.') valid_brands = [b for b in - mode_specific_strings.itervalues().next().iterkeys()] + next(iter(mode_specific_strings.values())).keys()] if not brand in valid_brands: parser.error('A brand was specified (' + brand + ') but it is not ' 'a valid brand [' + ', '.join(valid_brands) + '].') diff --git a/chromium/base/win/embedded_i18n/language_selector.cc b/chromium/base/win/embedded_i18n/language_selector.cc index 1a3a8a79602..762b3713e04 100644 --- a/chromium/base/win/embedded_i18n/language_selector.cc +++ b/chromium/base/win/embedded_i18n/language_selector.cc @@ -44,13 +44,13 @@ struct AvailableLanguageAliases { #if DCHECK_IS_ON() // Returns true if the items in the given range are sorted and lower cased. -bool IsArraySortedAndLowerCased( - base::span<const LangToOffset> languages_to_offset) { +bool IsArraySortedAndLowerCased(span<const LangToOffset> languages_to_offset) { return std::is_sorted(languages_to_offset.begin(), languages_to_offset.end()) && std::all_of(languages_to_offset.begin(), languages_to_offset.end(), [](const auto& lang) { - return base::ToLowerASCII(lang.first) == lang.first; + auto language = AsStringPiece16(lang.first); + return ToLowerASCII(language) == language; }); } #endif // DCHECK_IS_ON() @@ -58,29 +58,29 @@ bool IsArraySortedAndLowerCased( // Determines the availability of all languages that may be used as aliases in // GetAliasedLanguageOffset or GetCompatibleNeutralLanguageOffset AvailableLanguageAliases DetermineAvailableAliases( - base::span<const LangToOffset> languages_to_offset) { + span<const LangToOffset> languages_to_offset) { AvailableLanguageAliases available_aliases = {}; for (const LangToOffset& lang_to_offset : languages_to_offset) { - if (lang_to_offset.first == STRING16_LITERAL("en-gb")) + if (lang_to_offset.first == L"en-gb") available_aliases.en_gb_language_offset = &lang_to_offset; - else if (lang_to_offset.first == STRING16_LITERAL("en-us")) + else if (lang_to_offset.first == L"en-us") available_aliases.en_us_language_offset = &lang_to_offset; - else if (lang_to_offset.first == STRING16_LITERAL("es")) + else if (lang_to_offset.first == L"es") available_aliases.es_language_offset = &lang_to_offset; - else if (lang_to_offset.first == STRING16_LITERAL("es-419")) + else if (lang_to_offset.first == L"es-419") available_aliases.es_419_language_offset = &lang_to_offset; - else if (lang_to_offset.first == STRING16_LITERAL("fil")) + else if (lang_to_offset.first == L"fil") available_aliases.fil_language_offset = &lang_to_offset; - else if (lang_to_offset.first == STRING16_LITERAL("iw")) + else if (lang_to_offset.first == L"iw") available_aliases.iw_language_offset = &lang_to_offset; - else if (lang_to_offset.first == STRING16_LITERAL("no")) + else if (lang_to_offset.first == L"no") available_aliases.no_language_offset = &lang_to_offset; - else if (lang_to_offset.first == STRING16_LITERAL("pt-br")) + else if (lang_to_offset.first == L"pt-br") available_aliases.pt_br_language_offset = &lang_to_offset; - else if (lang_to_offset.first == STRING16_LITERAL("zh-cn")) + else if (lang_to_offset.first == L"zh-cn") available_aliases.zh_cn_language_offset = &lang_to_offset; - else if (lang_to_offset.first == STRING16_LITERAL("zh-tw")) + else if (lang_to_offset.first == L"zh-tw") available_aliases.zh_tw_language_offset = &lang_to_offset; } @@ -93,8 +93,8 @@ AvailableLanguageAliases DetermineAvailableAliases( // that matches the |language| exactly. |offset| will store the offset of the // language that matches if any. |languages_to_offset| must be sorted by // language and all languages must lower case. -bool GetExactLanguageOffset(base::span<const LangToOffset> languages_to_offset, - const base::string16& language, +bool GetExactLanguageOffset(span<const LangToOffset> languages_to_offset, + const std::wstring& language, const LangToOffset** matched_language_to_offset) { DCHECK(matched_language_to_offset); @@ -102,7 +102,7 @@ bool GetExactLanguageOffset(base::span<const LangToOffset> languages_to_offset, // to a given language |name|. auto search_result = std::lower_bound( languages_to_offset.begin(), languages_to_offset.end(), language, - [](const LangToOffset& left, const base::string16& to_find) { + [](const LangToOffset& left, const std::wstring& to_find) { return left.first < to_find; }); if (languages_to_offset.end() != search_result && @@ -115,65 +115,58 @@ bool GetExactLanguageOffset(base::span<const LangToOffset> languages_to_offset, // Returns true if the current language can be aliased to another language. bool GetAliasedLanguageOffset(const AvailableLanguageAliases& available_aliases, - const base::string16& language, + const std::wstring& language, const LangToOffset** matched_language_to_offset) { DCHECK(matched_language_to_offset); // Alias some English variants to British English (all others wildcard to // US). if (available_aliases.en_gb_language_offset && - (language == STRING16_LITERAL("en-au") || - language == STRING16_LITERAL("en-ca") || - language == STRING16_LITERAL("en-nz") || - language == STRING16_LITERAL("en-za"))) { + (language == L"en-au" || language == L"en-ca" || language == L"en-nz" || + language == L"en-za")) { *matched_language_to_offset = available_aliases.en_gb_language_offset; return true; } // Alias es-es to es (all others wildcard to es-419). - if (available_aliases.es_language_offset && - language == STRING16_LITERAL("es-es")) { + if (available_aliases.es_language_offset && language == L"es-es") { *matched_language_to_offset = available_aliases.es_language_offset; return true; } // Google web properties use iw for he. Handle both just to be safe. - if (available_aliases.iw_language_offset && - language == STRING16_LITERAL("he")) { + if (available_aliases.iw_language_offset && language == L"he") { *matched_language_to_offset = available_aliases.iw_language_offset; return true; } // Google web properties use no for nb. Handle both just to be safe. - if (available_aliases.no_language_offset && - language == STRING16_LITERAL("nb")) { + if (available_aliases.no_language_offset && language == L"nb") { *matched_language_to_offset = available_aliases.no_language_offset; return true; } // Some Google web properties use tl for fil. Handle both just to be safe. // They're not completely identical, but alias it here. - if (available_aliases.fil_language_offset && - language == STRING16_LITERAL("tl")) { + if (available_aliases.fil_language_offset && language == L"tl") { *matched_language_to_offset = available_aliases.fil_language_offset; return true; } if (available_aliases.zh_cn_language_offset && // Pre-Vista alias for Chinese w/ script subtag. - (language == STRING16_LITERAL("zh-chs") || + (language == L"zh-chs" || // Vista+ alias for Chinese w/ script subtag. - language == STRING16_LITERAL("zh-hans") || + language == L"zh-hans" || // Although the wildcard entry for zh would result in this, alias zh-sg // so that it will win if it precedes another valid tag in a list of // candidates. - language == STRING16_LITERAL("zh-sg"))) { + language == L"zh-sg")) { *matched_language_to_offset = available_aliases.zh_cn_language_offset; return true; } if (available_aliases.zh_tw_language_offset && // Pre-Vista alias for Chinese w/ script subtag. - (language == STRING16_LITERAL("zh-cht") || + (language == L"zh-cht" || // Vista+ alias for Chinese w/ script subtag. - language == STRING16_LITERAL("zh-hant") || + language == L"zh-hant" || // Alias Hong Kong and Macau to Taiwan. - language == STRING16_LITERAL("zh-hk") || - language == STRING16_LITERAL("zh-mo"))) { + language == L"zh-hk" || language == L"zh-mo")) { *matched_language_to_offset = available_aliases.zh_tw_language_offset; return true; } @@ -185,30 +178,26 @@ bool GetAliasedLanguageOffset(const AvailableLanguageAliases& available_aliases, // language. bool GetCompatibleNeutralLanguageOffset( const AvailableLanguageAliases& available_aliases, - const base::string16& neutral_language, + const std::wstring& neutral_language, const LangToOffset** matched_language_to_offset) { DCHECK(matched_language_to_offset); - if (available_aliases.en_us_language_offset && - neutral_language == STRING16_LITERAL("en")) { + if (available_aliases.en_us_language_offset && neutral_language == L"en") { // Use the U.S. region for anything English. *matched_language_to_offset = available_aliases.en_us_language_offset; return true; } - if (available_aliases.es_419_language_offset && - neutral_language == STRING16_LITERAL("es")) { + if (available_aliases.es_419_language_offset && neutral_language == L"es") { // Use the Latin American region for anything Spanish. *matched_language_to_offset = available_aliases.es_419_language_offset; return true; } - if (available_aliases.pt_br_language_offset && - neutral_language == STRING16_LITERAL("pt")) { + if (available_aliases.pt_br_language_offset && neutral_language == L"pt") { // Use the Brazil region for anything Portugese. *matched_language_to_offset = available_aliases.pt_br_language_offset; return true; } - if (available_aliases.zh_cn_language_offset && - neutral_language == STRING16_LITERAL("zh")) { + if (available_aliases.zh_cn_language_offset && neutral_language == L"zh") { // Use the P.R.C. region for anything Chinese. *matched_language_to_offset = available_aliases.zh_cn_language_offset; return true; @@ -223,11 +212,11 @@ bool GetCompatibleNeutralLanguageOffset( // candidate and |matched_offset| is assigned the language offset of the // selected translation. // static -bool SelectIf(const std::vector<base::string16>& candidates, - base::span<const LangToOffset> languages_to_offset, +bool SelectIf(const std::vector<std::wstring>& candidates, + span<const LangToOffset> languages_to_offset, const AvailableLanguageAliases& available_aliases, const LangToOffset** matched_language_to_offset, - base::string16* matched_name) { + std::wstring* matched_name) { DCHECK(matched_language_to_offset); DCHECK(matched_name); @@ -236,8 +225,9 @@ bool SelectIf(const std::vector<base::string16>& candidates, // An earlier candidate entry matching on an exact match or alias match takes // precedence over a later candidate entry matching on an exact match. - for (const base::string16& scan : candidates) { - base::string16 lower_case_candidate = base::ToLowerASCII(scan); + for (const std::wstring& scan : candidates) { + std::wstring lower_case_candidate = + AsWString(ToLowerASCII(AsStringPiece16(scan))); if (GetExactLanguageOffset(languages_to_offset, lower_case_candidate, matched_language_to_offset) || GetAliasedLanguageOffset(available_aliases, lower_case_candidate, @@ -249,12 +239,13 @@ bool SelectIf(const std::vector<base::string16>& candidates, // If no candidate matches exactly or by alias, try to match by locale neutral // language. - for (const base::string16& scan : candidates) { - base::string16 lower_case_candidate = base::ToLowerASCII(scan); + for (const std::wstring& scan : candidates) { + std::wstring lower_case_candidate = + AsWString(ToLowerASCII(AsStringPiece16(scan))); // Extract the locale neutral language from the language to search and try // to find an exact match for that language in the provided table. - base::string16 neutral_language = + std::wstring neutral_language = lower_case_candidate.substr(0, lower_case_candidate.find(L'-')); if (GetCompatibleNeutralLanguageOffset(available_aliases, neutral_language, @@ -268,11 +259,11 @@ bool SelectIf(const std::vector<base::string16>& candidates, } void SelectLanguageMatchingCandidate( - const std::vector<base::string16>& candidates, - base::span<const LangToOffset> languages_to_offset, + const std::vector<std::wstring>& candidates, + span<const LangToOffset> languages_to_offset, int* selected_offset, - base::string16* matched_candidate, - base::string16* selected_language) { + std::wstring* matched_candidate, + std::wstring* selected_language) { DCHECK(selected_offset); DCHECK(matched_candidate); DCHECK(selected_language); @@ -303,18 +294,18 @@ void SelectLanguageMatchingCandidate( &matched_language_to_offset, matched_candidate)) { matched_language_to_offset = available_aliases.en_us_language_offset; *matched_candidate = - base::string16(available_aliases.en_us_language_offset->first); + std::wstring(available_aliases.en_us_language_offset->first); } DCHECK(matched_language_to_offset); // Get the real language being used for the matched candidate. - *selected_language = base::string16(matched_language_to_offset->first); + *selected_language = std::wstring(matched_language_to_offset->first); *selected_offset = matched_language_to_offset->second; } -std::vector<base::string16> GetCandidatesFromSystem( - base::StringPiece16 preferred_language) { - std::vector<base::string16> candidates; +std::vector<std::wstring> GetCandidatesFromSystem( + WStringPiece preferred_language) { + std::vector<std::wstring> candidates; // Get the intitial candidate list for this particular implementation (if // applicable). @@ -323,21 +314,19 @@ std::vector<base::string16> GetCandidatesFromSystem( // Now try the UI languages. Use the thread preferred ones since that will // kindly return us a list of all kinds of fallbacks. - base::win::i18n::GetThreadPreferredUILanguageList(&candidates); + win::i18n::GetThreadPreferredUILanguageList(&candidates); return candidates; } } // namespace -LanguageSelector::LanguageSelector( - base::StringPiece16 preferred_language, - base::span<const LangToOffset> languages_to_offset) +LanguageSelector::LanguageSelector(WStringPiece preferred_language, + span<const LangToOffset> languages_to_offset) : LanguageSelector(GetCandidatesFromSystem(preferred_language), languages_to_offset) {} -LanguageSelector::LanguageSelector( - const std::vector<base::string16>& candidates, - base::span<const LangToOffset> languages_to_offset) +LanguageSelector::LanguageSelector(const std::vector<std::wstring>& candidates, + span<const LangToOffset> languages_to_offset) : selected_offset_(languages_to_offset.size()) { SelectLanguageMatchingCandidate(candidates, languages_to_offset, &selected_offset_, &matched_candidate_, diff --git a/chromium/base/win/embedded_i18n/language_selector.h b/chromium/base/win/embedded_i18n/language_selector.h index 2772aee8f7a..84d1e9cd506 100644 --- a/chromium/base/win/embedded_i18n/language_selector.h +++ b/chromium/base/win/embedded_i18n/language_selector.h @@ -8,13 +8,13 @@ #ifndef BASE_WIN_EMBEDDED_I18N_LANGUAGE_SELECTOR_H_ #define BASE_WIN_EMBEDDED_I18N_LANGUAGE_SELECTOR_H_ +#include <string> #include <utility> #include <vector> #include "base/base_export.h" #include "base/containers/span.h" #include "base/macros.h" -#include "base/strings/string16.h" #include "base/strings/string_piece.h" namespace base { @@ -26,7 +26,7 @@ namespace i18n { // override selection should a corresponding translation be available. class BASE_EXPORT LanguageSelector { public: - using LangToOffset = std::pair<base::StringPiece16, int>; + using LangToOffset = std::pair<WStringPiece, int>; // Constructor to be used for users of this class that will provide the actual // language offsets that will be used. @@ -36,8 +36,8 @@ class BASE_EXPORT LanguageSelector { // |languages_to_offset_begin| and |languages_to_offset_end| point to a sorted // array of language identifiers (and their offsets) for which translations // are available. - LanguageSelector(base::StringPiece16 preferred_language, - base::span<const LangToOffset> languages_to_offset); + LanguageSelector(WStringPiece preferred_language, + span<const LangToOffset> languages_to_offset); // Constructor for testing purposes. // |candidates| is a list of all candiate languages that can be used to @@ -45,8 +45,8 @@ class BASE_EXPORT LanguageSelector { // |languages_to_offset_begin| and |languages_to_offset_end| point to a sorted // array of language identifiers (and their offsets) for which translations // are available. - LanguageSelector(const std::vector<base::string16>& candidates, - base::span<const LangToOffset> languages_to_offset); + LanguageSelector(const std::vector<std::wstring>& candidates, + span<const LangToOffset> languages_to_offset); ~LanguageSelector(); @@ -54,16 +54,16 @@ class BASE_EXPORT LanguageSelector { int offset() const { return selected_offset_; } // The full name of the candidate language for which a match was found. - const base::string16& matched_candidate() const { return matched_candidate_; } + const std::wstring& matched_candidate() const { return matched_candidate_; } // The name of the selected translation. - const base::string16& selected_translation() const { + const std::wstring& selected_translation() const { return selected_language_; } private: - base::string16 matched_candidate_; - base::string16 selected_language_; + std::wstring matched_candidate_; + std::wstring selected_language_; int selected_offset_; DISALLOW_COPY_AND_ASSIGN(LanguageSelector); diff --git a/chromium/base/win/embedded_i18n/language_selector_unittest.cc b/chromium/base/win/embedded_i18n/language_selector_unittest.cc index 64efc1da713..6903d0cb38c 100644 --- a/chromium/base/win/embedded_i18n/language_selector_unittest.cc +++ b/chromium/base/win/embedded_i18n/language_selector_unittest.cc @@ -17,49 +17,26 @@ namespace win { namespace i18n { namespace { -constexpr const base::char16* kExactMatchCandidates[] = { - STRING16_LITERAL("am"), STRING16_LITERAL("ar"), - STRING16_LITERAL("bg"), STRING16_LITERAL("bn"), - STRING16_LITERAL("ca"), STRING16_LITERAL("cs"), - STRING16_LITERAL("da"), STRING16_LITERAL("de"), - STRING16_LITERAL("el"), STRING16_LITERAL("en-gb"), - STRING16_LITERAL("en-us"), STRING16_LITERAL("es"), - STRING16_LITERAL("es-419"), STRING16_LITERAL("et"), - STRING16_LITERAL("fa"), STRING16_LITERAL("fi"), - STRING16_LITERAL("fil"), STRING16_LITERAL("fr"), - STRING16_LITERAL("gu"), STRING16_LITERAL("hi"), - STRING16_LITERAL("hr"), STRING16_LITERAL("hu"), - STRING16_LITERAL("id"), STRING16_LITERAL("it"), - STRING16_LITERAL("iw"), STRING16_LITERAL("ja"), - STRING16_LITERAL("kn"), STRING16_LITERAL("ko"), - STRING16_LITERAL("lt"), STRING16_LITERAL("lv"), - STRING16_LITERAL("ml"), STRING16_LITERAL("mr"), - STRING16_LITERAL("nl"), STRING16_LITERAL("no"), - STRING16_LITERAL("pl"), STRING16_LITERAL("pt-br"), - STRING16_LITERAL("pt-pt"), STRING16_LITERAL("ro"), - STRING16_LITERAL("ru"), STRING16_LITERAL("sk"), - STRING16_LITERAL("sl"), STRING16_LITERAL("sr"), - STRING16_LITERAL("sv"), STRING16_LITERAL("sw"), - STRING16_LITERAL("ta"), STRING16_LITERAL("te"), - STRING16_LITERAL("th"), STRING16_LITERAL("tr"), - STRING16_LITERAL("uk"), STRING16_LITERAL("vi"), - STRING16_LITERAL("zh-cn"), STRING16_LITERAL("zh-tw")}; - -constexpr const base::char16* kAliasMatchCandidates[] = { - STRING16_LITERAL("he"), STRING16_LITERAL("nb"), - STRING16_LITERAL("tl"), STRING16_LITERAL("zh-chs"), - STRING16_LITERAL("zh-cht"), STRING16_LITERAL("zh-hans"), - STRING16_LITERAL("zh-hant"), STRING16_LITERAL("zh-hk"), - STRING16_LITERAL("zh-mo")}; - -constexpr const base::char16* kWildcardMatchCandidates[] = { - STRING16_LITERAL("en-AU"), STRING16_LITERAL("es-CO"), - STRING16_LITERAL("pt-AB"), STRING16_LITERAL("zh-SG")}; +constexpr const wchar_t* kExactMatchCandidates[] = { + L"am", L"ar", L"bg", L"bn", L"ca", L"cs", L"da", L"de", + L"el", L"en-gb", L"en-us", L"es", L"es-419", L"et", L"fa", L"fi", + L"fil", L"fr", L"gu", L"hi", L"hr", L"hu", L"id", L"it", + L"iw", L"ja", L"kn", L"ko", L"lt", L"lv", L"ml", L"mr", + L"nl", L"no", L"pl", L"pt-br", L"pt-pt", L"ro", L"ru", L"sk", + L"sl", L"sr", L"sv", L"sw", L"ta", L"te", L"th", L"tr", + L"uk", L"vi", L"zh-cn", L"zh-tw"}; + +constexpr const wchar_t* kAliasMatchCandidates[] = { + L"he", L"nb", L"tl", L"zh-chs", L"zh-cht", + L"zh-hans", L"zh-hant", L"zh-hk", L"zh-mo"}; + +constexpr const wchar_t* kWildcardMatchCandidates[] = {L"en-AU", L"es-CO", + L"pt-AB", L"zh-SG"}; std::vector<LanguageSelector::LangToOffset> MakeLanguageOffsetPairs() { std::vector<LanguageSelector::LangToOffset> language_offset_pairs; int i = 0; - for (const base::char16* lang : kExactMatchCandidates) { + for (const wchar_t* lang : kExactMatchCandidates) { language_offset_pairs.push_back({lang, i++}); } @@ -68,13 +45,12 @@ std::vector<LanguageSelector::LangToOffset> MakeLanguageOffsetPairs() { class TestLanguageSelector : public LanguageSelector { public: - TestLanguageSelector() - : TestLanguageSelector(std::vector<base::string16>()) {} - explicit TestLanguageSelector(const std::vector<base::string16>& candidates) + TestLanguageSelector() : TestLanguageSelector(std::vector<std::wstring>()) {} + explicit TestLanguageSelector(const std::vector<std::wstring>& candidates) : TestLanguageSelector(candidates, MakeLanguageOffsetPairs()) {} TestLanguageSelector( - const std::vector<base::string16>& candidates, - base::span<const LanguageSelector::LangToOffset> languages_to_offset) + const std::vector<std::wstring>& candidates, + span<const LanguageSelector::LangToOffset> languages_to_offset) : LanguageSelector(candidates, languages_to_offset) {} }; @@ -89,32 +65,28 @@ TEST(LanguageSelectorTest, DefaultSelection) { // Test some hypothetical candidate sets. TEST(LanguageSelectorTest, AssortedSelections) { { - std::vector<base::string16> candidates = {STRING16_LITERAL("fr-BE"), - STRING16_LITERAL("fr"), - STRING16_LITERAL("en")}; + std::vector<std::wstring> candidates = {L"fr-BE", L"fr", L"en"}; TestLanguageSelector instance(candidates); // Expect the exact match to win. - EXPECT_EQ(STRING16_LITERAL("fr"), instance.matched_candidate()); + EXPECT_EQ(L"fr", instance.matched_candidate()); } { - std::vector<base::string16> candidates = {STRING16_LITERAL("xx-YY"), - STRING16_LITERAL("cc-Ssss-RR")}; + std::vector<std::wstring> candidates = {L"xx-YY", L"cc-Ssss-RR"}; TestLanguageSelector instance(candidates); // Expect the fallback to win. - EXPECT_EQ(STRING16_LITERAL("en-us"), instance.matched_candidate()); + EXPECT_EQ(L"en-us", instance.matched_candidate()); } { - std::vector<base::string16> candidates = {STRING16_LITERAL("zh-SG"), - STRING16_LITERAL("en-GB")}; + std::vector<std::wstring> candidates = {L"zh-SG", L"en-GB"}; TestLanguageSelector instance(candidates); // Expect the alias match to win. - EXPECT_EQ(STRING16_LITERAL("zh-SG"), instance.matched_candidate()); + EXPECT_EQ(L"zh-SG", instance.matched_candidate()); } } // A fixture for testing sets of single-candidate selections. class LanguageSelectorMatchCandidateTest - : public ::testing::TestWithParam<const base::char16*> {}; + : public ::testing::TestWithParam<const wchar_t*> {}; TEST_P(LanguageSelectorMatchCandidateTest, TestMatchCandidate) { TestLanguageSelector instance({GetParam()}); @@ -141,7 +113,7 @@ INSTANTIATE_TEST_SUITE_P(TestWildcardMatches, // candidate that should be aliased to the expectation. class LanguageSelectorAliasTest : public ::testing::TestWithParam< - std::tuple<const base::char16*, const base::char16*>> {}; + std::tuple<const wchar_t*, const wchar_t*>> {}; // Test that the candidate language maps to the aliased translation. TEST_P(LanguageSelectorAliasTest, AliasesMatch) { @@ -149,49 +121,42 @@ TEST_P(LanguageSelectorAliasTest, AliasesMatch) { EXPECT_EQ(std::get<0>(GetParam()), instance.selected_translation()); } -INSTANTIATE_TEST_SUITE_P( - EnGbAliases, - LanguageSelectorAliasTest, - ::testing::Combine(::testing::Values(STRING16_LITERAL("en-gb")), - ::testing::Values(STRING16_LITERAL("en-au"), - STRING16_LITERAL("en-ca"), - STRING16_LITERAL("en-nz"), - STRING16_LITERAL("en-za")))); - -INSTANTIATE_TEST_SUITE_P( - IwAliases, - LanguageSelectorAliasTest, - ::testing::Combine(::testing::Values(STRING16_LITERAL("iw")), - ::testing::Values(STRING16_LITERAL("he")))); - -INSTANTIATE_TEST_SUITE_P( - NoAliases, - LanguageSelectorAliasTest, - ::testing::Combine(::testing::Values(STRING16_LITERAL("no")), - ::testing::Values(STRING16_LITERAL("nb")))); - -INSTANTIATE_TEST_SUITE_P( - FilAliases, - LanguageSelectorAliasTest, - ::testing::Combine(::testing::Values(STRING16_LITERAL("fil")), - ::testing::Values(STRING16_LITERAL("tl")))); +INSTANTIATE_TEST_SUITE_P(EnGbAliases, + LanguageSelectorAliasTest, + ::testing::Combine(::testing::Values(L"en-gb"), + ::testing::Values(L"en-au", + L"en-ca", + L"en-nz", + L"en-za"))); + +INSTANTIATE_TEST_SUITE_P(IwAliases, + LanguageSelectorAliasTest, + ::testing::Combine(::testing::Values(L"iw"), + ::testing::Values(L"he"))); + +INSTANTIATE_TEST_SUITE_P(NoAliases, + LanguageSelectorAliasTest, + ::testing::Combine(::testing::Values(L"no"), + ::testing::Values(L"nb"))); + +INSTANTIATE_TEST_SUITE_P(FilAliases, + LanguageSelectorAliasTest, + ::testing::Combine(::testing::Values(L"fil"), + ::testing::Values(L"tl"))); INSTANTIATE_TEST_SUITE_P( ZhCnAliases, LanguageSelectorAliasTest, - ::testing::Combine(::testing::Values(STRING16_LITERAL("zh-cn")), - ::testing::Values(STRING16_LITERAL("zh-chs"), - STRING16_LITERAL("zh-hans"), - STRING16_LITERAL("zh-sg")))); + ::testing::Combine(::testing::Values(L"zh-cn"), + ::testing::Values(L"zh-chs", L"zh-hans", L"zh-sg"))); -INSTANTIATE_TEST_SUITE_P( - ZhTwAliases, - LanguageSelectorAliasTest, - ::testing::Combine(::testing::Values(STRING16_LITERAL("zh-tw")), - ::testing::Values(STRING16_LITERAL("zh-cht"), - STRING16_LITERAL("zh-hant"), - STRING16_LITERAL("zh-hk"), - STRING16_LITERAL("zh-mo")))); +INSTANTIATE_TEST_SUITE_P(ZhTwAliases, + LanguageSelectorAliasTest, + ::testing::Combine(::testing::Values(L"zh-tw"), + ::testing::Values(L"zh-cht", + L"zh-hant", + L"zh-hk", + L"zh-mo"))); // Test that we can get a match of the default language. TEST(LanguageSelectorTest, DefaultLanguageName) { @@ -202,34 +167,32 @@ TEST(LanguageSelectorTest, DefaultLanguageName) { // All languages given to the selector must be lower cased (since generally // the language names are generated by a python script). TEST(LanguageSelectorTest, InvalidLanguageCasing) { - constexpr LanguageSelector::LangToOffset kLangToOffset[] = { - {STRING16_LITERAL("en-US"), 0}}; + constexpr LanguageSelector::LangToOffset kLangToOffset[] = {{L"en-US", 0}}; EXPECT_DCHECK_DEATH(LanguageSelector instance( - std::vector<base::string16>({STRING16_LITERAL("en-us")}), kLangToOffset)); + std::vector<std::wstring>({L"en-us"}), kLangToOffset)); } // Language name and offset pairs must be ordered when generated by the // python script. TEST(LanguageSelectorTest, InvalidLanguageNameOrder) { - constexpr LanguageSelector::LangToOffset kLangToOffset[] = { - {STRING16_LITERAL("en-us"), 0}, {STRING16_LITERAL("en-gb"), 1}}; + constexpr LanguageSelector::LangToOffset kLangToOffset[] = {{L"en-us", 0}, + {L"en-gb", 1}}; EXPECT_DCHECK_DEATH(LanguageSelector instance( - std::vector<base::string16>({STRING16_LITERAL("en-us")}), kLangToOffset)); + std::vector<std::wstring>({L"en-us"}), kLangToOffset)); } // There needs to be a fallback language available in the generated // languages if ever the selector is given a language that does not exist. TEST(LanguageSelectorTest, NoFallbackLanguageAvailable) { - constexpr LanguageSelector::LangToOffset kLangToOffset[] = { - {STRING16_LITERAL("en-gb"), 0}}; + constexpr LanguageSelector::LangToOffset kLangToOffset[] = {{L"en-gb", 0}}; EXPECT_DCHECK_DEATH(LanguageSelector instance( - std::vector<base::string16>({STRING16_LITERAL("aa-bb")}), kLangToOffset)); + std::vector<std::wstring>({L"aa-bb"}), kLangToOffset)); } // No languages available. TEST(LanguageSelectorTest, NoLanguagesAvailable) { - EXPECT_DCHECK_DEATH(LanguageSelector instance( - std::vector<base::string16>({STRING16_LITERAL("en-us")}), {})); + EXPECT_DCHECK_DEATH( + LanguageSelector instance(std::vector<std::wstring>({L"en-us"}), {})); } } // namespace i18n diff --git a/chromium/base/win/enum_variant.cc b/chromium/base/win/enum_variant.cc index 38861bfe70f..9ae35e2fdd3 100644 --- a/chromium/base/win/enum_variant.cc +++ b/chromium/base/win/enum_variant.cc @@ -4,6 +4,8 @@ #include "base/win/enum_variant.h" +#include <wrl/client.h> + #include <algorithm> #include "base/logging.h" @@ -26,27 +28,9 @@ VARIANT* EnumVariant::ItemAt(ULONG index) { return items_[index].AsInput(); } -ULONG STDMETHODCALLTYPE EnumVariant::AddRef() { - return IUnknownImpl::AddRef(); -} - -ULONG STDMETHODCALLTYPE EnumVariant::Release() { - return IUnknownImpl::Release(); -} - -STDMETHODIMP EnumVariant::QueryInterface(REFIID riid, void** ppv) { - if (riid == IID_IEnumVARIANT) { - *ppv = static_cast<IEnumVARIANT*>(this); - AddRef(); - return S_OK; - } - - return IUnknownImpl::QueryInterface(riid, ppv); -} - -STDMETHODIMP EnumVariant::Next(ULONG requested_count, - VARIANT* out_elements, - ULONG* out_elements_received) { +HRESULT EnumVariant::Next(ULONG requested_count, + VARIANT* out_elements, + ULONG* out_elements_received) { if (!out_elements) return E_INVALIDARG; @@ -65,7 +49,7 @@ STDMETHODIMP EnumVariant::Next(ULONG requested_count, return (count == requested_count ? S_OK : S_FALSE); } -STDMETHODIMP EnumVariant::Skip(ULONG skip_count) { +HRESULT EnumVariant::Skip(ULONG skip_count) { ULONG count = skip_count; if (current_index_ + count > ULONG{items_.size()}) count = ULONG{items_.size()} - current_index_; @@ -74,24 +58,23 @@ STDMETHODIMP EnumVariant::Skip(ULONG skip_count) { return (count == skip_count ? S_OK : S_FALSE); } -STDMETHODIMP EnumVariant::Reset() { +HRESULT EnumVariant::Reset() { current_index_ = 0; return S_OK; } -STDMETHODIMP EnumVariant::Clone(IEnumVARIANT** out_cloned_object) { +HRESULT EnumVariant::Clone(IEnumVARIANT** out_cloned_object) { if (!out_cloned_object) return E_INVALIDARG; size_t count = items_.size(); - EnumVariant* other = new EnumVariant(ULONG{count}); + Microsoft::WRL::ComPtr<EnumVariant> other = + Microsoft::WRL::Make<EnumVariant>(ULONG{count}); for (size_t i = 0; i < count; ++i) other->items_[i] = static_cast<const VARIANT&>(items_[i]); other->Skip(current_index_); - other->AddRef(); - *out_cloned_object = other; - return S_OK; + return other.CopyTo(IID_PPV_ARGS(out_cloned_object)); } } // namespace win diff --git a/chromium/base/win/enum_variant.h b/chromium/base/win/enum_variant.h index 47ffd070de1..1adf4aac224 100644 --- a/chromium/base/win/enum_variant.h +++ b/chromium/base/win/enum_variant.h @@ -5,12 +5,11 @@ #ifndef BASE_WIN_ENUM_VARIANT_H_ #define BASE_WIN_ENUM_VARIANT_H_ -#include <unknwn.h> +#include <wrl/implements.h> #include <memory> #include <vector> -#include "base/win/iunknown_impl.h" #include "base/win/scoped_variant.h" namespace base { @@ -18,29 +17,25 @@ namespace win { // A simple implementation of IEnumVARIANT. class BASE_EXPORT EnumVariant - : public IEnumVARIANT, - public IUnknownImpl { + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, + IEnumVARIANT> { public: // The constructor allocates a vector of empty ScopedVariants of size |count|. // Use ItemAt to set the value of each item in the array. explicit EnumVariant(ULONG count); + // IEnumVARIANT: + IFACEMETHODIMP Next(ULONG requested_count, + VARIANT* out_elements, + ULONG* out_elements_received) override; + IFACEMETHODIMP Skip(ULONG skip_count) override; + IFACEMETHODIMP Reset() override; + IFACEMETHODIMP Clone(IEnumVARIANT** out_cloned_object) override; + // Returns a mutable pointer to the item at position |index|. VARIANT* ItemAt(ULONG index); - // IUnknown. - ULONG STDMETHODCALLTYPE AddRef() override; - ULONG STDMETHODCALLTYPE Release() override; - STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override; - - // IEnumVARIANT. - STDMETHODIMP Next(ULONG requested_count, - VARIANT* out_elements, - ULONG* out_elements_received) override; - STDMETHODIMP Skip(ULONG skip_count) override; - STDMETHODIMP Reset() override; - STDMETHODIMP Clone(IEnumVARIANT** out_cloned_object) override; - private: ~EnumVariant() override; diff --git a/chromium/base/win/enum_variant_unittest.cc b/chromium/base/win/enum_variant_unittest.cc index 77b2fdef73c..3165b5e2659 100644 --- a/chromium/base/win/enum_variant_unittest.cc +++ b/chromium/base/win/enum_variant_unittest.cc @@ -4,7 +4,11 @@ #include "base/win/enum_variant.h" +#include <wrl/client.h> +#include <wrl/implements.h> + #include "base/win/scoped_com_initializer.h" +#include "base/win/scoped_variant.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { @@ -13,52 +17,34 @@ namespace win { TEST(EnumVariantTest, EmptyEnumVariant) { ScopedCOMInitializer com_initializer; - EnumVariant* ev = new EnumVariant(0); - ev->AddRef(); - - IUnknown* iunknown; - EXPECT_TRUE(SUCCEEDED( - ev->QueryInterface(IID_IUnknown, reinterpret_cast<void**>(&iunknown)))); - iunknown->Release(); + Microsoft::WRL::ComPtr<EnumVariant> ev = Microsoft::WRL::Make<EnumVariant>(0); + Microsoft::WRL::ComPtr<IEnumVARIANT> ienumvariant; + ASSERT_TRUE(SUCCEEDED(ev->QueryInterface(IID_PPV_ARGS(&ienumvariant)))); - IEnumVARIANT* ienumvariant; - EXPECT_TRUE(SUCCEEDED( - ev->QueryInterface(IID_IEnumVARIANT, - reinterpret_cast<void**>(&ienumvariant)))); - EXPECT_EQ(ev, ienumvariant); - ienumvariant->Release(); - - VARIANT out_element; - ::VariantInit(&out_element); - ULONG out_received = 0; - EXPECT_EQ(S_FALSE, ev->Next(1, &out_element, &out_received)); - EXPECT_EQ(0u, out_received); - ::VariantClear(&out_element); + { + base::win::ScopedVariant out_element; + ULONG out_received = 0; + EXPECT_EQ(S_FALSE, ev->Next(1, out_element.Receive(), &out_received)); + EXPECT_EQ(0u, out_received); + } EXPECT_EQ(S_FALSE, ev->Skip(1)); EXPECT_EQ(S_OK, ev->Reset()); - IEnumVARIANT* ev2 = NULL; + Microsoft::WRL::ComPtr<IEnumVARIANT> ev2; EXPECT_EQ(S_OK, ev->Clone(&ev2)); - EXPECT_NE(static_cast<IEnumVARIANT*>(NULL), ev2); + EXPECT_NE(nullptr, ev2); EXPECT_NE(ev, ev2); EXPECT_EQ(S_FALSE, ev2->Skip(1)); EXPECT_EQ(S_OK, ev2->Reset()); - - ULONG ev2_finalrefcount = ev2->Release(); - EXPECT_EQ(0u, ev2_finalrefcount); - - ULONG ev_finalrefcount = ev->Release(); - EXPECT_EQ(0u, ev_finalrefcount); } TEST(EnumVariantTest, SimpleEnumVariant) { ScopedCOMInitializer com_initializer; - EnumVariant* ev = new EnumVariant(3); - ev->AddRef(); + Microsoft::WRL::ComPtr<EnumVariant> ev = Microsoft::WRL::Make<EnumVariant>(3); ev->ItemAt(0)->vt = VT_I4; ev->ItemAt(0)->lVal = 10; ev->ItemAt(1)->vt = VT_I4; @@ -66,31 +52,36 @@ TEST(EnumVariantTest, SimpleEnumVariant) { ev->ItemAt(2)->vt = VT_I4; ev->ItemAt(2)->lVal = 30; - // Get elements one at a time. - VARIANT out_element; - ::VariantInit(&out_element); - ULONG out_received = 0; - EXPECT_EQ(S_OK, ev->Next(1, &out_element, &out_received)); - EXPECT_EQ(1u, out_received); - EXPECT_EQ(VT_I4, out_element.vt); - EXPECT_EQ(10, out_element.lVal); - ::VariantClear(&out_element); + // Get elements one at a time from index 0 and 2. + base::win::ScopedVariant out_element_0; + ULONG out_received_0 = 0; + EXPECT_EQ(S_OK, ev->Next(1, out_element_0.Receive(), &out_received_0)); + EXPECT_EQ(1u, out_received_0); + EXPECT_EQ(VT_I4, out_element_0.ptr()->vt); + EXPECT_EQ(10, out_element_0.ptr()->lVal); + EXPECT_EQ(S_OK, ev->Skip(1)); - EXPECT_EQ(S_OK, ev->Next(1, &out_element, &out_received)); - EXPECT_EQ(1u, out_received); - EXPECT_EQ(VT_I4, out_element.vt); - EXPECT_EQ(30, out_element.lVal); - ::VariantClear(&out_element); - EXPECT_EQ(S_FALSE, ev->Next(1, &out_element, &out_received)); - ::VariantClear(&out_element); - - // Reset and get all elements at once. + + base::win::ScopedVariant out_element_2; + ULONG out_received_2 = 0; + EXPECT_EQ(S_OK, ev->Next(1, out_element_2.Receive(), &out_received_2)); + EXPECT_EQ(1u, out_received_2); + EXPECT_EQ(VT_I4, out_element_2.ptr()->vt); + EXPECT_EQ(30, out_element_2.ptr()->lVal); + + base::win::ScopedVariant placeholder_variant; + EXPECT_EQ(S_FALSE, ev->Next(1, placeholder_variant.Receive(), nullptr)); + + // Verify the reset works for the next step. + ASSERT_EQ(S_OK, ev->Reset()); + + // Get all elements at once. VARIANT out_elements[3]; + ULONG out_received_multiple; for (int i = 0; i < 3; ++i) ::VariantInit(&out_elements[i]); - EXPECT_EQ(S_OK, ev->Reset()); - EXPECT_EQ(S_OK, ev->Next(3, out_elements, &out_received)); - EXPECT_EQ(3u, out_received); + EXPECT_EQ(S_OK, ev->Next(3, out_elements, &out_received_multiple)); + EXPECT_EQ(3u, out_received_multiple); EXPECT_EQ(VT_I4, out_elements[0].vt); EXPECT_EQ(10, out_elements[0].lVal); EXPECT_EQ(VT_I4, out_elements[1].vt); @@ -99,16 +90,31 @@ TEST(EnumVariantTest, SimpleEnumVariant) { EXPECT_EQ(30, out_elements[2].lVal); for (int i = 0; i < 3; ++i) ::VariantClear(&out_elements[i]); - EXPECT_EQ(S_FALSE, ev->Next(1, &out_element, &out_received)); - ::VariantClear(&out_element); + + base::win::ScopedVariant placeholder_variant_multiple; + EXPECT_EQ(S_FALSE, + ev->Next(1, placeholder_variant_multiple.Receive(), nullptr)); +} + +TEST(EnumVariantTest, Clone) { + ScopedCOMInitializer com_initializer; + + Microsoft::WRL::ComPtr<EnumVariant> ev = Microsoft::WRL::Make<EnumVariant>(3); + ev->ItemAt(0)->vt = VT_I4; + ev->ItemAt(0)->lVal = 10; + ev->ItemAt(1)->vt = VT_I4; + ev->ItemAt(1)->lVal = 20; + ev->ItemAt(2)->vt = VT_I4; + ev->ItemAt(2)->lVal = 30; // Clone it. - IEnumVARIANT* ev2 = NULL; + Microsoft::WRL::ComPtr<IEnumVARIANT> ev2; EXPECT_EQ(S_OK, ev->Clone(&ev2)); EXPECT_TRUE(ev2 != NULL); - EXPECT_EQ(S_FALSE, ev->Next(1, &out_element, &out_received)); - ::VariantClear(&out_element); - EXPECT_EQ(S_OK, ev2->Reset()); + + VARIANT out_elements[3]; + for (int i = 0; i < 3; ++i) + ::VariantInit(&out_elements[i]); EXPECT_EQ(S_OK, ev2->Next(3, out_elements, nullptr)); EXPECT_EQ(VT_I4, out_elements[0].vt); EXPECT_EQ(10, out_elements[0].lVal); @@ -118,14 +124,6 @@ TEST(EnumVariantTest, SimpleEnumVariant) { EXPECT_EQ(30, out_elements[2].lVal); for (int i = 0; i < 3; ++i) ::VariantClear(&out_elements[i]); - EXPECT_EQ(S_FALSE, ev2->Next(1, &out_element, nullptr)); - ::VariantClear(&out_element); - - ULONG ev2_finalrefcount = ev2->Release(); - EXPECT_EQ(0u, ev2_finalrefcount); - - ULONG ev_finalrefcount = ev->Release(); - EXPECT_EQ(0u, ev_finalrefcount); } } // namespace win diff --git a/chromium/base/win/i18n.cc b/chromium/base/win/i18n.cc index adba36d13e8..ca80f93884d 100644 --- a/chromium/base/win/i18n.cc +++ b/chromium/base/win/i18n.cc @@ -16,7 +16,7 @@ typedef decltype(::GetSystemPreferredUILanguages)* GetPreferredUILanguages_Fn; bool GetPreferredUILanguageList(GetPreferredUILanguages_Fn function, ULONG flags, - std::vector<base::string16>* languages) { + std::vector<std::wstring>* languages) { DCHECK_EQ((flags & (MUI_LANGUAGE_ID | MUI_LANGUAGE_NAME)), 0U); const ULONG call_flags = flags | MUI_LANGUAGE_NAME; ULONG language_count = 0; @@ -27,18 +27,21 @@ bool GetPreferredUILanguageList(GetPreferredUILanguages_Fn function, return false; } - base::string16 buffer(buffer_length, '\0'); - if (!function(call_flags, &language_count, base::as_writable_wcstr(buffer), + std::wstring buffer(buffer_length, '\0'); + if (!function(call_flags, &language_count, base::data(buffer), &buffer_length) || !language_count) { DPCHECK(!language_count) << "Failed getting preferred UI languages."; return false; } + languages->clear(); // Split string on NUL characters. - *languages = - base::SplitString(buffer, base::string16(1, '\0'), base::KEEP_WHITESPACE, - base::SPLIT_WANT_NONEMPTY); + for (const auto& token : base::SplitStringPiece( + base::AsStringPiece16(buffer), base::string16(1, '\0'), + base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) { + languages->push_back(base::AsWString(token)); + } DCHECK_EQ(languages->size(), language_count); return true; } @@ -49,13 +52,13 @@ namespace base { namespace win { namespace i18n { -bool GetUserPreferredUILanguageList(std::vector<base::string16>* languages) { +bool GetUserPreferredUILanguageList(std::vector<std::wstring>* languages) { DCHECK(languages); return GetPreferredUILanguageList(::GetUserPreferredUILanguages, 0, languages); } -bool GetThreadPreferredUILanguageList(std::vector<base::string16>* languages) { +bool GetThreadPreferredUILanguageList(std::vector<std::wstring>* languages) { DCHECK(languages); return GetPreferredUILanguageList( ::GetThreadPreferredUILanguages, diff --git a/chromium/base/win/i18n.h b/chromium/base/win/i18n.h index d586646a286..9e74d3f3da3 100644 --- a/chromium/base/win/i18n.h +++ b/chromium/base/win/i18n.h @@ -5,10 +5,10 @@ #ifndef BASE_WIN_I18N_H_ #define BASE_WIN_I18N_H_ +#include <string> #include <vector> #include "base/base_export.h" -#include "base/strings/string16.h" namespace base { namespace win { @@ -18,13 +18,13 @@ namespace i18n { // available, falling-back on the user default UI language otherwise. Returns // true if at least one language is added. BASE_EXPORT bool GetUserPreferredUILanguageList( - std::vector<base::string16>* languages); + std::vector<std::wstring>* languages); // Adds to |languages| the list of thread, process, user, and system preferred // UI languages from MUI, if available, falling-back on the user default UI // language otherwise. Returns true if at least one language is added. BASE_EXPORT bool GetThreadPreferredUILanguageList( - std::vector<base::string16>* languages); + std::vector<std::wstring>* languages); } // namespace i18n } // namespace win diff --git a/chromium/base/win/i18n_unittest.cc b/chromium/base/win/i18n_unittest.cc index 3b0b668a735..f38db247a4a 100644 --- a/chromium/base/win/i18n_unittest.cc +++ b/chromium/base/win/i18n_unittest.cc @@ -19,24 +19,24 @@ namespace i18n { // Tests that at least one user preferred UI language can be obtained. TEST(I18NTest, GetUserPreferredUILanguageList) { - std::vector<base::string16> languages; + std::vector<std::wstring> languages; EXPECT_TRUE(GetUserPreferredUILanguageList(&languages)); EXPECT_FALSE(languages.empty()); for (const auto& language : languages) { EXPECT_FALSE(language.empty()); // Ensure there's no extra trailing 0 characters. - EXPECT_EQ(language.size(), wcslen(base::as_wcstr(language))); + EXPECT_EQ(language.size(), wcslen(language.c_str())); } } // Tests that at least one thread preferred UI language can be obtained. TEST(I18NTest, GetThreadPreferredUILanguageList) { - std::vector<base::string16> languages; + std::vector<std::wstring> languages; EXPECT_TRUE(GetThreadPreferredUILanguageList(&languages)); EXPECT_FALSE(languages.empty()); for (const auto& language : languages) { EXPECT_FALSE(language.empty()); - EXPECT_EQ(language.size(), wcslen(base::as_wcstr(language))); + EXPECT_EQ(language.size(), wcslen(language.c_str())); } } diff --git a/chromium/base/win/iunknown_impl.cc b/chromium/base/win/iunknown_impl.cc deleted file mode 100644 index 2a88439220d..00000000000 --- a/chromium/base/win/iunknown_impl.cc +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/win/iunknown_impl.h" - -namespace base { -namespace win { - -IUnknownImpl::IUnknownImpl() - : ref_count_(0) { -} - -IUnknownImpl::~IUnknownImpl() { -} - -ULONG STDMETHODCALLTYPE IUnknownImpl::AddRef() { - ref_count_.Increment(); - return 1; -} - -ULONG STDMETHODCALLTYPE IUnknownImpl::Release() { - if (!ref_count_.Decrement()) { - delete this; - return 0; - } - return 1; -} - -STDMETHODIMP IUnknownImpl::QueryInterface(REFIID riid, void** ppv) { - if (riid == IID_IUnknown) { - *ppv = static_cast<IUnknown*>(this); - AddRef(); - return S_OK; - } - - *ppv = NULL; - return E_NOINTERFACE; -} - -} // namespace win -} // namespace base diff --git a/chromium/base/win/iunknown_impl.h b/chromium/base/win/iunknown_impl.h deleted file mode 100644 index b7de205ac6c..00000000000 --- a/chromium/base/win/iunknown_impl.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_WIN_IUNKNOWN_IMPL_H_ -#define BASE_WIN_IUNKNOWN_IMPL_H_ - -#include <unknwn.h> - -#include "base/atomic_ref_count.h" -#include "base/base_export.h" -#include "base/compiler_specific.h" - -namespace base { -namespace win { - -// IUnknown implementation for other classes to derive from. -class BASE_EXPORT IUnknownImpl : public IUnknown { - public: - IUnknownImpl(); - - ULONG STDMETHODCALLTYPE AddRef() override; - ULONG STDMETHODCALLTYPE Release() override; - - // Subclasses should extend this to return any interfaces they provide. - STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override; - - protected: - virtual ~IUnknownImpl(); - - private: - AtomicRefCount ref_count_; -}; - -} // namespace win -} // namespace base - -#endif // BASE_WIN_IUNKNOWN_IMPL_H_ diff --git a/chromium/base/win/iunknown_impl_unittest.cc b/chromium/base/win/iunknown_impl_unittest.cc deleted file mode 100644 index c6c3539037e..00000000000 --- a/chromium/base/win/iunknown_impl_unittest.cc +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/win/iunknown_impl.h" - -#include "base/win/scoped_com_initializer.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace win { - -class TestIUnknownImplSubclass : public IUnknownImpl { - public: - TestIUnknownImplSubclass() { - ++instance_count; - } - ~TestIUnknownImplSubclass() override { --instance_count; } - static int instance_count; -}; - -// static -int TestIUnknownImplSubclass::instance_count = 0; - -TEST(IUnknownImplTest, IUnknownImpl) { - ScopedCOMInitializer com_initializer; - - EXPECT_EQ(0, TestIUnknownImplSubclass::instance_count); - IUnknown* u = new TestIUnknownImplSubclass(); - - EXPECT_EQ(1, TestIUnknownImplSubclass::instance_count); - - EXPECT_EQ(1u, u->AddRef()); - EXPECT_EQ(1u, u->AddRef()); - - IUnknown* other = NULL; - EXPECT_EQ(E_NOINTERFACE, u->QueryInterface( - IID_IDispatch, reinterpret_cast<void**>(&other))); - EXPECT_EQ(S_OK, u->QueryInterface( - IID_IUnknown, reinterpret_cast<void**>(&other))); - other->Release(); - - EXPECT_EQ(1u, u->Release()); - EXPECT_EQ(0u, u->Release()); - EXPECT_EQ(0, TestIUnknownImplSubclass::instance_count); -} - -} // namespace win -} // namespace base diff --git a/chromium/base/win/object_watcher.cc b/chromium/base/win/object_watcher.cc index 20f146655b0..bc4fd1a11e3 100644 --- a/chromium/base/win/object_watcher.cc +++ b/chromium/base/win/object_watcher.cc @@ -21,13 +21,16 @@ ObjectWatcher::~ObjectWatcher() { StopWatching(); } -bool ObjectWatcher::StartWatchingOnce(HANDLE object, Delegate* delegate) { - return StartWatchingInternal(object, delegate, true); +bool ObjectWatcher::StartWatchingOnce(HANDLE object, + Delegate* delegate, + const Location& from_here) { + return StartWatchingInternal(object, delegate, true, from_here); } bool ObjectWatcher::StartWatchingMultipleTimes(HANDLE object, - Delegate* delegate) { - return StartWatchingInternal(object, delegate, false); + Delegate* delegate, + const Location& from_here) { + return StartWatchingInternal(object, delegate, false, from_here); } bool ObjectWatcher::StopWatching() { @@ -63,17 +66,20 @@ void CALLBACK ObjectWatcher::DoneWaiting(void* param, BOOLEAN timed_out) { // The destructor blocks on any callbacks that are in flight, so we know that // that is always a pointer to a valid ObjectWater. ObjectWatcher* that = static_cast<ObjectWatcher*>(param); - that->task_runner_->PostTask(FROM_HERE, that->callback_); + that->task_runner_->PostTask(that->location_, that->callback_); if (that->run_once_) that->callback_.Reset(); } -bool ObjectWatcher::StartWatchingInternal(HANDLE object, Delegate* delegate, - bool execute_only_once) { +bool ObjectWatcher::StartWatchingInternal(HANDLE object, + Delegate* delegate, + bool execute_only_once, + const Location& from_here) { DCHECK(delegate); DCHECK(!wait_object_) << "Already watching an object"; DCHECK(SequencedTaskRunnerHandle::IsSet()); + location_ = from_here; task_runner_ = SequencedTaskRunnerHandle::Get(); run_once_ = execute_only_once; @@ -112,6 +118,7 @@ void ObjectWatcher::Signal(Delegate* delegate) { void ObjectWatcher::Reset() { callback_.Reset(); + location_ = {}; object_ = nullptr; wait_object_ = nullptr; task_runner_ = nullptr; diff --git a/chromium/base/win/object_watcher.h b/chromium/base/win/object_watcher.h index 0779c271325..62b7277ad65 100644 --- a/chromium/base/win/object_watcher.h +++ b/chromium/base/win/object_watcher.h @@ -71,14 +71,19 @@ class BASE_EXPORT ObjectWatcher { // where StartWatchingOnce is called. The ObjectWatcher is not responsible for // deleting the delegate. // Returns whether watching was successfully initiated. - bool StartWatchingOnce(HANDLE object, Delegate* delegate); + bool StartWatchingOnce(HANDLE object, + Delegate* delegate, + const Location& from_here = Location::Current()); // Notifies the delegate, on the sequence where this method is called, each // time the object is set. By definition, the handle must be an auto-reset // object. The caller must ensure that it (or any Windows system code) doesn't // reset the event or else the delegate won't be called. // Returns whether watching was successfully initiated. - bool StartWatchingMultipleTimes(HANDLE object, Delegate* delegate); + bool StartWatchingMultipleTimes( + HANDLE object, + Delegate* delegate, + const Location& from_here = Location::Current()); // Stops watching. Does nothing if the watch has already completed. If the // watch is still active, then it is canceled, and the associated delegate is @@ -98,13 +103,17 @@ class BASE_EXPORT ObjectWatcher { static void CALLBACK DoneWaiting(void* param, BOOLEAN timed_out); // Helper used by StartWatchingOnce and StartWatchingMultipleTimes. - bool StartWatchingInternal(HANDLE object, Delegate* delegate, - bool execute_only_once); + bool StartWatchingInternal(HANDLE object, + Delegate* delegate, + bool execute_only_once, + const Location& from_here); void Signal(Delegate* delegate); void Reset(); + Location location_; + // A callback pre-bound to Signal() that is posted to the caller's task runner // when the wait completes. RepeatingClosure callback_; diff --git a/chromium/base/win/pe_image_unittest.cc b/chromium/base/win/pe_image_unittest.cc index caf241b8d22..45279c39815 100644 --- a/chromium/base/win/pe_image_unittest.cc +++ b/chromium/base/win/pe_image_unittest.cc @@ -182,29 +182,19 @@ TEST(PEImageTest, EnumeratesPEWithTargetModule) { const char kTargetModuleStatic[] = "user32.dll"; const char kTargetModuleDelay[] = "cfgmgr32.dll"; -#if defined(ARCH_CPU_ARM64) - const int kSections = 7; - const int kImportsDlls = 2; + const int kImportsDlls = 1; const int kDelayDlls = 1; const int kExports = 3; const int kImports = 2; const int kDelayImports = 1; +#if defined(ARCH_CPU_ARM64) + const int kSections = 7; const int kRelocs = 740; #elif defined(ARCH_CPU_64_BITS) const int kSections = 6; - const int kImportsDlls = 1; - const int kDelayDlls = 1; - const int kExports = 3; - const int kImports = 2; - const int kDelayImports = 1; const int kRelocs = 976; #else const int kSections = 5; - const int kImportsDlls = 1; - const int kDelayDlls = 1; - const int kExports = 3; - const int kImports = 2; - const int kDelayImports = 1; const int kRelocs = 2114; #endif diff --git a/chromium/base/win/registry.cc b/chromium/base/win/registry.cc index b95b4746858..cd6208eda5f 100644 --- a/chromium/base/win/registry.cc +++ b/chromium/base/win/registry.cc @@ -13,6 +13,7 @@ #include "base/logging.h" #include "base/stl_util.h" #include "base/strings/string_util.h" +#include "base/strings/string_util_win.h" #include "base/threading/thread_restrictions.h" #include "base/win/shlwapi.h" #include "base/win/windows_version.h" @@ -95,7 +96,7 @@ RegKey::RegKey() : key_(NULL), wow64access_(0) { RegKey::RegKey(HKEY key) : key_(key), wow64access_(0) { } -RegKey::RegKey(HKEY rootkey, const char16* subkey, REGSAM access) +RegKey::RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access) : key_(NULL), wow64access_(0) { if (rootkey) { if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK)) @@ -112,20 +113,20 @@ RegKey::~RegKey() { Close(); } -LONG RegKey::Create(HKEY rootkey, const char16* subkey, REGSAM access) { +LONG RegKey::Create(HKEY rootkey, const wchar_t* subkey, REGSAM access) { DWORD disposition_value; return CreateWithDisposition(rootkey, subkey, &disposition_value, access); } LONG RegKey::CreateWithDisposition(HKEY rootkey, - const char16* subkey, + const wchar_t* subkey, DWORD* disposition, REGSAM access) { DCHECK(rootkey && subkey && access && disposition); HKEY subhkey = NULL; - LONG result = RegCreateKeyEx(rootkey, as_wcstr(subkey), 0, NULL, - REG_OPTION_NON_VOLATILE, access, NULL, &subhkey, - disposition); + LONG result = + RegCreateKeyEx(rootkey, subkey, 0, NULL, REG_OPTION_NON_VOLATILE, access, + NULL, &subhkey, disposition); if (result == ERROR_SUCCESS) { Close(); key_ = subhkey; @@ -135,7 +136,7 @@ LONG RegKey::CreateWithDisposition(HKEY rootkey, return result; } -LONG RegKey::CreateKey(const char16* name, REGSAM access) { +LONG RegKey::CreateKey(const wchar_t* name, REGSAM access) { DCHECK(name && access); // After the application has accessed an alternate registry view using one of // the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations @@ -147,9 +148,8 @@ LONG RegKey::CreateKey(const char16* name, REGSAM access) { return ERROR_INVALID_PARAMETER; } HKEY subkey = NULL; - LONG result = - RegCreateKeyEx(key_, as_wcstr(name), 0, NULL, REG_OPTION_NON_VOLATILE, - access, NULL, &subkey, NULL); + LONG result = RegCreateKeyEx(key_, name, 0, NULL, REG_OPTION_NON_VOLATILE, + access, NULL, &subkey, NULL); if (result == ERROR_SUCCESS) { Close(); key_ = subkey; @@ -159,11 +159,11 @@ LONG RegKey::CreateKey(const char16* name, REGSAM access) { return result; } -LONG RegKey::Open(HKEY rootkey, const char16* subkey, REGSAM access) { +LONG RegKey::Open(HKEY rootkey, const wchar_t* subkey, REGSAM access) { DCHECK(rootkey && subkey && access); HKEY subhkey = NULL; - LONG result = RegOpenKeyEx(rootkey, as_wcstr(subkey), 0, access, &subhkey); + LONG result = RegOpenKeyEx(rootkey, subkey, 0, access, &subhkey); if (result == ERROR_SUCCESS) { Close(); key_ = subhkey; @@ -173,7 +173,7 @@ LONG RegKey::Open(HKEY rootkey, const char16* subkey, REGSAM access) { return result; } -LONG RegKey::OpenKey(const char16* relative_key_name, REGSAM access) { +LONG RegKey::OpenKey(const wchar_t* relative_key_name, REGSAM access) { DCHECK(relative_key_name && access); // After the application has accessed an alternate registry view using one of // the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations @@ -185,8 +185,7 @@ LONG RegKey::OpenKey(const char16* relative_key_name, REGSAM access) { return ERROR_INVALID_PARAMETER; } HKEY subkey = NULL; - LONG result = - RegOpenKeyEx(key_, as_wcstr(relative_key_name), 0, access, &subkey); + LONG result = RegOpenKeyEx(key_, relative_key_name, 0, access, &subkey); // We have to close the current opened key before replacing it with the new // one. @@ -221,9 +220,8 @@ HKEY RegKey::Take() { return key; } -bool RegKey::HasValue(const char16* name) const { - return RegQueryValueEx(key_, as_wcstr(name), 0, NULL, NULL, NULL) == - ERROR_SUCCESS; +bool RegKey::HasValue(const wchar_t* name) const { + return RegQueryValueEx(key_, name, 0, NULL, NULL, NULL) == ERROR_SUCCESS; } DWORD RegKey::GetValueCount() const { @@ -233,26 +231,25 @@ DWORD RegKey::GetValueCount() const { return (result == ERROR_SUCCESS) ? count : 0; } -LONG RegKey::GetValueNameAt(int index, string16* name) const { - char16 buf[256]; +LONG RegKey::GetValueNameAt(int index, std::wstring* name) const { + wchar_t buf[256]; DWORD bufsize = size(buf); - LONG r = ::RegEnumValue(key_, index, as_writable_wcstr(buf), &bufsize, NULL, - NULL, NULL, NULL); + LONG r = ::RegEnumValue(key_, index, buf, &bufsize, NULL, NULL, NULL, NULL); if (r == ERROR_SUCCESS) name->assign(buf, bufsize); return r; } -LONG RegKey::DeleteKey(const char16* name) { +LONG RegKey::DeleteKey(const wchar_t* name) { DCHECK(key_); DCHECK(name); HKEY subkey = NULL; // Verify the key exists before attempting delete to replicate previous // behavior. - LONG result = RegOpenKeyEx(key_, as_wcstr(name), 0, - READ_CONTROL | wow64access_, &subkey); + LONG result = + RegOpenKeyEx(key_, name, 0, READ_CONTROL | wow64access_, &subkey); if (result != ERROR_SUCCESS) return result; RegCloseKey(subkey); @@ -260,13 +257,13 @@ LONG RegKey::DeleteKey(const char16* name) { return RegDelRecurse(key_, name, wow64access_); } -LONG RegKey::DeleteEmptyKey(const char16* name) { +LONG RegKey::DeleteEmptyKey(const wchar_t* name) { DCHECK(key_); DCHECK(name); HKEY target_key = NULL; - LONG result = RegOpenKeyEx(key_, as_wcstr(name), 0, KEY_READ | wow64access_, - &target_key); + LONG result = + RegOpenKeyEx(key_, name, 0, KEY_READ | wow64access_, &target_key); if (result != ERROR_SUCCESS) return result; @@ -281,18 +278,18 @@ LONG RegKey::DeleteEmptyKey(const char16* name) { return result; if (count == 0) - return RegDeleteKeyEx(key_, as_wcstr(name), wow64access_, 0); + return RegDeleteKeyEx(key_, name, wow64access_, 0); return ERROR_DIR_NOT_EMPTY; } -LONG RegKey::DeleteValue(const char16* value_name) { +LONG RegKey::DeleteValue(const wchar_t* value_name) { DCHECK(key_); - LONG result = RegDeleteValue(key_, as_wcstr(value_name)); + LONG result = RegDeleteValue(key_, value_name); return result; } -LONG RegKey::ReadValueDW(const char16* name, DWORD* out_value) const { +LONG RegKey::ReadValueDW(const wchar_t* name, DWORD* out_value) const { DCHECK(out_value); DWORD type = REG_DWORD; DWORD size = sizeof(DWORD); @@ -308,7 +305,7 @@ LONG RegKey::ReadValueDW(const char16* name, DWORD* out_value) const { return result; } -LONG RegKey::ReadInt64(const char16* name, int64_t* out_value) const { +LONG RegKey::ReadInt64(const wchar_t* name, int64_t* out_value) const { DCHECK(out_value); DWORD type = REG_QWORD; int64_t local_value = 0; @@ -325,20 +322,19 @@ LONG RegKey::ReadInt64(const char16* name, int64_t* out_value) const { return result; } -LONG RegKey::ReadValue(const char16* name, string16* out_value) const { +LONG RegKey::ReadValue(const wchar_t* name, std::wstring* out_value) const { DCHECK(out_value); const size_t kMaxStringLength = 1024; // This is after expansion. // Use the one of the other forms of ReadValue if 1024 is too small for you. - char16 raw_value[kMaxStringLength]; + wchar_t raw_value[kMaxStringLength]; DWORD type = REG_SZ, size = sizeof(raw_value); - LONG result = ReadValue(name, as_writable_wcstr(raw_value), &size, &type); + LONG result = ReadValue(name, raw_value, &size, &type); if (result == ERROR_SUCCESS) { if (type == REG_SZ) { *out_value = raw_value; } else if (type == REG_EXPAND_SZ) { - char16 expanded[kMaxStringLength]; - size = ExpandEnvironmentStrings( - as_wcstr(raw_value), as_writable_wcstr(expanded), kMaxStringLength); + wchar_t expanded[kMaxStringLength]; + size = ExpandEnvironmentStrings(raw_value, expanded, kMaxStringLength); // Success: returns the number of wchar_t's copied // Fail: buffer too small, returns the size required // Fail: other, returns 0 @@ -356,16 +352,17 @@ LONG RegKey::ReadValue(const char16* name, string16* out_value) const { return result; } -LONG RegKey::ReadValue(const char16* name, +LONG RegKey::ReadValue(const wchar_t* name, void* data, DWORD* dsize, DWORD* dtype) const { - LONG result = RegQueryValueEx(key_, as_wcstr(name), 0, dtype, + LONG result = RegQueryValueEx(key_, name, 0, dtype, reinterpret_cast<LPBYTE>(data), dsize); return result; } -LONG RegKey::ReadValues(const char16* name, std::vector<string16>* values) { +LONG RegKey::ReadValues(const wchar_t* name, + std::vector<std::wstring>* values) { values->clear(); DWORD type = REG_MULTI_SZ; @@ -377,8 +374,8 @@ LONG RegKey::ReadValues(const char16* name, std::vector<string16>* values) { if (type != REG_MULTI_SZ) return ERROR_CANTREAD; - std::vector<char16> buffer(size / sizeof(char16)); - result = ReadValue(name, as_writable_wcstr(buffer.data()), &size, NULL); + std::vector<wchar_t> buffer(size / sizeof(wchar_t)); + result = ReadValue(name, buffer.data(), &size, NULL); if (result != ERROR_SUCCESS || size == 0) return result; @@ -395,27 +392,27 @@ LONG RegKey::ReadValues(const char16* name, std::vector<string16>* values) { return 0; } -LONG RegKey::WriteValue(const char16* name, DWORD in_value) { +LONG RegKey::WriteValue(const wchar_t* name, DWORD in_value) { return WriteValue( name, &in_value, static_cast<DWORD>(sizeof(in_value)), REG_DWORD); } -LONG RegKey::WriteValue(const char16* name, const char16* in_value) { +LONG RegKey::WriteValue(const wchar_t* name, const wchar_t* in_value) { return WriteValue( name, in_value, static_cast<DWORD>(sizeof(*in_value) * - (std::char_traits<char16>::length(in_value) + 1)), + (std::char_traits<wchar_t>::length(in_value) + 1)), REG_SZ); } -LONG RegKey::WriteValue(const char16* name, +LONG RegKey::WriteValue(const wchar_t* name, const void* data, DWORD dsize, DWORD dtype) { DCHECK(data || !dsize); LONG result = - RegSetValueEx(key_, as_wcstr(name), 0, dtype, + RegSetValueEx(key_, name, 0, dtype, reinterpret_cast<LPBYTE>(const_cast<void*>(data)), dsize); return result; } @@ -431,22 +428,22 @@ bool RegKey::StartWatching(ChangeCallback callback) { } // static -LONG RegKey::RegDelRecurse(HKEY root_key, const char16* name, REGSAM access) { +LONG RegKey::RegDelRecurse(HKEY root_key, const wchar_t* name, REGSAM access) { // First, see if the key can be deleted without having to recurse. - LONG result = RegDeleteKeyEx(root_key, as_wcstr(name), access, 0); + LONG result = RegDeleteKeyEx(root_key, name, access, 0); if (result == ERROR_SUCCESS) return result; HKEY target_key = NULL; - result = RegOpenKeyEx(root_key, as_wcstr(name), 0, - KEY_ENUMERATE_SUB_KEYS | access, &target_key); + result = RegOpenKeyEx(root_key, name, 0, KEY_ENUMERATE_SUB_KEYS | access, + &target_key); if (result == ERROR_FILE_NOT_FOUND) return ERROR_SUCCESS; if (result != ERROR_SUCCESS) return result; - string16 subkey_name(name); + std::wstring subkey_name(name); // Check for an ending slash and add one if it is missing. if (!subkey_name.empty() && subkey_name.back() != '\\') @@ -456,12 +453,11 @@ LONG RegKey::RegDelRecurse(HKEY root_key, const char16* name, REGSAM access) { result = ERROR_SUCCESS; const DWORD kMaxKeyNameLength = MAX_PATH; const size_t base_key_length = subkey_name.length(); - string16 key_name; + std::wstring key_name; while (result == ERROR_SUCCESS) { DWORD key_size = kMaxKeyNameLength; result = - RegEnumKeyEx(target_key, 0, - as_writable_wcstr(WriteInto(&key_name, kMaxKeyNameLength)), + RegEnumKeyEx(target_key, 0, WriteIntoW(&key_name, kMaxKeyNameLength), &key_size, NULL, NULL, NULL, NULL); if (result != ERROR_SUCCESS) @@ -478,7 +474,7 @@ LONG RegKey::RegDelRecurse(HKEY root_key, const char16* name, REGSAM access) { RegCloseKey(target_key); // Try again to delete the key. - result = RegDeleteKeyEx(root_key, as_wcstr(name), access, 0); + result = RegDeleteKeyEx(root_key, name, access, 0); return result; } @@ -486,24 +482,24 @@ LONG RegKey::RegDelRecurse(HKEY root_key, const char16* name, REGSAM access) { // RegistryValueIterator ------------------------------------------------------ RegistryValueIterator::RegistryValueIterator(HKEY root_key, - const char16* folder_key, + const wchar_t* folder_key, REGSAM wow64access) : name_(MAX_PATH, '\0'), value_(MAX_PATH, '\0') { Initialize(root_key, folder_key, wow64access); } RegistryValueIterator::RegistryValueIterator(HKEY root_key, - const char16* folder_key) + const wchar_t* folder_key) : name_(MAX_PATH, '\0'), value_(MAX_PATH, '\0') { Initialize(root_key, folder_key, 0); } void RegistryValueIterator::Initialize(HKEY root_key, - const char16* folder_key, + const wchar_t* folder_key, REGSAM wow64access) { DCHECK_EQ(wow64access & ~kWow64AccessMask, static_cast<REGSAM>(0)); - LONG result = RegOpenKeyEx(root_key, as_wcstr(folder_key), 0, - KEY_READ | wow64access, &key_); + LONG result = + RegOpenKeyEx(root_key, folder_key, 0, KEY_READ | wow64access, &key_); if (result != ERROR_SUCCESS) { key_ = NULL; } else { @@ -551,13 +547,10 @@ bool RegistryValueIterator::Read() { DWORD capacity = static_cast<DWORD>(name_.capacity()); DWORD name_size = capacity; // |value_size_| is in bytes. Reserve the last character for a NUL. - static_assert(sizeof(wchar_t) == sizeof(char16), - "wchar_t is not the same size as base::char16"); value_size_ = static_cast<DWORD>((value_.size() - 1) * sizeof(wchar_t)); LONG result = ::RegEnumValue( - key_, index_, as_writable_wcstr(WriteInto(&name_, name_size)), - &name_size, NULL, &type_, reinterpret_cast<BYTE*>(value_.data()), - &value_size_); + key_, index_, WriteIntoW(&name_, name_size), &name_size, NULL, &type_, + reinterpret_cast<BYTE*>(value_.data()), &value_size_); if (result == ERROR_MORE_DATA) { // Registry key names are limited to 255 characters and fit within @@ -572,9 +565,8 @@ bool RegistryValueIterator::Read() { value_size_ = static_cast<DWORD>((value_.size() - 1) * sizeof(wchar_t)); name_size = name_size == capacity ? MAX_REGISTRY_NAME_SIZE : capacity; result = ::RegEnumValue( - key_, index_, as_writable_wcstr(WriteInto(&name_, name_size)), - &name_size, NULL, &type_, reinterpret_cast<BYTE*>(value_.data()), - &value_size_); + key_, index_, WriteIntoW(&name_, name_size), &name_size, NULL, &type_, + reinterpret_cast<BYTE*>(value_.data()), &value_size_); } if (result == ERROR_SUCCESS) { @@ -593,12 +585,12 @@ bool RegistryValueIterator::Read() { // RegistryKeyIterator -------------------------------------------------------- RegistryKeyIterator::RegistryKeyIterator(HKEY root_key, - const char16* folder_key) { + const wchar_t* folder_key) { Initialize(root_key, folder_key, 0); } RegistryKeyIterator::RegistryKeyIterator(HKEY root_key, - const char16* folder_key, + const wchar_t* folder_key, REGSAM wow64access) { Initialize(root_key, folder_key, wow64access); } @@ -631,8 +623,8 @@ bool RegistryKeyIterator::Read() { if (Valid()) { DWORD ncount = static_cast<DWORD>(size(name_)); FILETIME written; - LONG r = ::RegEnumKeyEx(key_, index_, as_writable_wcstr(name_), &ncount, - NULL, NULL, NULL, &written); + LONG r = ::RegEnumKeyEx(key_, index_, name_, &ncount, NULL, NULL, NULL, + &written); if (ERROR_SUCCESS == r) return true; } @@ -642,11 +634,11 @@ bool RegistryKeyIterator::Read() { } void RegistryKeyIterator::Initialize(HKEY root_key, - const char16* folder_key, + const wchar_t* folder_key, REGSAM wow64access) { DCHECK_EQ(wow64access & ~kWow64AccessMask, static_cast<REGSAM>(0)); - LONG result = RegOpenKeyEx(root_key, as_wcstr(folder_key), 0, - KEY_READ | wow64access, &key_); + LONG result = + RegOpenKeyEx(root_key, folder_key, 0, KEY_READ | wow64access, &key_); if (result != ERROR_SUCCESS) { key_ = NULL; } else { diff --git a/chromium/base/win/registry.h b/chromium/base/win/registry.h index 1c57fc9f537..f7728219355 100644 --- a/chromium/base/win/registry.h +++ b/chromium/base/win/registry.h @@ -8,12 +8,12 @@ #include <stdint.h> #include <memory> +#include <string> #include <vector> #include "base/base_export.h" #include "base/callback.h" #include "base/macros.h" -#include "base/strings/string16.h" #include "base/win/object_watcher.h" #include "base/win/scoped_handle.h" #include "base/win/windows_types.h" @@ -37,24 +37,24 @@ class BASE_EXPORT RegKey { RegKey(); explicit RegKey(HKEY key); - RegKey(HKEY rootkey, const char16* subkey, REGSAM access); + RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access); ~RegKey(); - LONG Create(HKEY rootkey, const char16* subkey, REGSAM access); + LONG Create(HKEY rootkey, const wchar_t* subkey, REGSAM access); LONG CreateWithDisposition(HKEY rootkey, - const char16* subkey, + const wchar_t* subkey, DWORD* disposition, REGSAM access); // Creates a subkey or open it if it already exists. - LONG CreateKey(const char16* name, REGSAM access); + LONG CreateKey(const wchar_t* name, REGSAM access); // Opens an existing reg key. - LONG Open(HKEY rootkey, const char16* subkey, REGSAM access); + LONG Open(HKEY rootkey, const wchar_t* subkey, REGSAM access); // Opens an existing reg key, given the relative key name. - LONG OpenKey(const char16* relative_key_name, REGSAM access); + LONG OpenKey(const wchar_t* relative_key_name, REGSAM access); // Closes this reg key. void Close(); @@ -67,51 +67,51 @@ class BASE_EXPORT RegKey { // Returns false if this key does not have the specified value, or if an error // occurrs while attempting to access it. - bool HasValue(const char16* value_name) const; + bool HasValue(const wchar_t* value_name) const; // Returns the number of values for this key, or 0 if the number cannot be // determined. DWORD GetValueCount() const; // Determines the nth value's name. - LONG GetValueNameAt(int index, string16* name) const; + LONG GetValueNameAt(int index, std::wstring* name) const; // True while the key is valid. bool Valid() const { return key_ != NULL; } // Kills a key and everything that lives below it; please be careful when // using it. - LONG DeleteKey(const char16* name); + LONG DeleteKey(const wchar_t* name); // Deletes an empty subkey. If the subkey has subkeys or values then this // will fail. - LONG DeleteEmptyKey(const char16* name); + LONG DeleteEmptyKey(const wchar_t* name); // Deletes a single value within the key. - LONG DeleteValue(const char16* name); + LONG DeleteValue(const wchar_t* name); // Getters: // Reads a REG_DWORD (uint32_t) into |out_value|. If |name| is null or empty, // reads the key's default value, if any. - LONG ReadValueDW(const char16* name, DWORD* out_value) const; + LONG ReadValueDW(const wchar_t* name, DWORD* out_value) const; // Reads a REG_QWORD (int64_t) into |out_value|. If |name| is null or empty, // reads the key's default value, if any. - LONG ReadInt64(const char16* name, int64_t* out_value) const; + LONG ReadInt64(const wchar_t* name, int64_t* out_value) const; // Reads a string into |out_value|. If |name| is null or empty, reads // the key's default value, if any. - LONG ReadValue(const char16* name, string16* out_value) const; + LONG ReadValue(const wchar_t* name, std::wstring* out_value) const; // Reads a REG_MULTI_SZ registry field into a vector of strings. Clears // |values| initially and adds further strings to the list. Returns // ERROR_CANTREAD if type is not REG_MULTI_SZ. - LONG ReadValues(const char16* name, std::vector<string16>* values); + LONG ReadValues(const wchar_t* name, std::vector<std::wstring>* values); // Reads raw data into |data|. If |name| is null or empty, reads the key's // default value, if any. - LONG ReadValue(const char16* name, + LONG ReadValue(const wchar_t* name, void* data, DWORD* dsize, DWORD* dtype) const; @@ -119,13 +119,13 @@ class BASE_EXPORT RegKey { // Setters: // Sets an int32_t value. - LONG WriteValue(const char16* name, DWORD in_value); + LONG WriteValue(const wchar_t* name, DWORD in_value); // Sets a string value. - LONG WriteValue(const char16* name, const char16* in_value); + LONG WriteValue(const wchar_t* name, const wchar_t* in_value); // Sets raw data, including type. - LONG WriteValue(const char16* name, + LONG WriteValue(const wchar_t* name, const void* data, DWORD dsize, DWORD dtype); @@ -143,7 +143,7 @@ class BASE_EXPORT RegKey { class Watcher; // Recursively deletes a key and all of its subkeys. - static LONG RegDelRecurse(HKEY root_key, const char16* name, REGSAM access); + static LONG RegDelRecurse(HKEY root_key, const wchar_t* name, REGSAM access); HKEY key_; // The registry key being iterated. REGSAM wow64access_; @@ -156,7 +156,7 @@ class BASE_EXPORT RegKey { class BASE_EXPORT RegistryValueIterator { public: // Constructs a Registry Value Iterator with default WOW64 access. - RegistryValueIterator(HKEY root_key, const char16* folder_key); + RegistryValueIterator(HKEY root_key, const wchar_t* folder_key); // Constructs a Registry Key Iterator with specific WOW64 access, one of // KEY_WOW64_32KEY or KEY_WOW64_64KEY, or 0. @@ -164,7 +164,7 @@ class BASE_EXPORT RegistryValueIterator { // previously, or a predefined key (e.g. HKEY_LOCAL_MACHINE). // See http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx. RegistryValueIterator(HKEY root_key, - const char16* folder_key, + const wchar_t* folder_key, REGSAM wow64access); ~RegistryValueIterator(); @@ -177,8 +177,8 @@ class BASE_EXPORT RegistryValueIterator { // Advances to the next registry entry. void operator++(); - const char16* Name() const { return name_.c_str(); } - const char16* Value() const { return value_.data(); } + const wchar_t* Name() const { return name_.c_str(); } + const wchar_t* Value() const { return value_.data(); } // ValueSize() is in bytes. DWORD ValueSize() const { return value_size_; } DWORD Type() const { return type_; } @@ -189,7 +189,7 @@ class BASE_EXPORT RegistryValueIterator { // Reads in the current values. bool Read(); - void Initialize(HKEY root_key, const char16* folder_key, REGSAM wow64access); + void Initialize(HKEY root_key, const wchar_t* folder_key, REGSAM wow64access); // The registry key being iterated. HKEY key_; @@ -198,8 +198,8 @@ class BASE_EXPORT RegistryValueIterator { int index_; // Current values. - string16 name_; - std::vector<char16> value_; + std::wstring name_; + std::vector<wchar_t> value_; DWORD value_size_; DWORD type_; @@ -209,7 +209,7 @@ class BASE_EXPORT RegistryValueIterator { class BASE_EXPORT RegistryKeyIterator { public: // Constructs a Registry Key Iterator with default WOW64 access. - RegistryKeyIterator(HKEY root_key, const char16* folder_key); + RegistryKeyIterator(HKEY root_key, const wchar_t* folder_key); // Constructs a Registry Value Iterator with specific WOW64 access, one of // KEY_WOW64_32KEY or KEY_WOW64_64KEY, or 0. @@ -217,7 +217,7 @@ class BASE_EXPORT RegistryKeyIterator { // previously, or a predefined key (e.g. HKEY_LOCAL_MACHINE). // See http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx. RegistryKeyIterator(HKEY root_key, - const char16* folder_key, + const wchar_t* folder_key, REGSAM wow64access); ~RegistryKeyIterator(); @@ -230,7 +230,7 @@ class BASE_EXPORT RegistryKeyIterator { // Advances to the next entry in the folder. void operator++(); - const char16* Name() const { return name_; } + const wchar_t* Name() const { return name_; } int Index() const { return index_; } @@ -238,7 +238,7 @@ class BASE_EXPORT RegistryKeyIterator { // Reads in the current values. bool Read(); - void Initialize(HKEY root_key, const char16* folder_key, REGSAM wow64access); + void Initialize(HKEY root_key, const wchar_t* folder_key, REGSAM wow64access); // The registry key being iterated. HKEY key_; @@ -246,7 +246,7 @@ class BASE_EXPORT RegistryKeyIterator { // Current index of the iteration. int index_; - char16 name_[MAX_PATH]; + wchar_t name_[MAX_PATH]; DISALLOW_COPY_AND_ASSIGN(RegistryKeyIterator); }; diff --git a/chromium/base/win/registry_unittest.cc b/chromium/base/win/registry_unittest.cc index 12ebc00a1d0..50be1d3230f 100644 --- a/chromium/base/win/registry_unittest.cc +++ b/chromium/base/win/registry_unittest.cc @@ -23,7 +23,7 @@ namespace win { namespace { -const char16 kRootKey[] = STRING16_LITERAL("Base_Registry_Unittest"); +const wchar_t kRootKey[] = L"Base_Registry_Unittest"; class RegistryTest : public testing::Test { protected: @@ -38,18 +38,18 @@ class RegistryTest : public testing::Test { RegistryTest() {} void SetUp() override { // Create a temporary key. - RegKey key(HKEY_CURRENT_USER, STRING16_LITERAL(""), KEY_ALL_ACCESS); + RegKey key(HKEY_CURRENT_USER, L"", KEY_ALL_ACCESS); key.DeleteKey(kRootKey); ASSERT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_READ)); ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, kRootKey, KEY_READ)); - foo_software_key_ = STRING16_LITERAL("Software\\"); + foo_software_key_ = L"Software\\"; foo_software_key_ += kRootKey; - foo_software_key_ += STRING16_LITERAL("\\Foo"); + foo_software_key_ += L"\\Foo"; } void TearDown() override { // Clean up the temporary key. - RegKey key(HKEY_CURRENT_USER, STRING16_LITERAL(""), KEY_SET_VALUE); + RegKey key(HKEY_CURRENT_USER, L"", KEY_SET_VALUE); ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(kRootKey)); ASSERT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_READ)); } @@ -62,7 +62,7 @@ class RegistryTest : public testing::Test { #endif } - string16 foo_software_key_; + std::wstring foo_software_key_; private: DISALLOW_COPY_AND_ASSIGN(RegistryTest); @@ -75,8 +75,8 @@ const REGSAM RegistryTest::kRedirectedViewMask; TEST_F(RegistryTest, ValueTest) { RegKey key; - string16 foo_key(kRootKey); - foo_key += STRING16_LITERAL("\\Foo"); + std::wstring foo_key(kRootKey); + foo_key += L"\\Foo"; ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ)); @@ -85,10 +85,10 @@ TEST_F(RegistryTest, ValueTest) { KEY_READ | KEY_SET_VALUE)); ASSERT_TRUE(key.Valid()); - const char16 kStringValueName[] = STRING16_LITERAL("StringValue"); - const char16 kDWORDValueName[] = STRING16_LITERAL("DWORDValue"); - const char16 kInt64ValueName[] = STRING16_LITERAL("Int64Value"); - const char16 kStringData[] = STRING16_LITERAL("string data"); + const wchar_t kStringValueName[] = L"StringValue"; + const wchar_t kDWORDValueName[] = L"DWORDValue"; + const wchar_t kInt64ValueName[] = L"Int64Value"; + const wchar_t kStringData[] = L"string data"; const DWORD kDWORDData = 0xdeadbabe; const int64_t kInt64Data = 0xdeadbabedeadbabeLL; @@ -103,7 +103,7 @@ TEST_F(RegistryTest, ValueTest) { EXPECT_TRUE(key.HasValue(kInt64ValueName)); // Test Read - string16 string_value; + std::wstring string_value; DWORD dword_value = 0; int64_t int64_value = 0; ASSERT_EQ(ERROR_SUCCESS, key.ReadValue(kStringValueName, &string_value)); @@ -114,7 +114,7 @@ TEST_F(RegistryTest, ValueTest) { EXPECT_EQ(kInt64Data, int64_value); // Make sure out args are not touched if ReadValue fails - const char16* kNonExistent = STRING16_LITERAL("NonExistent"); + const wchar_t* kNonExistent = L"NonExistent"; ASSERT_NE(ERROR_SUCCESS, key.ReadValue(kNonExistent, &string_value)); ASSERT_NE(ERROR_SUCCESS, key.ReadValueDW(kNonExistent, &dword_value)); ASSERT_NE(ERROR_SUCCESS, key.ReadInt64(kNonExistent, &int64_value)); @@ -135,8 +135,8 @@ TEST_F(RegistryTest, ValueTest) { TEST_F(RegistryTest, BigValueIteratorTest) { RegKey key; - string16 foo_key(kRootKey); - foo_key += STRING16_LITERAL("\\Foo"); + std::wstring foo_key(kRootKey); + foo_key += L"\\Foo"; ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ)); ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, foo_key.c_str(), @@ -144,7 +144,7 @@ TEST_F(RegistryTest, BigValueIteratorTest) { ASSERT_TRUE(key.Valid()); // Create a test value that is larger than MAX_PATH. - string16 data(MAX_PATH * 2, 'a'); + std::wstring data(MAX_PATH * 2, 'a'); ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(data.c_str(), data.c_str())); @@ -153,23 +153,23 @@ TEST_F(RegistryTest, BigValueIteratorTest) { EXPECT_EQ(data, iterator.Name()); EXPECT_EQ(data, iterator.Value()); // ValueSize() is in bytes, including NUL. - EXPECT_EQ((MAX_PATH * 2 + 1) * sizeof(char16), iterator.ValueSize()); + EXPECT_EQ((MAX_PATH * 2 + 1) * sizeof(wchar_t), iterator.ValueSize()); ++iterator; EXPECT_FALSE(iterator.Valid()); } TEST_F(RegistryTest, TruncatedCharTest) { RegKey key; - string16 foo_key(kRootKey); - foo_key += STRING16_LITERAL("\\Foo"); + std::wstring foo_key(kRootKey); + foo_key += L"\\Foo"; ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ)); ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ | KEY_SET_VALUE)); ASSERT_TRUE(key.Valid()); - const char16 kName[] = STRING16_LITERAL("name"); - // kData size is not a multiple of sizeof(char16). + const wchar_t kName[] = L"name"; + // kData size is not a multiple of sizeof(wchar_t). const uint8_t kData[] = {1, 2, 3, 4, 5}; EXPECT_EQ(5u, size(kData)); ASSERT_EQ(ERROR_SUCCESS, @@ -179,11 +179,11 @@ TEST_F(RegistryTest, TruncatedCharTest) { ASSERT_TRUE(iterator.Valid()); // Avoid having to use EXPECT_STREQ here by leveraging StringPiece's // operator== to perform a deep comparison. - EXPECT_EQ(StringPiece16(kName), StringPiece16(iterator.Name())); + EXPECT_EQ(WStringPiece(kName), WStringPiece(iterator.Name())); // ValueSize() is in bytes. ASSERT_EQ(size(kData), iterator.ValueSize()); // Value() is NUL terminated. - int end = (iterator.ValueSize() + sizeof(char16) - 1) / sizeof(char16); + int end = (iterator.ValueSize() + sizeof(wchar_t) - 1) / sizeof(wchar_t); EXPECT_NE('\0', iterator.Value()[end - 1]); EXPECT_EQ('\0', iterator.Value()[end]); EXPECT_EQ(0, std::memcmp(kData, iterator.Value(), size(kData))); @@ -201,50 +201,47 @@ TEST_F(RegistryTest, RecursiveDelete) { // \->Moo // \->Foo // and delete kRootKey->Foo - string16 foo_key(kRootKey); - foo_key += STRING16_LITERAL("\\Foo"); + std::wstring foo_key(kRootKey); + foo_key += L"\\Foo"; ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE)); - ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(STRING16_LITERAL("Bar"), KEY_WRITE)); - ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(STRING16_LITERAL("TestValue"), - STRING16_LITERAL("TestData"))); + ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Bar", KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(L"TestValue", L"TestData")); ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE)); - ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(STRING16_LITERAL("Moo"), KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Moo", KEY_WRITE)); ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE)); - ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(STRING16_LITERAL("Foo"), KEY_WRITE)); - foo_key += STRING16_LITERAL("\\Bar"); + ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Foo", KEY_WRITE)); + foo_key += L"\\Bar"; ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE)); - foo_key += STRING16_LITERAL("\\Foo"); - ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(STRING16_LITERAL("Foo"), KEY_WRITE)); - ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(STRING16_LITERAL("TestValue"), - STRING16_LITERAL("TestData"))); + foo_key += L"\\Foo"; + ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Foo", KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(L"TestValue", L"TestData")); ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ)); ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_WRITE)); - ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(STRING16_LITERAL("Bar"))); - ASSERT_NE(ERROR_SUCCESS, key.DeleteEmptyKey(STRING16_LITERAL("Foo"))); - ASSERT_NE(ERROR_SUCCESS, - key.DeleteEmptyKey(STRING16_LITERAL("Foo\\Bar\\Foo"))); - ASSERT_NE(ERROR_SUCCESS, key.DeleteEmptyKey(STRING16_LITERAL("Foo\\Bar"))); - ASSERT_EQ(ERROR_SUCCESS, key.DeleteEmptyKey(STRING16_LITERAL("Foo\\Foo"))); + ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(L"Bar")); + ASSERT_NE(ERROR_SUCCESS, key.DeleteEmptyKey(L"Foo")); + ASSERT_NE(ERROR_SUCCESS, key.DeleteEmptyKey(L"Foo\\Bar\\Foo")); + ASSERT_NE(ERROR_SUCCESS, key.DeleteEmptyKey(L"Foo\\Bar")); + ASSERT_EQ(ERROR_SUCCESS, key.DeleteEmptyKey(L"Foo\\Foo")); ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE)); - ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(STRING16_LITERAL("Bar"), KEY_WRITE)); - ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(STRING16_LITERAL("Foo"), KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Bar", KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Foo", KEY_WRITE)); ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE)); - ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(STRING16_LITERAL(""))); + ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(L"")); ASSERT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ)); ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_WRITE)); - ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(STRING16_LITERAL("Foo"))); - ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(STRING16_LITERAL("Foo"))); + ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(L"Foo")); + ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(L"Foo")); ASSERT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ)); } @@ -273,18 +270,15 @@ TEST_F(RegistryTest, DISABLED_Wow64RedirectedFromNative) { // Open the non-redirected view of the parent and try to delete the test key. ASSERT_EQ(ERROR_SUCCESS, - key.Open(HKEY_LOCAL_MACHINE, STRING16_LITERAL("Software"), - KEY_SET_VALUE)); + key.Open(HKEY_LOCAL_MACHINE, L"Software", KEY_SET_VALUE)); ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(kRootKey)); - ASSERT_EQ(ERROR_SUCCESS, - key.Open(HKEY_LOCAL_MACHINE, STRING16_LITERAL("Software"), - KEY_SET_VALUE | kNativeViewMask)); + ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE, L"Software", + KEY_SET_VALUE | kNativeViewMask)); ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(kRootKey)); // Open the redirected view and delete the key created above. - ASSERT_EQ(ERROR_SUCCESS, - key.Open(HKEY_LOCAL_MACHINE, STRING16_LITERAL("Software"), - KEY_SET_VALUE | kRedirectedViewMask)); + ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE, L"Software", + KEY_SET_VALUE | kRedirectedViewMask)); ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(kRootKey)); } @@ -294,13 +288,11 @@ TEST_F(RegistryTest, DISABLED_Wow64RedirectedFromNative) { TEST_F(RegistryTest, SameWowFlags) { RegKey key; + ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE, L"Software", + KEY_READ | KEY_WOW64_64KEY)); ASSERT_EQ(ERROR_SUCCESS, - key.Open(HKEY_LOCAL_MACHINE, STRING16_LITERAL("Software"), - KEY_READ | KEY_WOW64_64KEY)); - ASSERT_EQ(ERROR_SUCCESS, key.OpenKey(STRING16_LITERAL("Microsoft"), - KEY_READ | KEY_WOW64_64KEY)); - ASSERT_EQ(ERROR_SUCCESS, key.OpenKey(STRING16_LITERAL("Windows"), - KEY_READ | KEY_WOW64_64KEY)); + key.OpenKey(L"Microsoft", KEY_READ | KEY_WOW64_64KEY)); + ASSERT_EQ(ERROR_SUCCESS, key.OpenKey(L"Windows", KEY_READ | KEY_WOW64_64KEY)); } // TODO(wfh): flaky test on Vista. See http://crbug.com/377917 @@ -323,14 +315,12 @@ TEST_F(RegistryTest, DISABLED_Wow64NativeFromRedirected) { // Open the redirected view of the parent and try to delete the test key // from the non-redirected view. - ASSERT_EQ(ERROR_SUCCESS, - key.Open(HKEY_LOCAL_MACHINE, STRING16_LITERAL("Software"), - KEY_SET_VALUE | kRedirectedViewMask)); + ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE, L"Software", + KEY_SET_VALUE | kRedirectedViewMask)); ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(kRootKey)); - ASSERT_EQ(ERROR_SUCCESS, - key.Open(HKEY_LOCAL_MACHINE, STRING16_LITERAL("Software"), - KEY_SET_VALUE | kNativeViewMask)); + ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE, L"Software", + KEY_SET_VALUE | kNativeViewMask)); ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(kRootKey)); } @@ -341,18 +331,18 @@ TEST_F(RegistryTest, OpenSubKey) { kRootKey, KEY_READ | KEY_CREATE_SUB_KEY)); - ASSERT_NE(ERROR_SUCCESS, key.OpenKey(STRING16_LITERAL("foo"), KEY_READ)); - ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(STRING16_LITERAL("foo"), KEY_READ)); + ASSERT_NE(ERROR_SUCCESS, key.OpenKey(L"foo", KEY_READ)); + ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"foo", KEY_READ)); ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_READ)); - ASSERT_EQ(ERROR_SUCCESS, key.OpenKey(STRING16_LITERAL("foo"), KEY_READ)); + ASSERT_EQ(ERROR_SUCCESS, key.OpenKey(L"foo", KEY_READ)); - string16 foo_key(kRootKey); - foo_key += STRING16_LITERAL("\\Foo"); + std::wstring foo_key(kRootKey); + foo_key += L"\\Foo"; ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ)); ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_WRITE)); - ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(STRING16_LITERAL("foo"))); + ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(L"foo")); } class TestChangeDelegate { @@ -380,8 +370,8 @@ TEST_F(RegistryTest, ChangeCallback) { TestChangeDelegate delegate; test::TaskEnvironment task_environment; - string16 foo_key(kRootKey); - foo_key += STRING16_LITERAL("\\Foo"); + std::wstring foo_key(kRootKey); + foo_key += L"\\Foo"; ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ)); @@ -394,8 +384,7 @@ TEST_F(RegistryTest, ChangeCallback) { ASSERT_EQ(ERROR_SUCCESS, key2.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ | KEY_SET_VALUE)); ASSERT_TRUE(key2.Valid()); - EXPECT_EQ(ERROR_SUCCESS, key2.WriteValue(STRING16_LITERAL("name"), - STRING16_LITERAL("data"))); + EXPECT_EQ(ERROR_SUCCESS, key2.WriteValue(L"name", L"data")); // Allow delivery of the notification. EXPECT_FALSE(delegate.WasCalled()); @@ -408,8 +397,7 @@ TEST_F(RegistryTest, ChangeCallback) { BindRepeating(&TestChangeDelegate::OnKeyChanged, Unretained(&delegate)))); // Change something else. - EXPECT_EQ(ERROR_SUCCESS, key2.WriteValue(STRING16_LITERAL("name2"), - STRING16_LITERAL("data2"))); + EXPECT_EQ(ERROR_SUCCESS, key2.WriteValue(L"name2", L"data2")); RunLoop().Run(); ASSERT_TRUE(delegate.WasCalled()); diff --git a/chromium/base/win/scoped_bstr.cc b/chromium/base/win/scoped_bstr.cc index dc68cbac013..94d7d084c26 100644 --- a/chromium/base/win/scoped_bstr.cc +++ b/chromium/base/win/scoped_bstr.cc @@ -16,8 +16,8 @@ namespace win { namespace { -BSTR AllocBstrOrDie(StringPiece16 non_bstr) { - BSTR result = ::SysAllocStringLen(as_wcstr(non_bstr), +BSTR AllocBstrOrDie(WStringPiece non_bstr) { + BSTR result = ::SysAllocStringLen(non_bstr.data(), checked_cast<UINT>(non_bstr.length())); if (!result) { base::TerminateBecauseOutOfMemory((non_bstr.length() + 1) * @@ -35,7 +35,7 @@ BSTR AllocBstrBytesOrDie(size_t bytes) { } // namespace -ScopedBstr::ScopedBstr(StringPiece16 non_bstr) +ScopedBstr::ScopedBstr(WStringPiece non_bstr) : bstr_(AllocBstrOrDie(non_bstr)) {} ScopedBstr::~ScopedBstr() { @@ -68,7 +68,7 @@ BSTR* ScopedBstr::Receive() { return &bstr_; } -BSTR ScopedBstr::Allocate(StringPiece16 str) { +BSTR ScopedBstr::Allocate(WStringPiece str) { Reset(AllocBstrOrDie(str)); return bstr_; } diff --git a/chromium/base/win/scoped_bstr.h b/chromium/base/win/scoped_bstr.h index 658e815dfa9..a3d3684d986 100644 --- a/chromium/base/win/scoped_bstr.h +++ b/chromium/base/win/scoped_bstr.h @@ -12,7 +12,6 @@ #include "base/base_export.h" #include "base/logging.h" #include "base/macros.h" -#include "base/strings/string16.h" #include "base/strings/string_piece.h" namespace base { @@ -28,7 +27,7 @@ class BASE_EXPORT ScopedBstr { // // NOTE: Do not pass a BSTR to this constructor expecting ownership to // be transferred - even though it compiles! ;-) - explicit ScopedBstr(StringPiece16 non_bstr); + explicit ScopedBstr(WStringPiece non_bstr); ~ScopedBstr(); // Give ScopedBstr ownership over an already allocated BSTR or null. @@ -44,7 +43,7 @@ class BASE_EXPORT ScopedBstr { // ScopedBstr instance, call |reset| instead. // // Returns a pointer to the new BSTR. - BSTR Allocate(StringPiece16 str); + BSTR Allocate(WStringPiece str); // Allocates a new BSTR with the specified number of bytes. // Returns a pointer to the new BSTR. diff --git a/chromium/base/win/scoped_bstr_unittest.cc b/chromium/base/win/scoped_bstr_unittest.cc index 1be543e1408..66cb0bd5845 100644 --- a/chromium/base/win/scoped_bstr_unittest.cc +++ b/chromium/base/win/scoped_bstr_unittest.cc @@ -7,8 +7,6 @@ #include <stddef.h> #include "base/stl_util.h" -#include "base/strings/string16.h" -#include "base/strings/string_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { @@ -16,8 +14,8 @@ namespace win { namespace { -static const char16 kTestString1[] = STRING16_LITERAL("123"); -static const char16 kTestString2[] = STRING16_LITERAL("456789"); +static const wchar_t kTestString1[] = L"123"; +static const wchar_t kTestString2[] = L"456789"; size_t test1_len = size(kTestString1) - 1; size_t test2_len = size(kTestString2) - 1; @@ -35,7 +33,7 @@ void DumbBstrTests() { } void GiveMeABstr(BSTR* ret) { - *ret = SysAllocString(as_wcstr(kTestString1)); + *ret = SysAllocString(kTestString1); } void BasicBstrTests() { @@ -47,10 +45,10 @@ void BasicBstrTests() { b1.Swap(b2); EXPECT_EQ(test1_len, b2.Length()); EXPECT_EQ(0u, b1.Length()); - EXPECT_STREQ(b2, as_wcstr(kTestString1)); + EXPECT_STREQ(b2, kTestString1); BSTR tmp = b2.Release(); EXPECT_TRUE(tmp != NULL); - EXPECT_STREQ(tmp, as_wcstr(kTestString1)); + EXPECT_STREQ(tmp, kTestString1); EXPECT_TRUE(b2 == NULL); SysFreeString(tmp); @@ -60,7 +58,7 @@ void BasicBstrTests() { EXPECT_TRUE(b2.AllocateBytes(100) != NULL); EXPECT_EQ(100u, b2.ByteLength()); EXPECT_EQ(100 / sizeof(kTestString1[0]), b2.Length()); - lstrcpy(static_cast<BSTR>(b2), as_wcstr(kTestString1)); + lstrcpy(static_cast<BSTR>(b2), kTestString1); EXPECT_EQ(test1_len, static_cast<size_t>(lstrlen(b2))); EXPECT_EQ(100 / sizeof(kTestString1[0]), b2.Length()); b2.SetByteLen(lstrlen(b2) * sizeof(kTestString2[0])); diff --git a/chromium/base/win/shortcut.cc b/chromium/base/win/shortcut.cc index 289f5e6ec14..5d9f102778b 100644 --- a/chromium/base/win/shortcut.cc +++ b/chromium/base/win/shortcut.cc @@ -115,7 +115,7 @@ bool CreateOrUpdateShortcutLink(const FilePath& shortcut_path, } if (properties.options & ShortcutProperties::PROPERTIES_ARGUMENTS) { - if (FAILED(i_shell_link->SetArguments(as_wcstr(properties.arguments)))) + if (FAILED(i_shell_link->SetArguments(properties.arguments.c_str()))) return false; } else if (old_i_persist_file.Get()) { wchar_t current_arguments[MAX_PATH] = {0}; @@ -126,7 +126,7 @@ bool CreateOrUpdateShortcutLink(const FilePath& shortcut_path, } if ((properties.options & ShortcutProperties::PROPERTIES_DESCRIPTION) && - FAILED(i_shell_link->SetDescription(as_wcstr(properties.description)))) { + FAILED(i_shell_link->SetDescription(properties.description.c_str()))) { return false; } @@ -225,42 +225,39 @@ bool ResolveShortcutProperties(const FilePath& shortcut_path, // Reset |properties|. properties->options = 0; - char16 temp[MAX_PATH]; + wchar_t temp[MAX_PATH]; if (options & ShortcutProperties::PROPERTIES_TARGET) { - if (FAILED(i_shell_link->GetPath(as_writable_wcstr(temp), MAX_PATH, NULL, - SLGP_UNCPRIORITY))) { + if (FAILED(i_shell_link->GetPath(temp, MAX_PATH, NULL, SLGP_UNCPRIORITY))) { return false; } - properties->set_target(FilePath(temp)); + properties->set_target(FilePath(AsStringPiece16(temp))); } if (options & ShortcutProperties::PROPERTIES_WORKING_DIR) { - if (FAILED(i_shell_link->GetWorkingDirectory(as_writable_wcstr(temp), - MAX_PATH))) + if (FAILED(i_shell_link->GetWorkingDirectory(temp, MAX_PATH))) return false; - properties->set_working_dir(FilePath(temp)); + properties->set_working_dir(FilePath(AsStringPiece16(temp))); } if (options & ShortcutProperties::PROPERTIES_ARGUMENTS) { - if (FAILED(i_shell_link->GetArguments(as_writable_wcstr(temp), MAX_PATH))) + if (FAILED(i_shell_link->GetArguments(temp, MAX_PATH))) return false; properties->set_arguments(temp); } if (options & ShortcutProperties::PROPERTIES_DESCRIPTION) { // Note: description length constrained by MAX_PATH. - if (FAILED(i_shell_link->GetDescription(as_writable_wcstr(temp), MAX_PATH))) + if (FAILED(i_shell_link->GetDescription(temp, MAX_PATH))) return false; properties->set_description(temp); } if (options & ShortcutProperties::PROPERTIES_ICON) { int temp_index; - if (FAILED(i_shell_link->GetIconLocation(as_writable_wcstr(temp), MAX_PATH, - &temp_index))) { + if (FAILED(i_shell_link->GetIconLocation(temp, MAX_PATH, &temp_index))) { return false; } - properties->set_icon(FilePath(temp), temp_index); + properties->set_icon(FilePath(AsStringPiece16(temp)), temp_index); } if (options & (ShortcutProperties::PROPERTIES_APP_ID | @@ -278,10 +275,10 @@ bool ResolveShortcutProperties(const FilePath& shortcut_path, } switch (pv_app_id.get().vt) { case VT_EMPTY: - properties->set_app_id(string16()); + properties->set_app_id(std::wstring()); break; case VT_LPWSTR: - properties->set_app_id(WideToUTF16(pv_app_id.get().pwszVal)); + properties->set_app_id(pv_app_id.get().pwszVal); break; default: NOTREACHED() << "Unexpected variant type: " << pv_app_id.get().vt; @@ -336,7 +333,7 @@ bool ResolveShortcutProperties(const FilePath& shortcut_path, bool ResolveShortcut(const FilePath& shortcut_path, FilePath* target_path, - string16* args) { + std::wstring* args) { uint32_t options = 0; if (target_path) options |= ShortcutProperties::PROPERTIES_TARGET; diff --git a/chromium/base/win/shortcut.h b/chromium/base/win/shortcut.h index 38c12b77f4d..9f656ad54a2 100644 --- a/chromium/base/win/shortcut.h +++ b/chromium/base/win/shortcut.h @@ -11,7 +11,6 @@ #include "base/base_export.h" #include "base/files/file_path.h" #include "base/logging.h" -#include "base/strings/string16.h" namespace base { namespace win { @@ -62,14 +61,14 @@ struct BASE_EXPORT ShortcutProperties { options |= PROPERTIES_WORKING_DIR; } - void set_arguments(const string16& arguments_in) { + void set_arguments(const std::wstring& arguments_in) { // Size restriction as per MSDN at http://goo.gl/TJ7q5. DCHECK(arguments_in.size() < MAX_PATH); arguments = arguments_in; options |= PROPERTIES_ARGUMENTS; } - void set_description(const string16& description_in) { + void set_description(const std::wstring& description_in) { // Size restriction as per MSDN at http://goo.gl/OdNQq. DCHECK(description_in.size() < MAX_PATH); description = description_in; @@ -82,7 +81,7 @@ struct BASE_EXPORT ShortcutProperties { options |= PROPERTIES_ICON; } - void set_app_id(const string16& app_id_in) { + void set_app_id(const std::wstring& app_id_in) { app_id = app_id_in; options |= PROPERTIES_APP_ID; } @@ -104,16 +103,16 @@ struct BASE_EXPORT ShortcutProperties { FilePath working_dir; // The arguments to be applied to |target| when launching from this shortcut. // The length of this string must be less than MAX_PATH. - string16 arguments; + std::wstring arguments; // The localized description of the shortcut. // The length of this string must be less than MAX_PATH. - string16 description; + std::wstring description; // The path to the icon (can be a dll or exe, in which case |icon_index| is // the resource id). FilePath icon; int icon_index; // The app model id for the shortcut. - string16 app_id; + std::wstring app_id; // Whether this is a dual mode shortcut (Win8+). bool dual_mode; // The CLSID of the COM object registered with the OS via the shortcut. This @@ -159,7 +158,7 @@ BASE_EXPORT bool ResolveShortcutProperties(const FilePath& shortcut_path, // |shortcut_path| and |target_path|. BASE_EXPORT bool ResolveShortcut(const FilePath& shortcut_path, FilePath* target_path, - string16* args); + std::wstring* args); // Pin to taskbar is only supported on Windows 7 and Windows 8. Returns true on // those platforms. diff --git a/chromium/base/win/shortcut_unittest.cc b/chromium/base/win/shortcut_unittest.cc index 992c1b118e6..76c80dcff55 100644 --- a/chromium/base/win/shortcut_unittest.cc +++ b/chromium/base/win/shortcut_unittest.cc @@ -12,7 +12,6 @@ #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/stl_util.h" -#include "base/strings/string16.h" #include "base/test/test_file_util.h" #include "base/test/test_shortcut_win.h" #include "base/win/scoped_com_initializer.h" @@ -42,10 +41,10 @@ class ShortcutTest : public testing::Test { link_properties_.set_target(target_file); link_properties_.set_working_dir(temp_dir_.GetPath()); - link_properties_.set_arguments(STRING16_LITERAL("--magic --awesome")); - link_properties_.set_description(STRING16_LITERAL("Chrome is awesome.")); + link_properties_.set_arguments(L"--magic --awesome"); + link_properties_.set_description(L"Chrome is awesome."); link_properties_.set_icon(link_properties_.target, 4); - link_properties_.set_app_id(STRING16_LITERAL("Chrome")); + link_properties_.set_app_id(L"Chrome"); link_properties_.set_dual_mode(false); // The CLSID below was randomly selected. @@ -68,12 +67,10 @@ class ShortcutTest : public testing::Test { link_properties_2_.set_target(target_file_2); link_properties_2_.set_working_dir(temp_dir_2_.GetPath()); - link_properties_2_.set_arguments(STRING16_LITERAL("--super --crazy")); - link_properties_2_.set_description( - STRING16_LITERAL("The best in the west.")); + link_properties_2_.set_arguments(L"--super --crazy"); + link_properties_2_.set_description(L"The best in the west."); link_properties_2_.set_icon(icon_path_2, 0); - link_properties_2_.set_app_id( - STRING16_LITERAL("Chrome.UserLevelCrazySuffix")); + link_properties_2_.set_app_id(L"Chrome.UserLevelCrazySuffix"); link_properties_2_.set_dual_mode(true); link_properties_2_.set_toast_activator_clsid(CLSID_NULL); } @@ -133,11 +130,11 @@ TEST_F(ShortcutTest, CreateAndResolveShortcutProperties) { ValidatePathsAreEqual(only_target_properties.target, properties_read_2.target); ValidatePathsAreEqual(FilePath(), properties_read_2.working_dir); - EXPECT_EQ(STRING16_LITERAL(""), properties_read_2.arguments); - EXPECT_EQ(STRING16_LITERAL(""), properties_read_2.description); + EXPECT_EQ(L"", properties_read_2.arguments); + EXPECT_EQ(L"", properties_read_2.description); ValidatePathsAreEqual(FilePath(), properties_read_2.icon); EXPECT_EQ(0, properties_read_2.icon_index); - EXPECT_EQ(STRING16_LITERAL(""), properties_read_2.app_id); + EXPECT_EQ(L"", properties_read_2.app_id); EXPECT_FALSE(properties_read_2.dual_mode); EXPECT_EQ(CLSID_NULL, properties_read_2.toast_activator_clsid); } @@ -162,7 +159,7 @@ TEST_F(ShortcutTest, ResolveShortcutWithArgs) { link_file_, link_properties_, SHORTCUT_CREATE_ALWAYS)); FilePath resolved_name; - string16 args; + std::wstring args; EXPECT_TRUE(ResolveShortcut(link_file_, &resolved_name, &args)); char read_contents[base::size(kFileContents)]; @@ -260,14 +257,14 @@ TEST_F(ShortcutTest, UpdateShortcutClearArguments) { link_file_, link_properties_, SHORTCUT_CREATE_ALWAYS)); ShortcutProperties clear_arguments_properties; - clear_arguments_properties.set_arguments(string16()); + clear_arguments_properties.set_arguments(std::wstring()); ASSERT_TRUE(CreateOrUpdateShortcutLink( link_file_, clear_arguments_properties, SHORTCUT_UPDATE_EXISTING)); ShortcutProperties expected_properties = link_properties_; - expected_properties.set_arguments(string16()); + expected_properties.set_arguments(std::wstring()); ValidateShortcut(link_file_, expected_properties); } @@ -303,7 +300,7 @@ TEST_F(ShortcutTest, ReplaceShortcutSomeProperties) { ShortcutProperties expected_properties(new_properties); expected_properties.set_working_dir(FilePath()); expected_properties.set_icon(FilePath(), 0); - expected_properties.set_app_id(string16()); + expected_properties.set_app_id(std::wstring()); expected_properties.set_dual_mode(false); ValidateShortcut(link_file_, expected_properties); } diff --git a/chromium/base/win/static_constants.cc b/chromium/base/win/static_constants.cc new file mode 100644 index 00000000000..f9894a22bd0 --- /dev/null +++ b/chromium/base/win/static_constants.cc @@ -0,0 +1,13 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/win/static_constants.h" + +namespace base { +namespace win { + +const char kApplicationVerifierDllName[] = "verifier.dll"; + +} // namespace win +} // namespace base diff --git a/chromium/base/win/static_constants.h b/chromium/base/win/static_constants.h new file mode 100644 index 00000000000..98a631f7cf2 --- /dev/null +++ b/chromium/base/win/static_constants.h @@ -0,0 +1,21 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Defines constants needed before imports (like base.dll) are fully resolved. +// For example, constants defined here can be used by interceptions (i.e. hooks) +// in the sandbox, which run before imports are resolved, and can therefore only +// reference static variables. + +#ifndef BASE_WIN_STATIC_CONSTANTS_H_ +#define BASE_WIN_STATIC_CONSTANTS_H_ + +namespace base { +namespace win { + +extern const char kApplicationVerifierDllName[]; + +} // namespace win +} // namespace base + +#endif // BASE_WIN_STATIC_CONSTANTS_H_ diff --git a/chromium/base/win/win_client_metrics.h b/chromium/base/win/win_client_metrics.h deleted file mode 100644 index 102148f06a9..00000000000 --- a/chromium/base/win/win_client_metrics.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file is separate from base/win/win_util.h to avoid pulling windows.h -// into too many translation units. - -#ifndef BASE_WIN_WIN_CLIENT_METRICS_H_ -#define BASE_WIN_WIN_CLIENT_METRICS_H_ - -#include <windows.h> - -// This is the same as NONCLIENTMETRICS except that the -// unused member |iPaddedBorderWidth| has been removed. -struct NONCLIENTMETRICS_XP { - UINT cbSize; - int iBorderWidth; - int iScrollWidth; - int iScrollHeight; - int iCaptionWidth; - int iCaptionHeight; - LOGFONTW lfCaptionFont; - int iSmCaptionWidth; - int iSmCaptionHeight; - LOGFONTW lfSmCaptionFont; - int iMenuWidth; - int iMenuHeight; - LOGFONTW lfMenuFont; - LOGFONTW lfStatusFont; - LOGFONTW lfMessageFont; -}; - -namespace base { -namespace win { - -BASE_EXPORT void GetNonClientMetrics(NONCLIENTMETRICS_XP* metrics); - -} // namespace win -} // namespace base - -#endif // BASE_WIN_WIN_CLIENT_METRICS_H_ diff --git a/chromium/base/win/win_util.cc b/chromium/base/win/win_util.cc index 7c87ace4dba..848e166f0da 100644 --- a/chromium/base/win/win_util.cc +++ b/chromium/base/win/win_util.cc @@ -40,6 +40,7 @@ #include "base/macros.h" #include "base/scoped_native_library.h" #include "base/strings/string_util.h" +#include "base/strings/string_util_win.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/scoped_thread_priority.h" @@ -52,7 +53,6 @@ #include "base/win/scoped_hstring.h" #include "base/win/scoped_propvariant.h" #include "base/win/shlwapi.h" -#include "base/win/win_client_metrics.h" #include "base/win/windows_version.h" namespace base { @@ -233,7 +233,7 @@ bool IsWindows10TabletMode(HWND hwnd) { // if the keyboard count is 1 or more.. While this will work in most cases // it won't work if there are devices which expose keyboard interfaces which // are attached to the machine. -bool IsKeyboardPresentOnSlate(std::string* reason, HWND hwnd) { +bool IsKeyboardPresentOnSlate(HWND hwnd, std::string* reason) { bool result = false; if (GetVersion() < Version::WIN8) { @@ -266,15 +266,14 @@ bool IsKeyboardPresentOnSlate(std::string* reason, HWND hwnd) { if (reason) *reason += "Tablet device.\n"; return false; - } else { - if (reason) { - *reason += "Not a tablet device"; - result = true; - } else { - return true; - } } + if (!reason) + return true; + + *reason += "Not a tablet device"; + result = true; + // To determine whether a keyboard is present on the device, we do the // following:- // 1. Check whether the device supports auto rotation. If it does then @@ -301,13 +300,12 @@ bool IsKeyboardPresentOnSlate(std::string* reason, HWND hwnd) { // If there is no auto rotation sensor or rotation is not supported in // the current configuration, then we can assume that this is a desktop // or a traditional laptop. - if (reason) { - *reason += (auto_rotation_state & AR_NOSENSOR) ? "AR_NOSENSOR\n" - : "AR_NOT_SUPPORTED\n"; - result = true; - } else { + if (!reason) return true; - } + + *reason += (auto_rotation_state & AR_NOSENSOR) ? "AR_NOSENSOR\n" + : "AR_NOT_SUPPORTED\n"; + result = true; } } @@ -334,20 +332,19 @@ bool IsKeyboardPresentOnSlate(std::string* reason, HWND hwnd) { break; // Get the device ID. - char16 device_id[MAX_DEVICE_ID_LEN]; - CONFIGRET status = - CM_Get_Device_ID(device_info_data.DevInst, as_writable_wcstr(device_id), - MAX_DEVICE_ID_LEN, 0); + wchar_t device_id[MAX_DEVICE_ID_LEN]; + CONFIGRET status = CM_Get_Device_ID(device_info_data.DevInst, device_id, + MAX_DEVICE_ID_LEN, 0); if (status == CR_SUCCESS) { // To reduce the scope of the hack we only look for ACPI and HID\\VID // prefixes in the keyboard device ids. - if (StartsWith(device_id, STRING16_LITERAL("ACPI"), + if (StartsWith(AsStringPiece16(device_id), STRING16_LITERAL("ACPI"), CompareCase::INSENSITIVE_ASCII) || - StartsWith(device_id, STRING16_LITERAL("HID\\VID"), + StartsWith(AsStringPiece16(device_id), STRING16_LITERAL("HID\\VID"), CompareCase::INSENSITIVE_ASCII)) { if (reason) { *reason += "device: "; - *reason += UTF16ToUTF8(device_id); + *reason += WideToUTF8(device_id); *reason += '\n'; } // The heuristic we are using is to check the count of keyboards and @@ -363,18 +360,7 @@ bool IsKeyboardPresentOnSlate(std::string* reason, HWND hwnd) { static bool g_crash_on_process_detach = false; -void GetNonClientMetrics(NONCLIENTMETRICS_XP* metrics) { - DCHECK(metrics); - metrics->cbSize = sizeof(*metrics); - const bool success = !!SystemParametersInfo( - SPI_GETNONCLIENTMETRICS, - metrics->cbSize, - reinterpret_cast<NONCLIENTMETRICS*>(metrics), - 0); - DCHECK(success); -} - -bool GetUserSidString(string16* user_sid) { +bool GetUserSidString(std::wstring* user_sid) { // Get the current token. HANDLE token = NULL; if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token)) @@ -396,7 +382,7 @@ bool GetUserSidString(string16* user_sid) { if (!::ConvertSidToStringSid(user->User.Sid, &sid_string)) return false; - *user_sid = as_u16cstr(sid_string); + *user_sid = sid_string; ::LocalFree(sid_string); @@ -409,14 +395,11 @@ bool UserAccountControlIsEnabled() { // http://code.google.com/p/chromium/issues/detail?id=61644 ThreadRestrictions::ScopedAllowIO allow_io; - RegKey key( - HKEY_LOCAL_MACHINE, - STRING16_LITERAL( - "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"), - KEY_READ); + RegKey key(HKEY_LOCAL_MACHINE, + L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", + KEY_READ); DWORD uac_enabled; - if (key.ReadValueDW(STRING16_LITERAL("EnableLUA"), &uac_enabled) != - ERROR_SUCCESS) { + if (key.ReadValueDW(L"EnableLUA", &uac_enabled) != ERROR_SUCCESS) { return true; } // Users can set the EnableLUA value to something arbitrary, like 2, which @@ -440,9 +423,9 @@ bool SetBooleanValueForPropertyStore(IPropertyStore* property_store, bool SetStringValueForPropertyStore(IPropertyStore* property_store, const PROPERTYKEY& property_key, - const char16* property_string_value) { + const wchar_t* property_string_value) { ScopedPropVariant property_value; - if (FAILED(InitPropVariantFromString(as_wcstr(property_string_value), + if (FAILED(InitPropVariantFromString(property_string_value, property_value.Receive()))) { return false; } @@ -466,36 +449,37 @@ bool SetClsidForPropertyStore(IPropertyStore* property_store, } bool SetAppIdForPropertyStore(IPropertyStore* property_store, - const char16* app_id) { + const wchar_t* app_id) { // App id should be less than 64 chars and contain no space. And recommended // format is CompanyName.ProductName[.SubProduct.ProductNumber]. // See http://msdn.microsoft.com/en-us/library/dd378459%28VS.85%29.aspx - DCHECK_LT(lstrlen(as_wcstr(app_id)), 64); - DCHECK_EQ(wcschr(as_wcstr(app_id), L' '), nullptr); + DCHECK_LT(lstrlen(app_id), 64); + DCHECK_EQ(wcschr(app_id, L' '), nullptr); return SetStringValueForPropertyStore(property_store, PKEY_AppUserModel_ID, app_id); } -static const char16 kAutoRunKeyPath[] = - STRING16_LITERAL("Software\\Microsoft\\Windows\\CurrentVersion\\Run"); +static const wchar_t kAutoRunKeyPath[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Run"; -bool AddCommandToAutoRun(HKEY root_key, const string16& name, - const string16& command) { +bool AddCommandToAutoRun(HKEY root_key, + const std::wstring& name, + const std::wstring& command) { RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_SET_VALUE); return (autorun_key.WriteValue(name.c_str(), command.c_str()) == ERROR_SUCCESS); } -bool RemoveCommandFromAutoRun(HKEY root_key, const string16& name) { +bool RemoveCommandFromAutoRun(HKEY root_key, const std::wstring& name) { RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_SET_VALUE); return (autorun_key.DeleteValue(name.c_str()) == ERROR_SUCCESS); } bool ReadCommandFromAutoRun(HKEY root_key, - const string16& name, - string16* command) { + const std::wstring& name, + std::wstring* command) { RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_QUERY_VALUE); return (autorun_key.ReadValue(name.c_str(), command) == ERROR_SUCCESS); } @@ -766,9 +750,9 @@ void* GetUser32FunctionPointer(const char* function_name, return nullptr; } -string16 GetWindowObjectName(HANDLE handle) { +std::wstring GetWindowObjectName(HANDLE handle) { // Get the size of the name. - string16 object_name; + std::wstring object_name; DWORD size = 0; ::GetUserObjectInformation(handle, UOI_NAME, nullptr, 0, &size); @@ -781,7 +765,7 @@ string16 GetWindowObjectName(HANDLE handle) { // Query the name of the object. if (!::GetUserObjectInformation( - handle, UOI_NAME, WriteInto(&object_name, size / sizeof(wchar_t)), + handle, UOI_NAME, WriteIntoW(&object_name, size / sizeof(wchar_t)), size, &size)) { DPCHECK(false); } @@ -789,13 +773,14 @@ string16 GetWindowObjectName(HANDLE handle) { return object_name; } -bool IsRunningUnderDesktopName(StringPiece16 desktop_name) { +bool IsRunningUnderDesktopName(WStringPiece desktop_name) { HDESK thread_desktop = ::GetThreadDesktop(::GetCurrentThreadId()); if (!thread_desktop) return false; - string16 current_desktop_name = GetWindowObjectName(thread_desktop); - return EqualsCaseInsensitiveASCII(current_desktop_name, desktop_name); + std::wstring current_desktop_name = GetWindowObjectName(thread_desktop); + return EqualsCaseInsensitiveASCII(AsStringPiece16(current_desktop_name), + AsStringPiece16(desktop_name)); } // This method is used to detect whether current session is a remote session. @@ -810,14 +795,13 @@ bool IsCurrentSessionRemote() { if (!::ProcessIdToSessionId(::GetCurrentProcessId(), ¤t_session_id)) return false; - static constexpr char16 kRdpSettingsKeyName[] = - STRING16_LITERAL("SYSTEM\\CurrentControlSet\\Control\\Terminal Server"); + static constexpr wchar_t kRdpSettingsKeyName[] = + L"SYSTEM\\CurrentControlSet\\Control\\Terminal Server"; RegKey key(HKEY_LOCAL_MACHINE, kRdpSettingsKeyName, KEY_READ); if (!key.Valid()) return false; - static constexpr char16 kGlassSessionIdValueName[] = - STRING16_LITERAL("GlassSessionId"); + static constexpr wchar_t kGlassSessionIdValueName[] = L"GlassSessionId"; DWORD glass_session_id = 0; if (key.ReadValueDW(kGlassSessionIdValueName, &glass_session_id) != ERROR_SUCCESS) diff --git a/chromium/base/win/win_util.h b/chromium/base/win/win_util.h index 921b5c102ea..c0848b62f9e 100644 --- a/chromium/base/win/win_util.h +++ b/chromium/base/win/win_util.h @@ -58,7 +58,7 @@ inline HANDLE Uint32ToHandle(uint32_t h) { // Returns the string representing the current user sid. Does not modify // |user_sid| on failure. -BASE_EXPORT bool GetUserSidString(string16* user_sid); +BASE_EXPORT bool GetUserSidString(std::wstring* user_sid); // Returns false if user account control (UAC) has been disabled with the // EnableLUA registry flag. Returns true if user account control is enabled. @@ -78,7 +78,7 @@ BASE_EXPORT bool SetBooleanValueForPropertyStore( BASE_EXPORT bool SetStringValueForPropertyStore( IPropertyStore* property_store, const PROPERTYKEY& property_key, - const char16* property_string_value); + const wchar_t* property_string_value); // Sets the CLSID value for a given key in a given IPropertyStore. BASE_EXPORT bool SetClsidForPropertyStore(IPropertyStore* property_store, @@ -89,22 +89,23 @@ BASE_EXPORT bool SetClsidForPropertyStore(IPropertyStore* property_store, // for tagging application/chromium shortcut, browser window and jump list for // Win7. BASE_EXPORT bool SetAppIdForPropertyStore(IPropertyStore* property_store, - const char16* app_id); + const wchar_t* app_id); // Adds the specified |command| using the specified |name| to the AutoRun key. // |root_key| could be HKCU or HKLM or the root of any user hive. BASE_EXPORT bool AddCommandToAutoRun(HKEY root_key, - const string16& name, - const string16& command); + const std::wstring& name, + const std::wstring& command); // Removes the command specified by |name| from the AutoRun key. |root_key| // could be HKCU or HKLM or the root of any user hive. -BASE_EXPORT bool RemoveCommandFromAutoRun(HKEY root_key, const string16& name); +BASE_EXPORT bool RemoveCommandFromAutoRun(HKEY root_key, + const std::wstring& name); // Reads the command specified by |name| from the AutoRun key. |root_key| // could be HKCU or HKLM or the root of any user hive. Used for unit-tests. BASE_EXPORT bool ReadCommandFromAutoRun(HKEY root_key, - const string16& name, - string16* command); + const std::wstring& name, + std::wstring* command); // Sets whether to crash the process during exit. This is inspected by DLLMain // and used to intercept unexpected terminations of the process (via calls to @@ -148,7 +149,7 @@ BASE_EXPORT bool IsDeviceUsedAsATablet(std::string* reason); // A slate is a touch device that may have a keyboard attached. This function // returns true if a keyboard is attached and optionally will set the |reason| // parameter to the detection method that was used to detect the keyboard. -BASE_EXPORT bool IsKeyboardPresentOnSlate(std::string* reason, HWND hwnd); +BASE_EXPORT bool IsKeyboardPresentOnSlate(HWND hwnd, std::string* reason); // Get the size of a struct up to and including the specified member. // This is necessary to set compatible struct sizes for different versions @@ -216,12 +217,12 @@ BASE_EXPORT void* GetUser32FunctionPointer( NativeLibraryLoadError* error = nullptr); // Returns the name of a desktop or a window station. -BASE_EXPORT string16 GetWindowObjectName(HANDLE handle); +BASE_EXPORT std::wstring GetWindowObjectName(HANDLE handle); // Checks if the calling thread is running under a desktop with the name // given by |desktop_name|. |desktop_name| is ASCII case insensitive (non-ASCII // characters will be compared with exact matches). -BASE_EXPORT bool IsRunningUnderDesktopName(StringPiece16 desktop_name); +BASE_EXPORT bool IsRunningUnderDesktopName(WStringPiece desktop_name); // Returns true if current session is a remote session. BASE_EXPORT bool IsCurrentSessionRemote(); diff --git a/chromium/base/win/win_util_unittest.cc b/chromium/base/win/win_util_unittest.cc index 1b161aa2bc6..db741f5e2ed 100644 --- a/chromium/base/win/win_util_unittest.cc +++ b/chromium/base/win/win_util_unittest.cc @@ -13,7 +13,6 @@ #include "base/strings/string16.h" #include "base/strings/string_util.h" #include "base/win/scoped_co_mem.h" -#include "base/win/win_client_metrics.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { @@ -43,19 +42,11 @@ TEST(BaseWinUtilTest, TestIsUACEnabled) { } TEST(BaseWinUtilTest, TestGetUserSidString) { - string16 user_sid; + std::wstring user_sid; EXPECT_TRUE(GetUserSidString(&user_sid)); EXPECT_TRUE(!user_sid.empty()); } -TEST(BaseWinUtilTest, TestGetNonClientMetrics) { - NONCLIENTMETRICS_XP metrics = {0}; - GetNonClientMetrics(&metrics); - EXPECT_GT(metrics.cbSize, 0u); - EXPECT_GT(metrics.iScrollWidth, 0); - EXPECT_GT(metrics.iScrollHeight, 0); -} - TEST(BaseWinUtilTest, TestGetLoadedModulesSnapshot) { std::vector<HMODULE> snapshot; @@ -101,9 +92,9 @@ TEST(BaseWinUtilTest, String16FromGUID) { } TEST(BaseWinUtilTest, GetWindowObjectName) { - base::string16 created_desktop_name(STRING16_LITERAL("test_desktop")); + std::wstring created_desktop_name(L"test_desktop"); HDESK desktop_handle = - ::CreateDesktop(as_wcstr(created_desktop_name), nullptr, nullptr, 0, + ::CreateDesktop(created_desktop_name.c_str(), nullptr, nullptr, 0, DESKTOP_CREATEWINDOW | DESKTOP_READOBJECTS | READ_CONTROL | WRITE_DAC | WRITE_OWNER, nullptr); @@ -117,13 +108,15 @@ TEST(BaseWinUtilTest, IsRunningUnderDesktopName) { HDESK thread_desktop = ::GetThreadDesktop(::GetCurrentThreadId()); ASSERT_NE(thread_desktop, nullptr); - base::string16 desktop_name = GetWindowObjectName(thread_desktop); + std::wstring desktop_name = GetWindowObjectName(thread_desktop); EXPECT_TRUE(IsRunningUnderDesktopName(desktop_name)); - EXPECT_TRUE(IsRunningUnderDesktopName(base::ToLowerASCII(desktop_name))); - EXPECT_TRUE(IsRunningUnderDesktopName(base::ToUpperASCII(desktop_name))); - EXPECT_FALSE(IsRunningUnderDesktopName( - desktop_name + STRING16_LITERAL("_non_existent_desktop_name"))); + EXPECT_TRUE(IsRunningUnderDesktopName( + AsWString(ToLowerASCII(AsStringPiece16(desktop_name))))); + EXPECT_TRUE(IsRunningUnderDesktopName( + AsWString(ToUpperASCII(AsStringPiece16(desktop_name))))); + EXPECT_FALSE( + IsRunningUnderDesktopName(desktop_name + L"_non_existent_desktop_name")); } } // namespace win diff --git a/chromium/base/win/windows_types.h b/chromium/base/win/windows_types.h index a57277a7c6a..60c4ffe8c9a 100644 --- a/chromium/base/win/windows_types.h +++ b/chromium/base/win/windows_types.h @@ -244,6 +244,8 @@ WINBASEAPI VOID WINAPI SetLastError(_In_ DWORD dwErrCode); #define DeleteFile DeleteFileW #define DispatchMessage DispatchMessageW #define DrawText DrawTextW +#define FindFirstFile FindFirstFileW +#define FindNextFile FindNextFileW #define GetComputerName GetComputerNameW #define GetCurrentDirectory GetCurrentDirectoryW #define GetCurrentTime() GetTickCount() @@ -253,6 +255,7 @@ WINBASEAPI VOID WINAPI SetLastError(_In_ DWORD dwErrCode); #define LoadIcon LoadIconW #define LoadImage LoadImageW #define PostMessage PostMessageW +#define RemoveDirectory RemoveDirectoryW #define ReplaceFile ReplaceFileW #define ReportEvent ReportEventW #define SendMessage SendMessageW diff --git a/chromium/base/win/windows_version.cc b/chromium/base/win/windows_version.cc index 1d3721944f4..476744b41fe 100644 --- a/chromium/base/win/windows_version.cc +++ b/chromium/base/win/windows_version.cc @@ -14,7 +14,6 @@ #include "base/files/file_path.h" #include "base/logging.h" #include "base/no_destructor.h" -#include "base/strings/string16.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/win/registry.h" @@ -34,8 +33,8 @@ namespace { // The values under the CurrentVersion registry hive are mirrored under // the corresponding Wow6432 hive. -constexpr char16 kRegKeyWindowsNTCurrentVersion[] = - STRING16_LITERAL("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"); +constexpr wchar_t kRegKeyWindowsNTCurrentVersion[] = + L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"; // Returns the "UBR" (Windows 10 patch number) and "ReleaseId" (Windows 10 // release number) from the registry. "UBR" is an undocumented value and will be @@ -43,16 +42,16 @@ constexpr char16 kRegKeyWindowsNTCurrentVersion[] = // value is not found. std::pair<int, std::string> GetVersionData() { DWORD ubr = 0; - string16 release_id; + std::wstring release_id; RegKey key; if (key.Open(HKEY_LOCAL_MACHINE, kRegKeyWindowsNTCurrentVersion, KEY_QUERY_VALUE) == ERROR_SUCCESS) { - key.ReadValueDW(STRING16_LITERAL("UBR"), &ubr); - key.ReadValue(STRING16_LITERAL("ReleaseId"), &release_id); + key.ReadValueDW(L"UBR", &ubr); + key.ReadValue(L"ReleaseId", &release_id); } - return std::make_pair(static_cast<int>(ubr), UTF16ToUTF8(release_id)); + return std::make_pair(static_cast<int>(ubr), WideToUTF8(release_id)); } const _SYSTEM_INFO& GetSystemInfoStorage() { @@ -215,27 +214,19 @@ base::Version OSInfo::Kernel32BaseVersion() const { FilePath(FILE_PATH_LITERAL("kernelbase.dll"))); } CHECK(file_version_info); - const int major = - HIWORD(file_version_info->fixed_file_info()->dwFileVersionMS); - const int minor = - LOWORD(file_version_info->fixed_file_info()->dwFileVersionMS); - const int build = - HIWORD(file_version_info->fixed_file_info()->dwFileVersionLS); - const int patch = - LOWORD(file_version_info->fixed_file_info()->dwFileVersionLS); - return base::Version(std::vector<uint32_t>{major, minor, build, patch}); + return file_version_info->GetFileVersion(); }()); return *version; } std::string OSInfo::processor_model_name() { if (processor_model_name_.empty()) { - const char16 kProcessorNameString[] = - STRING16_LITERAL("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"); + const wchar_t kProcessorNameString[] = + L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"; RegKey key(HKEY_LOCAL_MACHINE, kProcessorNameString, KEY_READ); - string16 value; - key.ReadValue(STRING16_LITERAL("ProcessorNameString"), &value); - processor_model_name_ = UTF16ToUTF8(value); + std::wstring value; + key.ReadValue(L"ProcessorNameString", &value); + processor_model_name_ = WideToUTF8(value); } return processor_model_name_; } diff --git a/chromium/base/win/wmi.cc b/chromium/base/win/wmi.cc index 4d5e094c3c4..c7da8a3c869 100644 --- a/chromium/base/win/wmi.cc +++ b/chromium/base/win/wmi.cc @@ -36,9 +36,9 @@ bool CreateLocalWmiConnection(bool set_blanket, return false; ComPtr<IWbemServices> wmi_services_r; - hr = wmi_locator->ConnectServer(ScopedBstr(STRING16_LITERAL("ROOT\\CIMV2")), - nullptr, nullptr, nullptr, 0, nullptr, - nullptr, &wmi_services_r); + hr = + wmi_locator->ConnectServer(ScopedBstr(L"ROOT\\CIMV2"), nullptr, nullptr, + nullptr, 0, nullptr, nullptr, &wmi_services_r); if (FAILED(hr)) return false; @@ -55,8 +55,8 @@ bool CreateLocalWmiConnection(bool set_blanket, } bool CreateWmiClassMethodObject(IWbemServices* wmi_services, - StringPiece16 class_name, - StringPiece16 method_name, + WStringPiece class_name, + WStringPiece method_name, ComPtr<IWbemClassObject>* class_instance) { // We attempt to instantiate a COM object that represents a WMI object plus // a method rolled into one entity. @@ -90,20 +90,20 @@ bool CreateWmiClassMethodObject(IWbemServices* wmi_services, // NOTE: The documentation for the Create method suggests that the ProcessId // parameter and return value are of type uint32_t, but when we call the method // the values in the returned out_params, are VT_I4, which is int32_t. -bool WmiLaunchProcess(const string16& command_line, int* process_id) { +bool WmiLaunchProcess(const std::wstring& command_line, int* process_id) { ComPtr<IWbemServices> wmi_local; if (!CreateLocalWmiConnection(true, &wmi_local)) return false; - static constexpr char16 class_name[] = STRING16_LITERAL("Win32_Process"); - static constexpr char16 method_name[] = STRING16_LITERAL("Create"); + static constexpr wchar_t class_name[] = L"Win32_Process"; + static constexpr wchar_t method_name[] = L"Create"; ComPtr<IWbemClassObject> process_create; if (!CreateWmiClassMethodObject(wmi_local.Get(), class_name, method_name, &process_create)) { return false; } - ScopedVariant b_command_line(as_wcstr(command_line)); + ScopedVariant b_command_line(command_line.c_str()); if (FAILED(process_create->Put(L"CommandLine", 0, b_command_line.AsInput(), 0))) { @@ -151,14 +151,14 @@ WmiComputerSystemInfo WmiComputerSystemInfo::Get() { void WmiComputerSystemInfo::PopulateModelAndManufacturer( const ComPtr<IWbemServices>& services) { - static constexpr StringPiece16 query_computer_system = - STRING16_LITERAL("SELECT Manufacturer,Model FROM Win32_ComputerSystem"); + static constexpr WStringPiece query_computer_system = + L"SELECT Manufacturer,Model FROM Win32_ComputerSystem"; ComPtr<IEnumWbemClassObject> enumerator_computer_system; - HRESULT hr = services->ExecQuery( - ScopedBstr(STRING16_LITERAL("WQL")), ScopedBstr(query_computer_system), - WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, nullptr, - &enumerator_computer_system); + HRESULT hr = + services->ExecQuery(ScopedBstr(L"WQL"), ScopedBstr(query_computer_system), + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + nullptr, &enumerator_computer_system); if (FAILED(hr) || !enumerator_computer_system.Get()) return; @@ -172,27 +172,26 @@ void WmiComputerSystemInfo::PopulateModelAndManufacturer( ScopedVariant manufacturer; hr = class_object->Get(L"Manufacturer", 0, manufacturer.Receive(), 0, 0); if (SUCCEEDED(hr) && manufacturer.type() == VT_BSTR) { - WideToUTF16(V_BSTR(manufacturer.ptr()), - ::SysStringLen(V_BSTR(manufacturer.ptr())), &manufacturer_); + manufacturer_.assign(V_BSTR(manufacturer.ptr()), + ::SysStringLen(V_BSTR(manufacturer.ptr()))); } ScopedVariant model; hr = class_object->Get(L"Model", 0, model.Receive(), 0, 0); if (SUCCEEDED(hr) && model.type() == VT_BSTR) { - WideToUTF16(V_BSTR(model.ptr()), ::SysStringLen(V_BSTR(model.ptr())), - &model_); + model_.assign(V_BSTR(model.ptr()), ::SysStringLen(V_BSTR(model.ptr()))); } } void WmiComputerSystemInfo::PopulateSerialNumber( const ComPtr<IWbemServices>& services) { - static constexpr StringPiece16 query_bios = - STRING16_LITERAL("SELECT SerialNumber FROM Win32_Bios"); + static constexpr WStringPiece query_bios = + L"SELECT SerialNumber FROM Win32_Bios"; ComPtr<IEnumWbemClassObject> enumerator_bios; - HRESULT hr = services->ExecQuery( - ScopedBstr(STRING16_LITERAL("WQL")), ScopedBstr(query_bios), - WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, nullptr, - &enumerator_bios); + HRESULT hr = + services->ExecQuery(ScopedBstr(L"WQL"), ScopedBstr(query_bios), + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + nullptr, &enumerator_bios); if (FAILED(hr) || !enumerator_bios.Get()) return; @@ -205,8 +204,8 @@ void WmiComputerSystemInfo::PopulateSerialNumber( ScopedVariant serial_number; hr = class_obj->Get(L"SerialNumber", 0, serial_number.Receive(), 0, 0); if (SUCCEEDED(hr) && serial_number.type() == VT_BSTR) { - WideToUTF16(V_BSTR(serial_number.ptr()), - ::SysStringLen(V_BSTR(serial_number.ptr())), &serial_number_); + serial_number_.assign(V_BSTR(serial_number.ptr()), + ::SysStringLen(V_BSTR(serial_number.ptr()))); } } diff --git a/chromium/base/win/wmi.h b/chromium/base/win/wmi.h index e1523152ce7..7949888a229 100644 --- a/chromium/base/win/wmi.h +++ b/chromium/base/win/wmi.h @@ -24,7 +24,6 @@ #include <wrl/client.h> #include "base/base_export.h" -#include "base/strings/string16.h" #include "base/strings/string_piece.h" namespace base { @@ -47,8 +46,8 @@ BASE_EXPORT bool CreateLocalWmiConnection( // WMI method that you can fill with parameter values using SetParameter. BASE_EXPORT bool CreateWmiClassMethodObject( IWbemServices* wmi_services, - StringPiece16 class_name, - StringPiece16 method_name, + WStringPiece class_name, + WStringPiece method_name, Microsoft::WRL::ComPtr<IWbemClassObject>* class_instance); // Creates a new process from |command_line|. The advantage over CreateProcess @@ -61,7 +60,7 @@ BASE_EXPORT bool CreateWmiClassMethodObject( // Processes created this way are children of wmiprvse.exe and run with the // caller credentials. // More info: http://msdn2.microsoft.com/en-us/library/aa394372(VS.85).aspx -BASE_EXPORT bool WmiLaunchProcess(const string16& command_line, +BASE_EXPORT bool WmiLaunchProcess(const std::wstring& command_line, int* process_id); // An encapsulation of information retrieved from the 'Win32_ComputerSystem' and @@ -72,9 +71,9 @@ class BASE_EXPORT WmiComputerSystemInfo { public: static WmiComputerSystemInfo Get(); - const string16& manufacturer() const { return manufacturer_; } - const string16& model() const { return model_; } - const string16& serial_number() const { return serial_number_; } + const std::wstring& manufacturer() const { return manufacturer_; } + const std::wstring& model() const { return model_; } + const std::wstring& serial_number() const { return serial_number_; } private: void PopulateModelAndManufacturer( @@ -82,9 +81,9 @@ class BASE_EXPORT WmiComputerSystemInfo { void PopulateSerialNumber( const Microsoft::WRL::ComPtr<IWbemServices>& services); - string16 manufacturer_; - string16 model_; - string16 serial_number_; + std::wstring manufacturer_; + std::wstring model_; + std::wstring serial_number_; }; } // namespace win diff --git a/chromium/base/win/wmi_unittest.cc b/chromium/base/win/wmi_unittest.cc index 6c465256196..438fff75d9c 100644 --- a/chromium/base/win/wmi_unittest.cc +++ b/chromium/base/win/wmi_unittest.cc @@ -42,8 +42,7 @@ TEST_F(WMITest, TestCreateClassMethod) { ASSERT_NE(wmi_services.Get(), nullptr); ComPtr<IWbemClassObject> class_method = nullptr; EXPECT_TRUE(CreateWmiClassMethodObject( - wmi_services.Get(), STRING16_LITERAL("Win32_ShortcutFile"), - STRING16_LITERAL("Rename"), &class_method)); + wmi_services.Get(), L"Win32_ShortcutFile", L"Rename", &class_method)); ASSERT_NE(class_method.Get(), nullptr); ULONG refs = class_method.Reset(); EXPECT_EQ(0u, refs); @@ -54,8 +53,7 @@ TEST_F(WMITest, TestCreateClassMethod) { // Creates an instance of cmd which executes 'echo' and exits immediately. TEST_F(WMITest, TestLaunchProcess) { int pid = 0; - bool result = - WmiLaunchProcess(STRING16_LITERAL("cmd.exe /c echo excelent!"), &pid); + bool result = WmiLaunchProcess(L"cmd.exe /c echo excelent!", &pid); EXPECT_TRUE(result); EXPECT_GT(pid, 0); } |