diff --git a/base/BUILD.gn b/base/BUILD.gn index 1b81cf081fbb..30533d974667 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn @@ -94,7 +94,7 @@ assert(!is_nacl || is_nacl_saigo, # This is here instead of in //build because //build is DEPS'd in by a few # subprojects that still support MSVC. -assert(!is_win || is_clang, +assert(!is_win || is_clang || use_cobalt_customizations, "only clang-cl is supported on Windows, see https://crbug.com/988071") if (is_apple) { @@ -202,13 +202,22 @@ if (enable_pkeys && is_debug) { } } +if (is_starboard) { + config("starboard_config") { + if (!is_gold) { + defines = [ "ENABLE_TEST_DATA" ] + } + } +} + buildflag_header("ios_cronet_buildflags") { header = "ios_cronet_buildflags.h" header_dir = "base/message_loop" flags = [ "CRONET_BUILD=$is_cronet_build" ] } -enable_message_pump_epoll = is_linux || is_chromeos || is_android +enable_message_pump_epoll = + !use_cobalt_customizations && (is_linux || is_chromeos || is_android) buildflag_header("message_pump_buildflags") { header = "message_pump_buildflags.h" header_dir = "base/message_loop" @@ -963,6 +972,48 @@ component("base") { "vlog.h", ] + if (use_cobalt_customizations && current_toolchain == default_toolchain) { + sources += [ + "base_paths_starboard.cc", + "base_paths_starboard.h", + "debug/debugger_starboard.cc", + "debug/stack_trace_starboard.cc", + "files/file_enumerator_starboard.cc", + "files/file_path_watcher_stub.cc", + "files/file_starboard.cc", + "files/file_util_starboard.cc", + "files/memory_mapped_file_starboard.cc", + "memory/page_size_starboard.cc", + "memory/platform_shared_memory_mapper_starboard.cc", + "memory/platform_shared_memory_region_starboard.cc", + "message_loop/message_pump_io_starboard.cc", + "message_loop/message_pump_io_starboard.h", + "message_loop/message_pump_ui_starboard.cc", + "message_loop/message_pump_ui_starboard.h", + "process/launch.cc", + "process/launch.h", + "process/launch_starboard.cc", + "process/memory_starboard.cc", + "process/process_starboard.cc", + "profiler/module_cache_starboard.cc", + "profiler/stack_sampler_starboard.cc", + "rand_util_starboard.cc", + "strings/string_util_starboard.cc", + "strings/string_util_starboard.h", + "strings/sys_string_conversions_starboard.cc", + "synchronization/condition_variable_starboard.cc", + "synchronization/lock_impl_starboard.cc", + "synchronization/waitable_event_starboard.cc", + "synchronization/waitable_event_watcher_starboard.cc", + "sys_info_starboard.cc", + "threading/platform_thread_starboard.cc", + "threading/thread_local_storage_starboard.cc", + "threading/thread_task_runner_handle.cc", + "time/time_now_starboard.cc", + "time/time_starboard.cc", + ] + } + # Various files that are unused in the Chromium build, but presumably here to # make downstream's life easier. They are not included in the main sources # list to avoid breaking GN formatting's auto-sorting. @@ -977,7 +1028,7 @@ component("base") { #"system/sys_info_openbsd.cc", ] - if (is_posix) { + if (!use_cobalt_customizations && is_posix) { sources += [ "debug/debugger_posix.cc", "file_descriptor_posix.cc", @@ -1005,7 +1056,7 @@ component("base") { ] } - if (is_linux || is_chromeos) { + if (!use_cobalt_customizations && (is_linux || is_chromeos)) { sources += [ "debug/proc_maps_linux.cc", "debug/proc_maps_linux.h", @@ -1024,7 +1075,14 @@ component("base") { ] } - if (is_linux || is_chromeos || is_android) { + if (use_cobalt_customizations) { + sources += [ + "process/internal_linux.cc", + "process/internal_linux.h", + ] + } + + if (!use_cobalt_customizations && (is_linux || is_chromeos || is_android)) { sources += [ "files/file_path_watcher_inotify.cc", "files/file_path_watcher_inotify.h", @@ -1057,6 +1115,16 @@ component("base") { "//third_party/modp_b64", ] + if (is_starboard) { + deps -= [ "//base/allocator/partition_allocator:raw_ptr" ] + # TODO: b/330221826 Change to |public_configs| with matching dependencies. + all_dependent_configs += [ ":starboard_config" ] + } + + if (is_starboard && current_toolchain != host_toolchain) { + deps += [ "//starboard:starboard_group" ] + } + # `raw_ptr` cannot be made a component due to CRT symbol issues. # Its gateway to being a component is through `//base`, so we have # to provide the appropriate `#define` here. @@ -1098,6 +1166,10 @@ component("base") { "//third_party/abseil-cpp:absl", ] + if (is_starboard) { + public_deps -= [ "//base/allocator/partition_allocator:buildflags" ] + } + if (build_rust_base_conversions) { # Base provides conversions between CXX types and base types (e.g. # StringPiece). @@ -1112,7 +1184,7 @@ component("base") { # building inside the cros_sdk environment - use host_toolchain as a # more robust check for this. if (!use_sysroot && (is_android || is_chromeos || (is_linux && !is_castos)) && - host_toolchain != "//build/toolchain/cros:host") { + host_toolchain != "//build/toolchain/cros:host" && !sb_is_modular) { libs += [ "atomic" ] } @@ -1193,7 +1265,7 @@ component("base") { } # Android. - if (is_android) { + if (is_android && !use_cobalt_customizations) { sources += [ "android/android_hardware_buffer_compat.cc", "android/android_hardware_buffer_compat.h", @@ -1356,7 +1428,7 @@ component("base") { # Make jni.h available. configs += [ "//third_party/jdk" ] } - if (is_android || is_robolectric) { + if ((is_android || is_robolectric) && !use_cobalt_customizations) { sources += [ "android/base_jni_onload.cc", "android/base_jni_onload.h", @@ -1610,8 +1682,6 @@ component("base") { "native_library.h", "path_service.cc", "path_service.h", - "process/process_metrics.cc", - "process/process_metrics.h", "scoped_native_library.cc", "scoped_native_library.h", "system/sys_info.cc", @@ -1621,28 +1691,38 @@ component("base") { "task/thread_pool/initialization_util.h", ] - if (is_win) { + if (is_starboard) { + sources -= [ + # Not used by cobalt. + "native_library.cc", + "native_library.h", + "scoped_native_library.cc", + "scoped_native_library.h", + ] + } + + if (is_win && !is_starboard) { sources += [ "base_paths_win.cc", "base_paths_win.h", ] } - if (is_mac) { + if (is_mac && !is_starboard) { sources += [ "base_paths_mac.h", "base_paths_mac.mm", ] } - if (is_android) { + if (is_android && !is_starboard) { sources += [ "base_paths_android.cc", "base_paths_android.h", ] } - if (is_posix) { + if (!use_cobalt_customizations && is_posix) { sources += [ "base_paths_posix.h", "memory/madv_free_discardable_memory_allocator_posix.cc", @@ -1656,7 +1736,7 @@ component("base") { ] } - if (is_posix || is_fuchsia) { + if (!use_cobalt_customizations && (is_posix || is_fuchsia)) { sources += [ "files/file_descriptor_watcher_posix.cc", "files/file_descriptor_watcher_posix.h", @@ -1666,14 +1746,14 @@ component("base") { ] } - if ((is_posix && !is_ios) || is_fuchsia) { + if ((!use_cobalt_customizations && is_posix && !is_ios) || is_fuchsia) { sources += [ "process/process_metrics_posix.cc", "sync_socket_posix.cc", ] } - if (is_posix && !is_apple) { + if (!use_cobalt_customizations && is_posix && !is_apple) { sources += [ "native_library_posix.cc", "posix/can_lower_nice_to.cc", @@ -1689,11 +1769,11 @@ component("base") { ] } - if (is_posix && !is_android) { + if (!use_cobalt_customizations && is_posix && !is_android) { sources += [ "debug/stack_trace_posix.cc" ] } - if (is_posix && !is_ios) { + if (!use_cobalt_customizations && is_posix && !is_ios) { sources += [ "process/kill_posix.cc", "process/process_posix.cc", @@ -1712,10 +1792,12 @@ component("base") { "process/memory.h", "process/process_iterator.cc", "process/process_iterator.h", + "process/process_metrics.cc", + "process/process_metrics.h", ] } - if (is_linux || is_chromeos) { + if ((is_linux || is_chromeos) && !is_starboard) { sources += [ "base_paths_posix.cc", "debug/elf_reader.cc", @@ -1814,7 +1896,7 @@ component("base") { } # Windows. - if (is_win) { + if (is_win && !is_starboard) { sources += [ "debug/debugger_win.cc", "debug/gdi_debug_util_win.cc", @@ -2043,7 +2125,7 @@ component("base") { } # Desktop Mac. - if (is_mac) { + if (is_mac && !is_starboard) { sources += [ "allocator/partition_allocator/shim/allocator_interception_mac.h", "allocator/partition_allocator/shim/allocator_interception_mac.mm", @@ -2129,7 +2211,7 @@ component("base") { } # Mac or iOS. - if (is_apple) { + if (is_apple && !is_starboard) { sources += [ "file_version_info_mac.h", "file_version_info_mac.mm", @@ -2175,7 +2257,7 @@ component("base") { } # Linux. - if (is_linux || is_chromeos) { + if (!use_cobalt_customizations && (is_linux || is_chromeos)) { # TODO(brettw) this will need to be parameterized at some point. linux_configs = [] if (use_glib) { @@ -2202,7 +2284,7 @@ component("base") { ] } } else { - if (!is_android) { + if (!is_android || is_starboard) { sources -= [ "linux_util.cc", "linux_util.h", @@ -2211,7 +2293,7 @@ component("base") { } # iOS - if (is_ios) { + if (is_ios && !is_starboard) { sources += [ "base_paths_mac.h", "base_paths_mac.mm", @@ -2316,7 +2398,7 @@ component("base") { ] } - if (use_blink) { + if (use_blink || true) { sources += [ "files/file_path_watcher.cc", "files/file_path_watcher.h", @@ -2345,14 +2427,14 @@ component("base") { # Android and MacOS have their own custom shared memory handle # implementations. e.g. due to supporting both POSIX and native handles. - if (is_posix && !is_android && !is_apple) { + if (!use_cobalt_customizations && is_posix && !is_android && !is_apple) { sources += [ "memory/platform_shared_memory_mapper_posix.cc", "memory/platform_shared_memory_region_posix.cc", ] } - if (is_posix && !is_apple) { + if (!use_cobalt_customizations && is_posix && !is_apple) { sources += [ "strings/sys_string_conversions_posix.cc", "synchronization/waitable_event_posix.cc", @@ -2381,17 +2463,19 @@ component("base") { } } + if (!is_starboard) { if ((is_posix && !is_apple && !is_android) || is_fuchsia) { sources += [ "profiler/stack_sampler_posix.cc" ] } + } - if ((is_posix && !is_apple && !is_android && !is_chromeos) || is_fuchsia) { + if ((is_posix && !is_apple && !is_android && !is_chromeos) || is_fuchsia || is_starboard) { sources += [ "power_monitor/power_monitor_device_source_stub.cc" ] } # On ARC++-enabled ChromeOS system, we need TimeTicks::FromUptimeMillis to # interpret time values sent from Android container. - if (is_android || is_chromeos_ash) { + if ((is_android || is_chromeos_ash) && !is_starboard) { sources += [ "time/time_android.cc" ] } @@ -2473,6 +2557,9 @@ component("base") { "tracing/tracing_tls.cc", "tracing/tracing_tls.h", ] + if (use_cobalt_customizations && use_xcode_clang) { + defines = [ "USES_XCODE_CLANG" ] + } public_deps += [ "//base/tracing/protos:chrome_track_event_zero", @@ -2489,7 +2576,7 @@ component("base") { "//third_party/perfetto/gn:public_config", ] - if (is_win) { + if (is_win && !is_starboard) { sources += [ "trace_event/trace_event_etw_export_win.cc", "trace_event/trace_event_etw_export_win.h", @@ -2498,7 +2585,7 @@ component("base") { ] } - if (is_android) { + if (is_android && !is_starboard) { sources += [ "trace_event/application_state_proto_android.cc", "trace_event/application_state_proto_android.h", @@ -2703,6 +2790,10 @@ static_library("base_static") { deps = [ "//build:chromeos_buildflags" ] + if (is_starboard && current_toolchain != host_toolchain) { + deps += [ "//starboard:starboard_group" ] + } + if (is_win) { sources += [ "win/static_constants.cc", @@ -2769,8 +2860,6 @@ component("i18n") { "i18n/streaming_utf8_validator.h", "i18n/string_compare.cc", "i18n/string_compare.h", - "i18n/string_search.cc", - "i18n/string_search.h", "i18n/time_formatting.cc", "i18n/time_formatting.h", "i18n/timezone.cc", @@ -2790,11 +2879,21 @@ component("i18n") { "//base/third_party/dynamic_annotations", "//build:chromecast_buildflags", "//build:chromeos_buildflags", + "//starboard/client_porting/icu_init", ] + if (is_starboard) { + public_deps -= [ "//third_party/ced" ] + } + if (!is_debug) { + if (is_starboard) { + configs -= [ "//starboard/build/config:size" ] + configs += [ "//starboard/build/config:speed" ] + } else { configs -= [ "//build/config/compiler:default_optimization" ] configs += [ "//build/config/compiler:optimize_max" ] + } } if (is_chromeos_lacros) { @@ -2804,11 +2903,12 @@ component("i18n") { ] } - if (is_mac) { + if (is_mac && !is_starboard) { frameworks = [ "CoreFoundation.framework" ] } } +if (!is_starboard) { test("base_perftests") { sources = [ "hash/hash_perftest.cc", @@ -2883,8 +2983,9 @@ test("base_i18n_perftests") { "//testing/gtest", ] } +} -if (!is_ios) { +if (!is_ios && !use_cobalt_customizations) { executable("build_utf8_validator_tables") { sources = [ "i18n/build_utf8_validator_tables.cc" ] deps = [ @@ -2908,7 +3009,7 @@ if (!is_ios) { } } -if (is_win) { +if (is_win && !is_starboard) { # Target to manually rebuild pe_image_test.dll which is checked into # base/test/data/pe_image. shared_library("pe_image_test") { @@ -2933,7 +3034,8 @@ if (is_win) { } } -if ((is_win && (current_cpu == "x64" || current_cpu == "arm64")) || is_mac || +if ((is_win && (current_cpu == "x64" || current_cpu == "arm64") && !is_starboard) || + (is_mac && !is_starboard) || (is_android && (current_cpu == "arm" || current_cpu == "arm64")) || (is_chromeos && current_cpu == "x64")) { # Must be a loadable module so that it can be loaded/unloaded at runtime @@ -2944,7 +3046,7 @@ if ((is_win && (current_cpu == "x64" || current_cpu == "arm64")) || is_mac || } } -if (is_android && (current_cpu == "arm" || current_cpu == "arm64")) { +if (is_android && (current_cpu == "arm" || current_cpu == "arm64") && !use_cobalt_customizations) { # Use separate library for # |LibunwindstackUnwinderAndroidTest.ReparsesMapsOnNewDynamicLibraryLoad| # testcase. We can't use the existing `base_profiler_test_support_library` @@ -2958,7 +3060,7 @@ if (is_android && (current_cpu == "arm" || current_cpu == "arm64")) { } } -if (is_android) { +if (is_android && !use_cobalt_customizations) { source_set("native_unwinder_android") { # This target is intended to be used only within the stack_unwinder dynamic # feature module, to avoid binary size increase in Chrome due to the @@ -2993,7 +3095,7 @@ source_set("base_stack_sampling_profiler_test_util") { "//base/test:test_support", "//testing/gtest", ] - if (is_android) { + if (is_android && !use_cobalt_customizations) { sources += [ "profiler/stack_sampling_profiler_java_test_util.cc", "profiler/stack_sampling_profiler_java_test_util.h", @@ -3005,9 +3107,11 @@ source_set("base_stack_sampling_profiler_test_util") { } } -if (is_apple) { - bundle_data("base_unittests_bundle_data") { +copy("base_unittests_bundle_data") { testonly = true + if (is_starboard) { + install_content = true + } sources = [ "//tools/metrics/histograms/enums.xml", "test/data/file_util/binary_file.bin", @@ -3033,12 +3137,15 @@ if (is_apple) { "test/data/serializer_test.json", "test/data/serializer_test_nowhitespace.json", ] + if (is_starboard) { + outputs = [ "$sb_static_contents_output_data_dir/test/base/{{source_target_relative}}" ] + } else { outputs = [ "{{bundle_resources_dir}}/" + "{{source_root_relative_dir}}/{{source_file_part}}" ] } } -if (is_apple) { +if (is_apple && !is_starboard) { source_set("base_unittests_arc") { testonly = true sources = [ @@ -3056,7 +3163,7 @@ if (is_apple) { } } -if (!is_nacl && (is_linux || is_chromeos)) { +if (!is_nacl && (is_linux || is_chromeos) && !use_cobalt_customizations) { # This test must compile with -fstack-protector-all source_set("stack_canary_linux_unittests") { testonly = true @@ -3511,6 +3618,87 @@ test("base_unittests") { sources += [ "system/sys_info_starboard_unittest.cc" ] } + if (use_cobalt_customizations) { + sources -= [ + # Cobalt builds don't include build timestamps. + "build_time_unittest.cc", + + # Since Starboard lacks abstractions for multi-processing, and shared + # memory is solely used for inter-process communication, we disable + # shared memory and process code in Cobalt. + "memory/discardable_memory_backing_field_trial_unittest.cc", + "memory/discardable_shared_memory_unittest.cc", + "memory/memory_pressure_listener_unittest.cc", + "memory/platform_shared_memory_region_unittest.cc", + "memory/shared_memory_hooks_unittest.cc", + "memory/shared_memory_mapping_unittest.cc", + "memory/shared_memory_region_unittest.cc", + "memory/unsafe_shared_memory_pool_unittest.cc", + + # MessagePumpUIStarboard lives on top of an external message pump + # and behaves differently than normal message pumps. + "message_loop/message_pump_unittest.cc", + "process/process_metrics_unittest.cc", + "process/process_unittest.cc", + "process/process_util_unittest.cc", + + # moduleCache is not implemented in Starboard. + "profiler/module_cache_unittest.cc", + + # TODO: b/327008491 - Not used by Cobalt, but should be tested to get + # closer to Chrome. + "feature_list_unittest.cc", + "immediate_crash_unittest.cc", + "metrics/field_trial_params_unittest.cc", + "native_library_unittest.cc", + "test/launcher/test_launcher_unittest.cc", + "test/launcher/test_results_tracker_unittest.cc", + "test/metrics/histogram_enum_reader_unittest.cc", + "test/scoped_feature_list_unittest.cc", + "threading/hang_watcher_unittest.cc", + "test/gtest_links_unittest.cc", + "test/gtest_tags_unittest.cc", + "test/gtest_xml_unittest_result_printer_unittest.cc", + + # SyncSocket is not implemented in Starboard. + "sync_socket_unittest.cc", + + # WaitableEventWatcher is not implemented by Starboard. + "synchronization/waitable_event_watcher_unittest.cc", + + # Depends on USE_PARTITION_ALLOC. + "allocator/partition_allocator/pointers/raw_ptr_test_support.h", + "allocator/partition_allocator/pointers/raw_ptr_unittest.cc", + "allocator/partition_allocator/pointers/raw_ref_unittest.cc", + + # Previously disabled tests. + "environment_unittest.cc", + "files/memory_mapped_file_unittest.cc", + ] + + if (is_win && cobalt_pending_clean_up) { + sources -= [ + "power_monitor/battery_state_sampler_unittest.cc", + "profiler/stack_sampling_profiler_unittest.cc", + "ranges/algorithm_unittest.cc", + "safe_numerics_unittest.cc", + "strings/string_util_unittest.cc", + "task/sequence_manager/atomic_flag_set_unittest.cc", + "test/test_future_unittest.cc", + "tools_sanity_unittest.cc", + "types/expected_unittest.cc", + + # NiceMock needs to be fixed + "threading/scoped_blocking_call_unittest.cc", + ] + cflags = [ "/wd4172" ] + } + + if (is_clang) { + cflags_cc = [ "-Wno-trigraphs" ] + } + } + if (!is_cronet_build) { # location_unittest.cc fails gn check for tracing-shimmed Cronet builds. # Cronet buildbot do not run base_unittests, so exclusing this test @@ -3553,10 +3741,6 @@ test("base_unittests") { ] data_deps = [ - "//base/test:immediate_crash_test_helper", - "//base/test:test_child_process", - "//base/test:test_shared_library", - "//testing/buildbot/filters:base_unittests_filters", ] if (is_android && enable_chrome_android_internal) { @@ -3567,13 +3751,20 @@ test("base_unittests") { deps += [ "//build/rust:cxx_cppdeps" ] } - if (is_apple) { + if (is_apple && !is_starboard) { public_deps = [ ":base_unittests_bundle_data" ] deps += [ ":base_unittests_arc" ] } - if (!is_ios) { + if (is_starboard){ + data_deps += [ + ":base_unittests_bundle_data", + "//cobalt/network:copy_ssl_certificates", + ] + } + + if (!is_ios && !is_starboard) { sources += [ "allocator/partition_allocator/tagging_unittest.cc" ] } @@ -3582,7 +3773,7 @@ test("base_unittests") { "//tools/metrics/histograms/enums.xml", ] - if (is_win) { + if (is_win && !is_starboard) { deps += [ "//base/win:base_win_buildflags" ] sources += [ @@ -3653,18 +3844,18 @@ test("base_unittests") { ] } - if (is_linux || is_chromeos) { + if (!use_cobalt_customizations && (is_linux || is_chromeos)) { sources += [ "debug/proc_maps_linux_unittest.cc", "files/scoped_file_linux_unittest.cc", ] - if (!is_nacl) { + if (!!use_cobalt_customizations && !is_nacl) { deps += [ ":stack_canary_linux_unittests" ] } } - if (is_mac) { + if (is_mac && !is_starboard) { sources += [ "allocator/partition_allocator/shim/allocator_interception_mac_unittest.mm", "allocator/partition_allocator/shim/malloc_zone_functions_mac_unittest.cc", @@ -3688,11 +3879,11 @@ test("base_unittests") { ] } - if (is_apple && enable_mach_absolute_time_ticks) { + if (is_apple && enable_mach_absolute_time_ticks && !is_starboard) { sources += [ "time/time_mac_unittest.mm" ] } - if (is_posix) { + if (!use_cobalt_customizations && is_posix) { sources += [ "files/dir_reader_posix_unittest.cc", "files/file_descriptor_watcher_posix_unittest.cc", @@ -3717,7 +3908,7 @@ test("base_unittests") { defines += [ "SYSTEM_NATIVE_UTF8" ] } - if (is_android) { + if (is_android && !use_cobalt_customizations) { # Add unwind tables in base_unittests_apk test apk. The unwind tables are # generated from debug info in the binary. Removing "default_symbols" and # adding symbols config removes the "strip_debug" config that strips the @@ -3801,7 +3992,11 @@ test("base_unittests") { sources += [ "debug/allocation_trace_unittest.cc" ] } - if (is_ios) { + if (is_starboard) { + sources += [ "message_loop/message_pump_io_starboard_unittest.cc" ] + } + + if (is_ios && !is_starboard) { sources += [ "ios/device_util_unittest.mm", "ios/scoped_critical_action_unittest.mm", @@ -3925,7 +4120,7 @@ test("base_unittests") { deps += [ ":partition_alloc_test_support" ] } - if (is_mac) { + if (is_mac && !is_starboard) { sources += [ "message_loop/message_pump_kqueue_unittest.cc" ] frameworks = [ "CoreFoundation.framework", @@ -3937,7 +4132,7 @@ test("base_unittests") { } } - if (is_fuchsia || is_linux || is_chromeos) { + if (!use_cobalt_customizations && (is_fuchsia || is_linux || is_chromeos)) { sources += [ "debug/elf_reader_unittest.cc", "debug/test_elf_image_builder.cc", @@ -3960,7 +4155,7 @@ test("base_unittests") { sources += [ "i18n/icu_mergeable_data_file_unittest.cc" ] } - if (is_linux || is_chromeos_lacros) { + if (!use_cobalt_customizations && (is_linux || is_chromeos_lacros)) { sources += [ "linux_util_unittest.cc", "nix/xdg_util_unittest.cc", @@ -4027,11 +4222,15 @@ test("base_unittests") { [ "//build/config/fuchsia/test/logger.shard.test-cml" ] } - if (!is_fuchsia && !is_ios) { + if (!use_cobalt_customizations && !is_fuchsia && !is_ios) { sources += [ "files/file_locking_unittest.cc" ] } - if (is_android) { + if (use_cobalt_customizations) { + sources -= [ "files/file_path_watcher_unittest.cc" ] + } + + if (is_android && !use_cobalt_customizations) { deps += [ "//testing/android/native_test:native_test_native_code" ] sources += [ "debug/elf_reader_unittest.cc", @@ -4041,7 +4240,7 @@ test("base_unittests") { ] } - if (is_win) { + if (is_win && !is_starboard) { deps += [ "//base:scoped_handle_test_dll" ] if (current_cpu == "x64" || current_cpu == "arm64") { sources += [ "profiler/win32_stack_frame_unwinder_unittest.cc" ] @@ -4053,7 +4252,7 @@ test("base_unittests") { deps += [ ":rust_cfg_win_test" ] } } - if (is_apple) { + if (is_apple && !is_starboard) { sources += [ "profiler/frame_pointer_unwinder_unittest.cc" ] } if (is_chromeos && current_cpu == "x64") { @@ -4099,7 +4298,15 @@ test("base_unittests") { "tracing/perfetto_task_runner_unittest.cc", ] - if (is_android) { + if (cobalt_pending_clean_up) { + sources -= [ + "trace_event/memory_allocator_dump_unittest.cc", + "trace_event/process_memory_dump_unittest.cc", + "trace_event/typed_macros_unittest.cc", + ] + } + + if (is_android && !is_starboard) { sources += [ "trace_event/cpufreq_monitor_android_unittest.cc", "trace_event/java_heap_dump_provider_android_unittest.cc", @@ -4185,7 +4392,7 @@ if (enable_nocompile_tests) { } } -if (is_android || is_robolectric) { +if ((is_android || is_robolectric) && !use_cobalt_customizations) { generate_jni("base_jni_headers") { sources = [ "android/java/src/org/chromium/base/ApkAssets.java", @@ -4247,7 +4454,7 @@ if (is_android || is_robolectric) { } } # is_android || is_robolectric -if (is_android) { +if (is_android && !use_cobalt_customizations) { java_library("jni_java") { supports_android = true sources = [ @@ -4870,7 +5077,7 @@ fuzzer_test("base_json_string_escape_fuzzer") { deps = [ "//base" ] } -if (is_mac) { +if (is_mac && !is_starboard) { protoc_convert("base_mach_port_rendezvous_convert_corpus") { sources = [ "test/data/mach_port_rendezvous_fuzz/dead_name.textproto", @@ -5021,4 +5228,8 @@ source_set("partition_alloc_test_support") { configs -= [ "//build/config/compiler:default_optimization" ] configs += [ "//build/config/compiler:optimize_speed" ] } + + if (is_starboard) { + sources = [] + } } diff --git a/base/BUILD.gn.rej b/base/BUILD.gn.rej new file mode 100644 index 000000000000..3bd8ab09d2c5 --- /dev/null +++ b/base/BUILD.gn.rej @@ -0,0 +1,10 @@ +--- BUILD.gn ++++ BUILD.gn +@@ -103,6 +103,7 @@ if (is_apple) { + + # Determines whether libevent should be dep. + dep_libevent = !is_fuchsia && !is_win && !is_mac && !is_nacl ++ && !use_cobalt_customizations + + # Determines whether message_pump_libevent should be used. + use_libevent = dep_libevent && !is_ios diff --git a/base/METADATA b/base/METADATA new file mode 100644 index 000000000000..3d9342e0e79e --- /dev/null +++ b/base/METADATA @@ -0,0 +1,24 @@ +name: "base" +description: + "Subtree at base." + +third_party { + identifier { + type: "ChromiumVersion" + value: "114.0.5735.358" # from https://chromereleases.googleblog.com/2024/03/long-term-support-channel-update-for_26.html + } + identifier { + type: "Git" + value: "https://chromium.googlesource.com/chromium/src.git" + version: "1759c6ae9316996b9f150c0ce9d0ca78a3d15c02" + } + identifier { + type: "UpstreamSubdir" + value: "base" + } + last_upgrade_date { + year: 2023 + month: 9 + day: 12 + } +} diff --git a/base/allocator/dispatcher/dispatcher.cc b/base/allocator/dispatcher/dispatcher.cc index 2d4f17ea2a30..aa0e0ae0da45 100644 --- a/base/allocator/dispatcher/dispatcher.cc +++ b/base/allocator/dispatcher/dispatcher.cc @@ -6,9 +6,11 @@ #include "base/allocator/buildflags.h" #include "base/allocator/dispatcher/internal/dispatch_data.h" +#if !defined(COBALT_PENDING_CLEAN_UP) #include "base/allocator/partition_allocator/partition_alloc.h" #include "base/allocator/partition_allocator/partition_alloc_buildflags.h" #include "base/allocator/partition_allocator/shim/allocator_shim.h" +#endif #include "base/check.h" #include "base/dcheck_is_on.h" #include "base/no_destructor.h" diff --git a/base/allocator/dispatcher/dispatcher_unittest.cc b/base/allocator/dispatcher/dispatcher_unittest.cc index cc9028e5753f..559493f1d44b 100644 --- a/base/allocator/dispatcher/dispatcher_unittest.cc +++ b/base/allocator/dispatcher/dispatcher_unittest.cc @@ -26,8 +26,10 @@ namespace base::allocator::dispatcher { namespace { using configuration::kMaximumNumberOfObservers; using configuration::kMaximumNumberOfOptionalObservers; +#if BUILDFLAG(USE_PARTITION_ALLOC) using partition_alloc::PartitionOptions; using partition_alloc::ThreadSafePartitionRoot; +#endif using testing::DispatcherTest; // A simple observer implementation. Since these tests plug in to Partition diff --git a/base/allocator/dispatcher/internal/dispatch_data.h b/base/allocator/dispatcher/internal/dispatch_data.h index 62f73dd3dec2..64931e616f0b 100644 --- a/base/allocator/dispatcher/internal/dispatch_data.h +++ b/base/allocator/dispatcher/internal/dispatch_data.h @@ -6,7 +6,9 @@ #define BASE_ALLOCATOR_DISPATCHER_INTERNAL_DISPATCH_DATA_H_ #include "base/allocator/buildflags.h" +#if defined(COBALT_PENDING_CLEAN_UP) #include "base/allocator/partition_allocator/partition_alloc_buildflags.h" +#endif #include "base/base_export.h" #include "build/build_config.h" diff --git a/base/allocator/dispatcher/internal/dispatcher_internal.h b/base/allocator/dispatcher/internal/dispatcher_internal.h index 165e2ba6f01a..7b62fb0d2744 100644 --- a/base/allocator/dispatcher/internal/dispatcher_internal.h +++ b/base/allocator/dispatcher/internal/dispatcher_internal.h @@ -10,7 +10,9 @@ #include "base/allocator/dispatcher/internal/dispatch_data.h" #include "base/allocator/dispatcher/internal/tools.h" #include "base/allocator/dispatcher/subsystem.h" +#if !defined(COBALT_PENDING_CLEAN_UP) #include "base/allocator/partition_allocator/partition_alloc_buildflags.h" +#endif #include "base/check.h" #include "base/compiler_specific.h" #include "build/build_config.h" diff --git a/base/allocator/partition_allocator/BUILD.gn b/base/allocator/partition_allocator/BUILD.gn index 567b78c5f3af..8ae534ed8026 100644 --- a/base/allocator/partition_allocator/BUILD.gn +++ b/base/allocator/partition_allocator/BUILD.gn @@ -325,7 +325,7 @@ component("partition_alloc") { ] deps = [] public_configs = [] - if (is_android) { + if (is_android && !use_cobalt_customizations) { # tagging.cc requires __arm_mte_set_* functions. deps += [ "//third_party/android_ndk:cpu_features" ] } @@ -370,6 +370,12 @@ component("partition_alloc") { if (enable_pkeys && is_debug) { configs += [ ":no_stack_protector" ] } + + # Need to include |component("partition_alloc")| for gn check. + if (is_starboard) { + # TODO: b/326459868 - Try using |check_includes = false|. + sources = [] + } } source_set("raw_ptr") { diff --git a/base/allocator/partition_allocator/partition_alloc.gni b/base/allocator/partition_allocator/partition_alloc.gni index 3954743c8c70..e6835a8917d5 100644 --- a/base/allocator/partition_allocator/partition_alloc.gni +++ b/base/allocator/partition_allocator/partition_alloc.gni @@ -47,7 +47,7 @@ declare_args() { # and doesn't wish to incur the library size increase (crbug.com/674570). # 2. On NaCl (through this declaration), where PartitionAlloc doesn't # build at all. - use_partition_alloc = !is_nacl + use_partition_alloc = !use_cobalt_customizations && !is_nacl } declare_args() { @@ -305,7 +305,7 @@ assert(!use_asan_backup_ref_ptr || use_hookable_raw_ptr, "AsanBackupRefPtr requires RawPtrHookableImpl") declare_args() { - enable_pkeys = is_linux && target_cpu == "x64" + enable_pkeys = is_linux && target_cpu == "x64" && !use_cobalt_customizations } assert(!enable_pkeys || (is_linux && target_cpu == "x64"), "Pkeys are only supported on x64 linux") diff --git a/base/allocator/partition_allocator/partition_alloc_base/check.h b/base/allocator/partition_allocator/partition_alloc_base/check.h index 298c4f700443..9eb3e7fb2675 100644 --- a/base/allocator/partition_allocator/partition_alloc_base/check.h +++ b/base/allocator/partition_allocator/partition_alloc_base/check.h @@ -102,7 +102,7 @@ class PA_COMPONENT_EXPORT(PARTITION_ALLOC) CheckError { #error "Debug builds are not expected to be optimized as official builds." #endif // defined(OFFICIAL_BUILD) && !defined(NDEBUG) -#if defined(OFFICIAL_BUILD) && !BUILDFLAG(PA_DCHECK_IS_ON) +#if defined(OFFICIAL_BUILD) && !BUILDFLAG(PA_DCHECK_IS_ON) && !defined(STARBOARD) // Discard log strings to reduce code bloat. // diff --git a/base/allocator/partition_allocator/partition_alloc_base/rand_util_posix.cc b/base/allocator/partition_allocator/partition_alloc_base/rand_util_posix.cc index 37a22783401f..6019bbc715c3 100644 --- a/base/allocator/partition_allocator/partition_alloc_base/rand_util_posix.cc +++ b/base/allocator/partition_allocator/partition_alloc_base/rand_util_posix.cc @@ -18,7 +18,8 @@ #include "base/allocator/partition_allocator/partition_alloc_check.h" #include "build/build_config.h" -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) +#if defined(STARBOARD) +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) #include "third_party/lss/linux_syscall_support.h" #elif BUILDFLAG(IS_MAC) // TODO(crbug.com/995996): Waiting for this header to appear in the iOS SDK. diff --git a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread.h b/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread.h index a99ed138b274..affac9b085ec 100644 --- a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread.h +++ b/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread.h @@ -18,7 +18,9 @@ #include "base/allocator/partition_allocator/partition_alloc_base/time/time.h" #include "build/build_config.h" -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +#include "starboard/thread.h" +#elif BUILDFLAG(IS_WIN) #include "base/allocator/partition_allocator/partition_alloc_base/win/windows_types.h" #elif BUILDFLAG(IS_FUCHSIA) #include @@ -32,7 +34,9 @@ namespace partition_alloc::internal::base { // Used for logging. Always an integer value. -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +typedef SbThreadId PlatformThreadId; +#elif BUILDFLAG(IS_WIN) typedef DWORD PlatformThreadId; #elif BUILDFLAG(IS_FUCHSIA) typedef zx_handle_t PlatformThreadId; @@ -45,7 +49,9 @@ typedef pid_t PlatformThreadId; // Used to operate on threads. class PlatformThreadHandle { public: -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) + typedef SbThread Handle; +#elif BUILDFLAG(IS_WIN) typedef void* Handle; #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) typedef pthread_t Handle; diff --git a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_ref.h b/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_ref.h index f7457a90c524..3fec951f16b2 100644 --- a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_ref.h +++ b/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_ref.h @@ -15,7 +15,9 @@ #include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" #include "build/build_config.h" -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +#include "starboard/thread.h" +#elif BUILDFLAG(IS_WIN) #include "base/allocator/partition_allocator/partition_alloc_base/win/windows_types.h" #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) #include @@ -33,7 +35,9 @@ namespace partition_alloc::internal::base { // to distinguish a new thread from an old, dead thread. class PlatformThreadRef { public: -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) + typedef SbThread RefType; +#elif BUILDFLAG(IS_WIN) using RefType = DWORD; #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) using RefType = pthread_t; diff --git a/base/allocator/partition_allocator/partition_alloc_config.h.rej b/base/allocator/partition_allocator/partition_alloc_config.h.rej new file mode 100644 index 000000000000..7e99e74f5fe8 --- /dev/null +++ b/base/allocator/partition_allocator/partition_alloc_config.h.rej @@ -0,0 +1,11 @@ +--- allocator/partition_allocator/partition_alloc_config.h ++++ allocator/partition_allocator/partition_alloc_config.h +@@ -104,7 +104,7 @@ static_assert(sizeof(void*) != 8, ""); + // POSIX is not only UNIX, e.g. macOS and other OSes. We do use Linux-specific + // features such as futex(2). + #define PA_CONFIG_HAS_LINUX_KERNEL() \ +- (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)) ++ (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)) && !defined(STARBOARD) + + // On some platforms, we implement locking by spinning in userspace, then going + // into the kernel only if there is contention. This requires platform support, diff --git a/base/allocator/partition_allocator/partition_alloc_unittest.cc b/base/allocator/partition_allocator/partition_alloc_unittest.cc index 010497bf6070..9a0db5559e02 100644 --- a/base/allocator/partition_allocator/partition_alloc_unittest.cc +++ b/base/allocator/partition_allocator/partition_alloc_unittest.cc @@ -53,7 +53,8 @@ #include #endif -#if BUILDFLAG(IS_POSIX) +#if defined(STARBOARD) +#elif BUILDFLAG(IS_POSIX) #if BUILDFLAG(IS_LINUX) // We need PKEY_DISABLE_WRITE in this file; glibc defines it in sys/mman.h but // it's actually Linux-specific and other Linux libcs define it in linux/mman.h. diff --git a/base/at_exit.cc b/base/at_exit.cc index a71988fc446c..61d5ad41b5ea 100644 --- a/base/at_exit.cc +++ b/base/at_exit.cc @@ -28,7 +28,7 @@ static bool g_disable_managers = false; AtExitManager::AtExitManager() : next_manager_(g_top_manager) { // If multiple modules instantiate AtExitManagers they'll end up living in this // module... they have to coexist. -#if !defined(COMPONENT_BUILD) +#if !defined(COMPONENT_BUILD) && !defined(STARBOARD) DCHECK(!g_top_manager); #endif g_top_manager = this; diff --git a/base/base_paths.h b/base/base_paths.h index 28c164346423..3cea49ea3481 100644 --- a/base/base_paths.h +++ b/base/base_paths.h @@ -10,6 +10,9 @@ #include "build/build_config.h" +#if defined(STARBOARD) +#include "base/base_paths_starboard.h" +#else #if BUILDFLAG(IS_WIN) #include "base/base_paths_win.h" #elif BUILDFLAG(IS_APPLE) @@ -21,6 +24,7 @@ #if BUILDFLAG(IS_POSIX) #include "base/base_paths_posix.h" #endif +#endif namespace base { diff --git a/base/base_paths_starboard.cc b/base/base_paths_starboard.cc new file mode 100644 index 000000000000..c13f645fb421 --- /dev/null +++ b/base/base_paths_starboard.cc @@ -0,0 +1,119 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "base/base_paths.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "starboard/configuration_constants.h" +#include "starboard/system.h" + +namespace base { + +// This is where we can control the path for placement of a lot of file +// resources for cobalt. +bool PathProviderStarboard(int key, FilePath *result) { + std::vector path(kSbFileMaxPath, 0); + switch (key) { + case base::FILE_EXE: + case base::FILE_MODULE: { + bool success = SbSystemGetPath(kSbSystemPathExecutableFile, path.data(), + path.size()); + DCHECK(success); + if (success) { + *result = FilePath(path.data()); + return true; + } + DLOG(ERROR) << "FILE_EXE not defined."; + return false; + } + + case base::DIR_EXE: + case base::DIR_MODULE: + case base::DIR_ASSETS: { + bool success = SbSystemGetPath(kSbSystemPathContentDirectory, path.data(), + path.size()); + DCHECK(success); + if (success) { + *result = FilePath(path.data()); + return true; + } + DLOG(ERROR) << "DIR_EXE/DIR_MODULE not defined."; + return false; + } + +#if defined(ENABLE_TEST_DATA) + case base::DIR_TEST_DATA: { + bool success = SbSystemGetPath(kSbSystemPathContentDirectory, path.data(), + path.size()); + DCHECK(success); + if (success) { + // Append "test" to match the output of the files copied during builds. + *result = FilePath(path.data()).Append(FILE_PATH_LITERAL("test")); + return true; + } + DLOG(ERROR) << "DIR_TEST_DATA not defined."; + return false; + } +#endif // ENABLE_TEST_DATA + + case base::DIR_CACHE: { + bool success = SbSystemGetPath(kSbSystemPathCacheDirectory, path.data(), + path.size()); + if (success) { + *result = FilePath(path.data()); + return true; + } + DLOG(INFO) << "DIR_CACHE not defined."; + return false; + } + + case base::DIR_TEMP: { + bool success = + SbSystemGetPath(kSbSystemPathTempDirectory, path.data(), path.size()); + DCHECK(success); + if (success) { + *result = FilePath(path.data()); + return true; + } + DLOG(ERROR) << "DIR_TEMP not defined."; + return false; + } + + case base::DIR_HOME: + // TODO: Add a home directory to SbSystemPathId and get it from there. + return PathProviderStarboard(base::DIR_CACHE, result); + + case base::DIR_SYSTEM_FONTS: + if (SbSystemGetPath(kSbSystemPathFontDirectory, path.data(), + path.size())) { + *result = FilePath(path.data()); + return true; + } + DLOG(INFO) << "DIR_SYSTEM_FONTS not defined."; + return false; + + case base::DIR_SYSTEM_FONTS_CONFIGURATION: + if (SbSystemGetPath(kSbSystemPathFontConfigurationDirectory, path.data(), + path.size())) { + *result = FilePath(path.data()); + return true; + } + DLOG(INFO) << "DIR_SYSTEM_FONTS_CONFIGURATION not defined."; + return false; + } + + return false; +} + +} diff --git a/base/base_paths_starboard.h b/base/base_paths_starboard.h new file mode 100644 index 000000000000..7ce274edea71 --- /dev/null +++ b/base/base_paths_starboard.h @@ -0,0 +1,48 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef BASE_BASE_PATHS_STARBOARD_H_ +#define BASE_BASE_PATHS_STARBOARD_H_ + +// This file declares Starboard-specific path keys for the base module. These +// can be used with the PathService to access various special directories and +// files. + +namespace base { + +enum { + PATH_STARBOARD_START = 500, + + DIR_CACHE, // Directory where to put cache data. Note this is + // *not* where the browser cache lives, but the + // browser cache can be a subdirectory. + + DIR_SYSTEM_FONTS, // Directory where system font files can be + // be found. This is only specified on + // platforms that provide fonts usable by + // Starboard applications. + + DIR_SYSTEM_FONTS_CONFIGURATION, // Directory where system font configuration + // metadata can be found. May be the same + // directory as DIR_SYSTEM_FONTS, but not + // necessarily. This is only specified on + // platforms that provide fonts usable by + // Starboard applications. + + PATH_STARBOARD_END +}; + +} // namespace base + +#endif // BASE_BASE_PATHS_STARBOARD_H_ diff --git a/base/base_switches.cc b/base/base_switches.cc index 00c1f165b703..184dd3875d84 100644 --- a/base/base_switches.cc +++ b/base/base_switches.cc @@ -8,6 +8,14 @@ namespace switches { +#ifdef COBALT_PENDING_CLEAN_UP +// Setting this switch defines which font format(s) Cobalt will load locally. +// Values include 'ttf', 'ttf-preferred', 'woff2', and 'woff2-preferred'. +// Values with 'preferred' can load all types of fonts but prioritize the +// format specified. 'woff2-preferred' is the default value. +const char kFontFormat[] = "font-format"; +#endif + // Delays execution of TaskPriority::BEST_EFFORT tasks until shutdown. const char kDisableBestEffortTasks[] = "disable-best-effort-tasks"; diff --git a/base/base_switches.h b/base/base_switches.h index 093ba75edb3a..56463e1e7403 100644 --- a/base/base_switches.h +++ b/base/base_switches.h @@ -12,6 +12,9 @@ namespace switches { +#ifdef COBALT_PENDING_CLEAN_UP +extern const char kFontFormat[]; +#endif extern const char kDisableBestEffortTasks[]; extern const char kDisableBreakpad[]; extern const char kDisableFeatures[]; diff --git a/base/basictypes.h b/base/basictypes.h new file mode 100644 index 000000000000..d84eda80f6ab --- /dev/null +++ b/base/basictypes.h @@ -0,0 +1,289 @@ +#ifndef BASE_BASICTYPES_H_ +#define BASE_BASICTYPES_H_ + +#ifndef COBALT_PENDING_CLEAN_UP +#error "TODO: Remove these" +#endif + +#include // So we can set the bounds of our types +#include // For size_t +#include // for memcpy + +#include "base/macros.h" +#include "starboard/types.h" + +typedef signed char schar; +typedef signed char int8; +typedef short int16; +typedef int32_t int32; +typedef int64_t int64; +typedef uint8_t uint8; +typedef uint16_t uint16; +typedef uint32_t uint32; +typedef uint64_t uint64; +typedef int16_t char16; +typedef int32_t char32; + +const uint8 kuint8max = ((uint8)0xFF); +const uint16 kuint16max = ((uint16)0xFFFF); +const uint32 kuint32max = ((uint32)0xFFFFFFFF); +// const uint64 kuint64max = ((uint64) GG_LONGLONG(0xFFFFFFFFFFFFFFFF)); +const int8 kint8min = ((int8)0x80); +const int8 kint8max = ((int8)0x7F); +const int16 kint16min = ((int16)0x8000); +const int16 kint16max = ((int16)0x7FFF); +const int32 kint32min = ((int32)0x80000000); +const int32 kint32max = ((int32)0x7FFFFFFF); +// const int64 kint64min = (( int64) GG_LONGLONG(0x8000000000000000)); +// const int64 kint64max = (( int64) GG_LONGLONG(0x7FFFFFFFFFFFFFFF)); + +// The arraysize(arr) macro returns the # of elements in an array arr. +// The expression is a compile-time constant, and therefore can be +// used in defining new arrays, for example. If you use arraysize on +// a pointer by mistake, you will get a compile-time error. +// +// One caveat is that arraysize() doesn't accept any array of an +// anonymous type or a type defined inside a function. In these rare +// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below. This is +// due to a limitation in C++'s template system. The limitation might +// eventually be removed, but it hasn't happened yet. + +// This template function declaration is used in defining arraysize. +// Note that the function doesn't need an implementation, as we only +// use its type. +template +char (&ArraySizeHelper(T (&array)[N]))[N]; + +// That gcc wants both of these prototypes seems mysterious. VC, for +// its part, can't decide which to use (another mystery). Matching of +// template overloads: the final frontier. +#ifndef _MSC_VER +template +char (&ArraySizeHelper(const T (&array)[N]))[N]; +#endif + +#if defined(COMPILER_GHS) +// GHS does not support local types as template arguments, so we must fall back +// to the unsafe version +#define arraysize ARRAYSIZE_UNSAFE +#else +#define arraysize(array) (sizeof(ArraySizeHelper(array))) +#endif + +// ARRAYSIZE_UNSAFE performs essentially the same calculation as arraysize, +// but can be used on anonymous types or types defined inside +// functions. It's less safe than arraysize as it accepts some +// (although not all) pointers. Therefore, you should use arraysize +// whenever possible. +// +// The expression ARRAYSIZE_UNSAFE(a) is a compile-time constant of type +// size_t. +// +// ARRAYSIZE_UNSAFE catches a few type errors. If you see a compiler error +// +// "warning: division by zero in ..." +// +// when using ARRAYSIZE_UNSAFE, you are (wrongfully) giving it a pointer. +// You should only use ARRAYSIZE_UNSAFE on statically allocated arrays. +// +// The following comments are on the implementation details, and can +// be ignored by the users. +// +// ARRAYSIZE_UNSAFE(arr) works by inspecting sizeof(arr) (the # of bytes in +// the array) and sizeof(*(arr)) (the # of bytes in one array +// element). If the former is divisible by the latter, perhaps arr is +// indeed an array, in which case the division result is the # of +// elements in the array. Otherwise, arr cannot possibly be an array, +// and we generate a compiler error to prevent the code from +// compiling. +// +// Since the size of bool is implementation-defined, we need to cast +// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final +// result has type size_t. +// +// This macro is not perfect as it wrongfully accepts certain +// pointers, namely where the pointer size is divisible by the pointee +// size. Since all our code has to go through a 32-bit compiler, +// where a pointer is 4 bytes, this means all pointers to a type whose +// size is 3 or greater than 4 will be (righteously) rejected. + +#define ARRAYSIZE_UNSAFE(a) \ + ((sizeof(a) / sizeof(*(a))) / \ + static_cast(!(sizeof(a) % sizeof(*(a))))) + + +// Use implicit_cast as a safe version of static_cast or const_cast +// for upcasting in the type hierarchy (i.e. casting a pointer to Foo +// to a pointer to SuperclassOfFoo or casting a pointer to Foo to +// a const pointer to Foo). +// When you use implicit_cast, the compiler checks that the cast is safe. +// Such explicit implicit_casts are necessary in surprisingly many +// situations where C++ demands an exact type match instead of an +// argument type convertable to a target type. +// +// The From type can be inferred, so the preferred syntax for using +// implicit_cast is the same as for static_cast etc.: +// +// implicit_cast(expr) +// +// implicit_cast would have been part of the C++ standard library, +// but the proposal was submitted too late. It will probably make +// its way into the language in the future. +template +inline To implicit_cast(From const& f) { + return f; +} + +// The COMPILE_ASSERT macro can be used to verify that a compile time +// expression is true. For example, you could use it to verify the +// size of a static array: +// +// COMPILE_ASSERT(ARRAYSIZE_UNSAFE(content_type_names) == CONTENT_NUM_TYPES, +// content_type_names_incorrect_size); +// +// or to make sure a struct is smaller than a certain size: +// +// COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large); +// +// The second argument to the macro is the name of the variable. If +// the expression is false, most compilers will issue a warning/error +// containing the name of the variable. + +template +struct CompileAssert {}; + +#undef COMPILE_ASSERT +#define COMPILE_ASSERT(expr, msg) \ + typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] + +// Implementation details of COMPILE_ASSERT: +// +// - COMPILE_ASSERT works by defining an array type that has -1 +// elements (and thus is invalid) when the expression is false. +// +// - The simpler definition +// +// #define COMPILE_ASSERT(expr, msg) typedef char msg[(expr) ? 1 : -1] +// +// does not work, as gcc supports variable-length arrays whose sizes +// are determined at run-time (this is gcc's extension and not part +// of the C++ standard). As a result, gcc fails to reject the +// following code with the simple definition: +// +// int foo; +// COMPILE_ASSERT(foo, msg); // not supposed to compile as foo is +// // not a compile-time constant. +// +// - By using the type CompileAssert<(bool(expr))>, we ensures that +// expr is a compile-time constant. (Template arguments must be +// determined at compile-time.) +// +// - The outer parentheses in CompileAssert<(bool(expr))> are necessary +// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written +// +// CompileAssert +// +// instead, these compilers will refuse to compile +// +// COMPILE_ASSERT(5 > 0, some_message); +// +// (They seem to think the ">" in "5 > 0" marks the end of the +// template argument list.) +// +// - The array size is (bool(expr) ? 1 : -1), instead of simply +// +// ((expr) ? 1 : -1). +// +// This is to avoid running into a bug in MS VC 7.1, which +// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1. + + +// bit_cast is a template function that implements the +// equivalent of "*reinterpret_cast(&source)". We need this in +// very low-level functions like the protobuf library and fast math +// support. +// +// float f = 3.14159265358979; +// int i = bit_cast(f); +// // i = 0x40490fdb +// +// The classical address-casting method is: +// +// // WRONG +// float f = 3.14159265358979; // WRONG +// int i = * reinterpret_cast(&f); // WRONG +// +// The address-casting method actually produces undefined behavior +// according to ISO C++ specification section 3.10 -15 -. Roughly, this +// section says: if an object in memory has one type, and a program +// accesses it with a different type, then the result is undefined +// behavior for most values of "different type". +// +// This is true for any cast syntax, either *(int*)&f or +// *reinterpret_cast(&f). And it is particularly true for +// conversions betweeen integral lvalues and floating-point lvalues. +// +// The purpose of 3.10 -15- is to allow optimizing compilers to assume +// that expressions with different types refer to different memory. gcc +// 4.0.1 has an optimizer that takes advantage of this. So a +// non-conforming program quietly produces wildly incorrect output. +// +// The problem is not the use of reinterpret_cast. The problem is type +// punning: holding an object in memory of one type and reading its bits +// back using a different type. +// +// The C++ standard is more subtle and complex than this, but that +// is the basic idea. +// +// Anyways ... +// +// bit_cast<> calls memcpy() which is blessed by the standard, +// especially by the example in section 3.9 . Also, of course, +// bit_cast<> wraps up the nasty logic in one place. +// +// Fortunately memcpy() is very fast. In optimized mode, with a +// constant size, gcc 2.95.3, gcc 4.0.1, and msvc 7.1 produce inline +// code with the minimal amount of data movement. On a 32-bit system, +// memcpy(d,s,4) compiles to one load and one store, and memcpy(d,s,8) +// compiles to two loads and two stores. +// +// I tested this code with gcc 2.95.3, gcc 4.0.1, icc 8.1, and msvc 7.1. +// +// WARNING: if Dest or Source is a non-POD type, the result of the memcpy +// is likely to surprise you. + +template +inline Dest bit_cast(const Source& source) { + // Compile time assertion: sizeof(Dest) == sizeof(Source) + // A compile error here means your Dest and Source have different sizes. + typedef char VerifySizesAreEqual[sizeof(Dest) == sizeof(Source) ? 1 : -1]; + + Dest dest; + memcpy(&dest, &source, sizeof(dest)); + return dest; +} +// The following enum should be used only as a constructor argument to indicate +// that the variable has static storage class, and that the constructor should +// do nothing to its state. It indicates to the reader that it is legal to +// declare a static instance of the class, provided the constructor is given +// the base::LINKER_INITIALIZED argument. Normally, it is unsafe to declare a +// static variable that has a constructor or a destructor because invocation +// order is undefined. However, IF the type can be initialized by filling with +// zeroes (which the loader does for static variables), AND the destructor also +// does nothing to the storage, AND there are no virtual methods, then a +// constructor declared as +// explicit MyClass(base::LinkerInitialized x) {} +// and invoked as +// static MyClass my_variable_name(base::LINKER_INITIALIZED); +namespace base { +enum LinkerInitialized { LINKER_INITIALIZED }; + +// Use these to declare and define a static local variable (static T;) so that +// it is leaked so that its destructors are not called at exit. If you need +// thread-safe initialization, use base/lazy_instance.h instead. +#define CR_DEFINE_STATIC_LOCAL(type, name, arguments) \ + static type& name = *new type arguments + +} // namespace base + +#endif // BASE_BASICTYPES_H_ diff --git a/base/bind.h b/base/bind.h new file mode 100644 index 000000000000..be4eab42d032 --- /dev/null +++ b/base/bind.h @@ -0,0 +1,20 @@ +#ifndef BASE_BIND_H_ +#define BASE_BIND_H_ + +#ifndef COBALT_PENDING_CLEAN_UP +#error "TODO: Remove these" +#endif + +#include + +#include "base/functional/bind.h" + +namespace base { +template +inline auto Bind(Functor&& functor, Args&&... args) + -> decltype(BindRepeating(std::forward(functor), std::forward(args)...)) { + return BindRepeating(std::forward(functor), std::forward(args)...); +} +} + +#endif diff --git a/base/bind_helpers.h b/base/bind_helpers.h new file mode 100644 index 000000000000..6330bfc4a4ee --- /dev/null +++ b/base/bind_helpers.h @@ -0,0 +1,34 @@ +#ifndef BASE_BIND_HELPERS_ +#define BASE_BIND_HELPERS_ + +#ifndef COBALT_PENDING_CLEAN_UP +#error "TODO: Remove these" +#endif + +#include "base/functional/callback_helpers.h" + +namespace base { +class BASE_EXPORT DoNothing { + public: + template + operator RepeatingCallback() const { + return Repeatedly(); + } + template + operator OnceCallback() const { + return Once(); + } + // Explicit way of specifying a specific callback type when the compiler can't + // deduce it. + template + static RepeatingCallback Repeatedly() { + return BindRepeating([](Args... /*args*/) {}); + } + template + static OnceCallback Once() { + return BindOnce([](Args... /*args*/) {}); + } +}; +} + +#endif diff --git a/base/bits.h b/base/bits.h index c10ba0c9edb5..f2d2caa1d6c8 100644 --- a/base/bits.h +++ b/base/bits.h @@ -34,6 +34,21 @@ constexpr bool IsPowerOfTwo(T value) { return value > 0 && (value & (value - 1)) == 0; } +#ifdef COBALT_PENDING_CLEAN_UP +// Round up |size| to a multiple of alignment, which must be a power of two. +inline size_t Align(size_t size, size_t alignment) { + DCHECK(IsPowerOfTwo(alignment)); + return (size + alignment - 1) & ~(alignment - 1); +} + +// Round up |size| to a multiple of alignment, which must be a power of two. +inline constexpr size_t AlignUp(size_t size, size_t alignment) { + DCHECK(IsPowerOfTwo(alignment)); + return (size + alignment - 1) & ~(alignment - 1); +} +#endif + + // Round down |size| to a multiple of alignment, which must be a power of two. template >> constexpr T AlignDown(T size, T alignment) { @@ -80,6 +95,56 @@ inline T* AlignUp(T* ptr, uintptr_t alignment) { // TODO(pkasting): When C++20 is available, replace with std::countl_zero() and // similar. +#if defined(COMPILER_MSVC) + +template +ALWAYS_INLINE + typename std::enable_if::value && sizeof(T) <= 4, + unsigned>::type + CountLeadingZeroBits(T x) { + static_assert(bits > 0, "invalid instantiation"); + unsigned long index; + return LIKELY(_BitScanReverse(&index, static_cast(x))) + ? (31 - index - (32 - bits)) + : bits; +} + +template +ALWAYS_INLINE + typename std::enable_if::value && sizeof(T) == 8, + unsigned>::type + CountLeadingZeroBits(T x) { + static_assert(bits > 0, "invalid instantiation"); + unsigned long index; + return LIKELY(_BitScanReverse64(&index, static_cast(x))) + ? (63 - index) + : 64; +} + +template +ALWAYS_INLINE + typename std::enable_if::value && sizeof(T) <= 4, + unsigned>::type + CountTrailingZeroBits(T x) { + static_assert(bits > 0, "invalid instantiation"); + unsigned long index; + return LIKELY(_BitScanForward(&index, static_cast(x))) ? index + : bits; +} + +template +ALWAYS_INLINE + typename std::enable_if::value && sizeof(T) == 8, + unsigned>::type + CountTrailingZeroBits(T x) { + static_assert(bits > 0, "invalid instantiation"); + unsigned long index; + return LIKELY(_BitScanForward64(&index, static_cast(x))) ? index + : 64; +} + +#elif defined(COMPILER_GCC) + // __builtin_clz has undefined behaviour for an input of 0, even though there's // clearly a return value that makes sense, and even though some processor clz // instructions have defined behaviour for 0. We could drop to raw __asm__ to @@ -108,6 +173,8 @@ ALWAYS_INLINE constexpr : bits; } +#endif + // Returns the integer i such as 2^i <= n < 2^(i+1). // // There is a common `BitLength` function, which returns the number of bits @@ -115,12 +182,12 @@ ALWAYS_INLINE constexpr // use `Log2Floor` and add 1 to the result. // // TODO(pkasting): When C++20 is available, replace with std::bit_xxx(). -constexpr int Log2Floor(uint32_t n) { +ALWAYS_INLINE int Log2Floor(uint32_t n) { return 31 - CountLeadingZeroBits(n); } // Returns the integer i such as 2^(i-1) < n <= 2^i. -constexpr int Log2Ceiling(uint32_t n) { +ALWAYS_INLINE int Log2Ceiling(uint32_t n) { // When n == 0, we want the function to return -1. // When n == 0, (n - 1) will underflow to 0xFFFFFFFF, which is // why the statement below starts with (n ? 32 : -1). diff --git a/base/callback.h b/base/callback.h new file mode 100644 index 000000000000..022abccf7e9b --- /dev/null +++ b/base/callback.h @@ -0,0 +1,17 @@ +#ifndef BASE_CALLBACK_H_ +#define BASE_CALLBACK_H_ + +#ifndef COBALT_PENDING_CLEAN_UP +#error "TODO: Remove these" +#endif + +#include "base/functional/callback.h" + +namespace base { +template +using Callback = RepeatingCallback; + +using Closure = Callback; +} + +#endif diff --git a/base/check.cc b/base/check.cc index c9f41c5802d8..89e7894f88a0 100644 --- a/base/check.cc +++ b/base/check.cc @@ -83,7 +83,27 @@ class DCheckLogMessage : public LogMessage { const base::Location location_; }; -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +class DCheckStarboardErrorLogMessage : public StarboardErrorLogMessage { + public: + DCheckStarboardErrorLogMessage(const base::Location& location, + LogSeverity severity, + SystemErrorCode err) + : StarboardErrorLogMessage(location.file_name(), + location.line_number(), + severity, + err), + location_(location) {} + ~DCheckStarboardErrorLogMessage() override { + if (severity() != logging::LOGGING_FATAL) { + DCheckDumpWithoutCrashing(this, location_); + } + } + + private: + const base::Location location_; +}; +#elif BUILDFLAG(IS_WIN) class DCheckWin32ErrorLogMessage : public Win32ErrorLogMessage { public: DCheckWin32ErrorLogMessage(const base::Location& location, @@ -146,7 +166,10 @@ CheckError CheckError::PCheck(const char* file, int line, const char* condition) { SystemErrorCode err_code = logging::GetLastSystemErrorCode(); -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) + auto* const log_message = + new StarboardErrorLogMessage(file, line, LOGGING_FATAL, err_code); +#elif BUILDFLAG(IS_WIN) auto* const log_message = new Win32ErrorLogMessage(file, line, LOGGING_FATAL, err_code); #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) @@ -164,7 +187,10 @@ CheckError CheckError::PCheck(const char* file, int line) { CheckError CheckError::DPCheck(const char* condition, const base::Location& location) { SystemErrorCode err_code = logging::GetLastSystemErrorCode(); -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) + auto* const log_message = + new DCheckStarboardErrorLogMessage(location, LOGGING_DCHECK, err_code); +#elif BUILDFLAG(IS_WIN) auto* const log_message = new DCheckWin32ErrorLogMessage(location, LOGGING_DCHECK, err_code); #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) diff --git a/base/check_unittest.cc b/base/check_unittest.cc index 2f328f2fae39..cc23927dfb1c 100644 --- a/base/check_unittest.cc +++ b/base/check_unittest.cc @@ -81,6 +81,7 @@ TEST(CheckDeathTest, Basics) { CHECK_LT(a, b) << "custom message"); } +#if !defined(STARBOARD) TEST(CheckDeathTest, PCheck) { const char file[] = "/nonexistentfile123"; std::ignore = fopen(file, "r"); @@ -111,6 +112,7 @@ TEST(CheckDeathTest, PCheck) { err, DPCHECK(fopen(file, "r") != nullptr) << "foo"); } +#endif TEST(CheckDeathTest, CheckOp) { int a = 1, b = 2; @@ -185,6 +187,8 @@ class ScopedDcheckSeverity { }; #endif // BUILDFLAG(DCHECK_IS_CONFIGURABLE) +#if !defined(STARBOARD) + // https://crbug.com/709067 tracks test flakiness on iOS. #if BUILDFLAG(IS_IOS) #define MAYBE_Dcheck DISABLED_Dcheck @@ -261,6 +265,8 @@ TEST(CheckDeathTest, MAYBE_Dcheck) { DCHECK_EQ(mp2, &MemberFunctions::MemberFunction1)); } +#endif + TEST(CheckTest, DcheckReleaseBehavior) { int var1 = 1; int var2 = 2; @@ -513,18 +519,21 @@ void NiLogOnce() { NOTIMPLEMENTED_LOG_ONCE(); } +#if !defined(STARBOARD) || !defined(COMPILER_MSVC) TEST(CheckTest, NotImplementedLogOnce) { static const std::string expected_msg = "Not implemented reached in void (anonymous namespace)::NiLogOnce()\n"; #if DCHECK_IS_ON() - EXPECT_LOG_ERROR(__LINE__ - 8, NiLogOnce(), expected_msg); + // In Starboard, we account for add lines (macros and comments). + EXPECT_LOG_ERROR(__LINE__ - 10, NiLogOnce(), expected_msg); EXPECT_NO_LOG(NiLogOnce()); #else EXPECT_NO_LOG(NiLogOnce()); EXPECT_NO_LOG(NiLogOnce()); #endif } +#endif // Test CHECK_DEREF of `T*` TEST(CheckTest, CheckDerefOfPointer) { diff --git a/base/command_line.cc b/base/command_line.cc index 0ff6e0c111e6..64f9b4d63bb1 100644 --- a/base/command_line.cc +++ b/base/command_line.cc @@ -50,7 +50,7 @@ constexpr CommandLine::CharType kSwitchValueSeparator[] = // value by changing the value of switch_prefix_count to be one less than // the array size. constexpr CommandLine::StringPieceType kSwitchPrefixes[] = {L"--", L"-", L"/"}; -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) // Unixes don't use slash as a switch. constexpr CommandLine::StringPieceType kSwitchPrefixes[] = {"--", "-"}; #endif @@ -226,7 +226,7 @@ bool CommandLine::Init(int argc, const char* const* argv) { current_process_commandline_ = new CommandLine(NO_PROGRAM); #if BUILDFLAG(IS_WIN) current_process_commandline_->ParseFromString(::GetCommandLineW()); -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) current_process_commandline_->InitFromArgv(argc, argv); #else #error Unsupported platform @@ -288,7 +288,7 @@ void CommandLine::SetProgram(const FilePath& program) { #endif #if BUILDFLAG(IS_WIN) argv_[0] = StringType(TrimWhitespace(program.value(), TRIM_ALL)); -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) TrimWhitespaceASCII(program.value(), TRIM_ALL, &argv_[0]); #else #error Unsupported platform @@ -308,7 +308,7 @@ std::string CommandLine::GetSwitchValueASCII(StringPiece switch_string) const { StringType value = GetSwitchValueNative(switch_string); #if BUILDFLAG(IS_WIN) if (!IsStringASCII(base::AsStringPiece16(value))) { -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) if (!IsStringASCII(value)) { #endif DLOG(WARNING) << "Value of switch (" << switch_string << ") must be ASCII."; @@ -316,7 +316,7 @@ std::string CommandLine::GetSwitchValueASCII(StringPiece switch_string) const { } #if BUILDFLAG(IS_WIN) return WideToUTF8(value); -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) return value; #endif } @@ -349,7 +349,7 @@ void CommandLine::AppendSwitchNative(StringPiece switch_string, #if BUILDFLAG(IS_WIN) const std::string switch_key = ToLowerASCII(switch_string); StringType combined_switch_string(UTF8ToWide(switch_key)); -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) StringPiece switch_key = switch_string; StringType combined_switch_string(switch_key); #endif @@ -378,7 +378,7 @@ void CommandLine::AppendSwitchASCII(StringPiece switch_string, StringPiece value_string) { #if BUILDFLAG(IS_WIN) AppendSwitchNative(switch_string, UTF8ToWide(value_string)); -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) AppendSwitchNative(switch_string, value_string); #else #error Unsupported platform @@ -391,7 +391,7 @@ void CommandLine::RemoveSwitch(base::StringPiece switch_key_without_prefix) { #endif #if BUILDFLAG(IS_WIN) StringType switch_key_native = UTF8ToWide(switch_key_without_prefix); -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) StringType switch_key_native(switch_key_without_prefix); #endif @@ -443,7 +443,7 @@ void CommandLine::AppendArg(StringPiece value) { #if BUILDFLAG(IS_WIN) DCHECK(IsStringUTF8(value)); AppendArgNative(UTF8ToWide(value)); -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) AppendArgNative(value); #else #error Unsupported platform @@ -539,7 +539,7 @@ void CommandLine::AppendSwitchesAndArguments( CommandLine::StringType arg = argv[i]; #if BUILDFLAG(IS_WIN) arg = CommandLine::StringType(TrimWhitespace(arg, TRIM_ALL)); -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) TrimWhitespaceASCII(arg, TRIM_ALL, &arg); #endif @@ -554,7 +554,7 @@ void CommandLine::AppendSwitchesAndArguments( return; } AppendSwitchNative(WideToUTF8(switch_string), switch_value); -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) AppendSwitchNative(switch_string, switch_value); #else #error Unsupported platform diff --git a/base/command_line.h b/base/command_line.h index 90034c859a00..784c2bf25dca 100644 --- a/base/command_line.h +++ b/base/command_line.h @@ -42,7 +42,7 @@ class BASE_EXPORT CommandLine { #if BUILDFLAG(IS_WIN) // The native command line string type. using StringType = std::wstring; -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) using StringType = std::string; #endif diff --git a/base/command_line_unittest.cc b/base/command_line_unittest.cc index 8ce0ed053cad..909d6acc21fe 100644 --- a/base/command_line_unittest.cc +++ b/base/command_line_unittest.cc @@ -223,7 +223,7 @@ TEST(CommandLineTest, GetArgumentsString) { CommandLine::StringType expected_second_arg(UTF8ToWide(kSecondArgName)); CommandLine::StringType expected_third_arg(UTF8ToWide(kThirdArgName)); CommandLine::StringType expected_fourth_arg(UTF8ToWide(kFourthArgName)); -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) CommandLine::StringType expected_first_arg(kFirstArgName); CommandLine::StringType expected_second_arg(kSecondArgName); CommandLine::StringType expected_third_arg(kThirdArgName); diff --git a/base/compiler_specific.h b/base/compiler_specific.h index 26eb69dce4e5..90942f47a64a 100644 --- a/base/compiler_specific.h +++ b/base/compiler_specific.h @@ -7,7 +7,7 @@ #include "build/build_config.h" -#if defined(COMPILER_MSVC) && !defined(__clang__) +#if defined(COMPILER_MSVC) && !defined(__clang__) && !defined(USE_COBALT_CUSTOMIZATIONS) #error "Only clang-cl is supported on Windows, see https://crbug.com/988071" #endif @@ -228,6 +228,34 @@ #define HAS_FEATURE(FEATURE) 0 #endif +#ifdef COBALT_PENDING_CLEAN_UP +#if defined(COMPILER_MSVC) +// MSVC_SUPPRESS_WARNING disables warning |n| for the remainder of the line and +// for the next line of the source file. +#define MSVC_SUPPRESS_WARNING(n) __pragma(warning(suppress:n)) + +// MSVC_PUSH_DISABLE_WARNING pushes |n| onto a stack of warnings to be disabled. +// The warning remains disabled until popped by MSVC_POP_WARNING. +#define MSVC_PUSH_DISABLE_WARNING(n) __pragma(warning(push)) \ + __pragma(warning(disable:n)) + +// Pop effects of innermost MSVC_PUSH_* macro. +#define MSVC_POP_WARNING() __pragma(warning(pop)) + +#define ALLOW_THIS_IN_INITIALIZER_LIST(code) \ + MSVC_PUSH_DISABLE_WARNING(4355) \ + code MSVC_POP_WARNING() +#else +#define _Printf_format_string_ +#define MSVC_SUPPRESS_WARNING(n) +#define MSVC_PUSH_DISABLE_WARNING(n) +#define MSVC_POP_WARNING() +#define MSVC_DISABLE_OPTIMIZE() +#define MSVC_ENABLE_OPTIMIZE() +#define ALLOW_THIS_IN_INITIALIZER_LIST(code) code +#endif +#endif + #if defined(COMPILER_GCC) #define PRETTY_FUNCTION __PRETTY_FUNCTION__ #elif defined(COMPILER_MSVC) @@ -414,4 +442,16 @@ inline constexpr bool AnalyzerAssumeTrue(bool arg) { #define LOGICALLY_CONST #endif +// Macro for telling -Wimplicit-fallthrough that a fallthrough is intentional. +#if defined(__clang__) +#define FALLTHROUGH [[clang::fallthrough]] +#else +#define FALLTHROUGH +#endif + +#if defined(COBALT_PENDING_CLEAN_UP) +#define ALLOW_UNUSED_TYPE +#define WARN_UNUSED_RESULT +#endif + #endif // BASE_COMPILER_SPECIFIC_H_ diff --git a/base/component_export.h b/base/component_export.h index cc468b80d90f..c2c0bf210d16 100644 --- a/base/component_export.h +++ b/base/component_export.h @@ -69,8 +69,9 @@ // Helper which simply selects its third argument. Used in conjunction with // |COMPONENT_MACRO_CONDITIONAL_COMMA_()| above to implement conditional macro // expansion. +#define CR_EXPAND_ARG(arg) arg #define COMPONENT_MACRO_SELECT_THIRD_ARGUMENT_(...) \ - COMPONENT_MACRO_SELECT_THIRD_ARGUMENT_IMPL_(__VA_ARGS__) + CR_EXPAND_ARG(COMPONENT_MACRO_SELECT_THIRD_ARGUMENT_IMPL_(__VA_ARGS__)) #define COMPONENT_MACRO_SELECT_THIRD_ARGUMENT_IMPL_(a, b, c, ...) c #endif // BASE_COMPONENT_EXPORT_H_ diff --git a/base/containers/checked_iterators.h b/base/containers/checked_iterators.h index 0c98b8fab4d0..64f23aac90ad 100644 --- a/base/containers/checked_iterators.h +++ b/base/containers/checked_iterators.h @@ -31,7 +31,12 @@ class CheckedContiguousIterator { // Required for certain libc++ algorithm optimizations that are not available // for NaCl. -#if defined(_LIBCPP_VERSION) && !BUILDFLAG(IS_NACL) +// TODO: b/326979654 -- Remove this when we have -stdlib=libc++ defined in +// all Cobalt toolchains. +#if (defined(_LIBCPP_VERSION) && !BUILDFLAG(IS_NACL)) || \ + defined(COBALT_PENDING_CLEAN_UP) + // Required to be able to get to the underlying pointer without triggering + // CHECK failures. template friend struct std::pointer_traits; #endif @@ -224,7 +229,7 @@ using CheckedContiguousConstIterator = CheckedContiguousIterator; } // namespace base -#if defined(_LIBCPP_VERSION) && !BUILDFLAG(IS_NACL) +#if defined(_LIBCPP_VERSION) && !BUILDFLAG(IS_NACL) && !defined(STARBOARD) // Specialize both std::__is_cpp17_contiguous_iterator and std::pointer_traits // for CCI in case we compile with libc++ outside of NaCl. The former is // required to enable certain algorithm optimizations (e.g. std::copy can be a @@ -267,6 +272,29 @@ struct pointer_traits<::base::CheckedContiguousIterator> { }; } // namespace std +// TODO: b/326979654 -- Remove this when we have -stdlib=libc++ defined in +// all Cobalt toolchains. +#elif defined(COBALT_PENDING_CLEAN_UP) +// Specialize std::pointer_traits so that we can obtain the underlying raw +// pointer without resulting in CHECK failures. The important bit is the +// `to_address(pointer)` overload, which is the standard blessed way to +// customize `std::to_address(pointer)` in C++20 [1]. +// +// [1] https://wg21.link/pointer.traits.optmem +template +struct std::pointer_traits<::base::CheckedContiguousIterator> { + using pointer = ::base::CheckedContiguousIterator; + using element_type = T; + using difference_type = ptrdiff_t; + template + using rebind = ::base::CheckedContiguousIterator; + static constexpr pointer pointer_to(element_type& r) noexcept { + return pointer(&r, &r); + } + static constexpr element_type* to_address(pointer p) noexcept { + return p.current_; + } +}; #endif #endif // BASE_CONTAINERS_CHECKED_ITERATORS_H_ diff --git a/base/containers/checked_iterators_unittest.cc b/base/containers/checked_iterators_unittest.cc index 4d7cffa16303..3ec260b7363b 100644 --- a/base/containers/checked_iterators_unittest.cc +++ b/base/containers/checked_iterators_unittest.cc @@ -89,7 +89,9 @@ TEST(CheckedContiguousIterator, ConvertingComparisonOperators) { // lags a bit behind. // TODO(crbug.com/1166360): Enable this test on ChromeOS once the shared libc++ // is sufficiently modern. -#if defined(_LIBCPP_VERSION) && !BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_CHROMEOS) +// Starboard(Cobalt) has platforms such as ATV that do not support std::copy +// optimization. +#if defined(_LIBCPP_VERSION) && !BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_CHROMEOS) && !defined(STARBOARD) namespace { // Helper template that wraps an iterator and disables its dereference and diff --git a/base/containers/flat_tree.h b/base/containers/flat_tree.h index abb02d53b631..c57b8858d379 100644 --- a/base/containers/flat_tree.h +++ b/base/containers/flat_tree.h @@ -721,7 +721,11 @@ auto flat_tree::begin() template constexpr auto flat_tree::begin() const -> const_iterator { +#if defined(STARBOARD) + return body_.begin(); +#else return ranges::begin(body_); +#endif } template @@ -738,7 +742,11 @@ auto flat_tree::end() -> iterator { template constexpr auto flat_tree::end() const -> const_iterator { +#if defined(STARBOARD) + return body_.end(); +#else return ranges::end(body_); +#endif } template diff --git a/base/containers/hash_tables.h b/base/containers/hash_tables.h new file mode 100644 index 000000000000..eb5a0e4294a9 --- /dev/null +++ b/base/containers/hash_tables.h @@ -0,0 +1,33 @@ +#ifndef BASE_CONTAINERS_HASH_MAP_H_ +#define BASE_CONTAINERS_HASH_MAP_H_ + +#ifndef COBALT_PENDING_CLEAN_UP +#error "TODO: Remove these" +#endif + +#include +#include +#include + +#include "starboard/configuration.h" + +// namespace std { +// template +// struct hash { +// std::size_t operator()(T* value) const { +// return std::hash()( +// reinterpret_cast(value)); +// } +// }; +// } // namespace std + +namespace base { +template , + class KeyEqual = std::equal_to> +using hash_map = std::unordered_map; +template , class KeyEqual = std::equal_to> +using hash_set = std::unordered_set; +} +#endif diff --git a/base/containers/id_map.h b/base/containers/id_map.h index 042a21881c05..8dacf87124ec 100644 --- a/base/containers/id_map.h +++ b/base/containers/id_map.h @@ -14,6 +14,7 @@ #include #include #include +#include #include "base/check.h" #include "base/check_op.h" diff --git a/base/containers/stack_container_unittest.cc b/base/containers/stack_container_unittest.cc index 68c6321824d4..b5932121062e 100644 --- a/base/containers/stack_container_unittest.cc +++ b/base/containers/stack_container_unittest.cc @@ -206,9 +206,12 @@ TEST(StackContainer, CustomAllocator) { EXPECT_EQ(0, Allocator::deallocated); v->clear(); +// |v->shrink_to_fit()| returns before |deallocate()| is called. +#if !defined(STARBOARD) // shrink_to_fit() makes sure to destroy empty backing store. v->shrink_to_fit(); EXPECT_EQ(1, Allocator::deallocated); +#endif } } // namespace base diff --git a/base/debug/asan_service.cc b/base/debug/asan_service.cc index 1ec961a453a0..eafa6a2739a3 100644 --- a/base/debug/asan_service.cc +++ b/base/debug/asan_service.cc @@ -6,6 +6,7 @@ #if defined(ADDRESS_SANITIZER) #include +#include #include "base/debug/task_trace.h" #include "base/no_destructor.h" diff --git a/base/debug/debugger_starboard.cc b/base/debug/debugger_starboard.cc new file mode 100644 index 000000000000..1dbd3a1a9cb6 --- /dev/null +++ b/base/debug/debugger_starboard.cc @@ -0,0 +1,43 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "base/debug/stack_trace.h" + +#include "base/notreached.h" +#include "starboard/system.h" + +namespace base { +namespace debug { + +bool SpawnDebuggerOnProcess(unsigned process_id) { + NOTIMPLEMENTED(); + return false; +} + +bool BeingDebugged() { +#if defined(COBALT_BUILD_TYPE_GOLD) + return false; +#else + return SbSystemIsDebuggerAttached(); +#endif +} + +void BreakDebuggerAsyncSafe() { + SbSystemBreakIntoDebugger(); +} + +void VerifyDebugger() {} + +} // namespace debug +} // namespace base diff --git a/base/debug/leak_annotations.h b/base/debug/leak_annotations.h index 506e1e01daee..f18aa3821f41 100644 --- a/base/debug/leak_annotations.h +++ b/base/debug/leak_annotations.h @@ -18,7 +18,7 @@ // ANNOTATE_LEAKING_OBJECT_PTR(X): the heap object referenced by pointer X will // be annotated as a leak. -#if defined(LEAK_SANITIZER) && !BUILDFLAG(IS_NACL) +#if defined(LEAK_SANITIZER) && !BUILDFLAG(IS_NACL) || defined(STARBOARD) && defined(ADDRESS_SANITIZER) #include diff --git a/base/debug/stack_trace.h b/base/debug/stack_trace.h index ef4a1c665968..1ffe0844c6bd 100644 --- a/base/debug/stack_trace.h +++ b/base/debug/stack_trace.h @@ -245,7 +245,7 @@ class BASE_EXPORT ScopedStackFrameLinker { namespace internal { -#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) +#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) || defined(STARBOARD) // POSIX doesn't define any async-signal safe function for converting // an integer to ASCII. We'll have to define our own version. // itoa_r() converts a (signed) integer to ASCII. It returns "buf", if the diff --git a/base/debug/stack_trace_starboard.cc b/base/debug/stack_trace_starboard.cc new file mode 100644 index 000000000000..c7e9729626f6 --- /dev/null +++ b/base/debug/stack_trace_starboard.cc @@ -0,0 +1,198 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "base/debug/stack_trace.h" + +#include +#include + +#include "starboard/common/log.h" +#include "starboard/system.h" + +namespace base { +namespace debug { + +namespace { + +class BacktraceOutputHandler { + public: + virtual void HandleOutput(const char* output) = 0; + + protected: + virtual ~BacktraceOutputHandler() {} +}; + +class PrintBacktraceOutputHandler : public BacktraceOutputHandler { + public: + PrintBacktraceOutputHandler() {} + + virtual void HandleOutput(const char* output) { + // NOTE: This code MUST be async-signal safe (it's used by in-process + // stack dumping signal handler). NO malloc or stdio is allowed here. + SbLogRaw(output); + } + + private: + PrintBacktraceOutputHandler(const PrintBacktraceOutputHandler&) = delete; + PrintBacktraceOutputHandler& operator=(const PrintBacktraceOutputHandler&) = delete; +}; + +class StreamBacktraceOutputHandler : public BacktraceOutputHandler { + public: + StreamBacktraceOutputHandler(std::ostream* os) : os_(os) {} + + virtual void HandleOutput(const char* output) { (*os_) << output; } + + private: + std::ostream* os_; + + StreamBacktraceOutputHandler(const StreamBacktraceOutputHandler&) = delete; + StreamBacktraceOutputHandler& operator=(const StreamBacktraceOutputHandler&) = delete; +}; + +void OutputPointer(void* pointer, BacktraceOutputHandler* handler) { + char buf[1024] = {'\0'}; + handler->HandleOutput(" [0x"); + internal::itoa_r(reinterpret_cast(pointer), buf, sizeof(buf), 16, 0); + handler->HandleOutput(buf); + handler->HandleOutput("]"); +} + +void ProcessBacktrace(void* const* trace, + int size, + const char* prefix_string, + BacktraceOutputHandler* handler) { + for (int i = 0; i < size; ++i) { + if (prefix_string) + handler->HandleOutput(prefix_string); + handler->HandleOutput("\t"); + + char buf[1024] = {'\0'}; + + // Subtract by one as return address of function may be in the next + // function when a function is annotated as noreturn. + void* address = static_cast(trace[i]) - 1; + if (SbSystemSymbolize(address, buf, sizeof(buf))) { + handler->HandleOutput(buf); + } else { + handler->HandleOutput(""); + } + + OutputPointer(trace[i], handler); + handler->HandleOutput("\n"); + } +} + +} // namespace + +namespace internal { + +// NOTE: code from sandbox/linux/seccomp-bpf/demo.cc. +char* itoa_r(intptr_t i, char* buf, size_t sz, int base, size_t padding) { + // Make sure we can write at least one NUL byte. + size_t n = 1; + if (n > sz) + return NULL; + + if (base < 2 || base > 16) { + buf[0] = '\000'; + return NULL; + } + + char* start = buf; + + uintptr_t j = i; + + // Handle negative numbers (only for base 10). + if (i < 0 && base == 10) { + j = -i; + + // Make sure we can write the '-' character. + if (++n > sz) { + buf[0] = '\000'; + return NULL; + } + *start++ = '-'; + } + + // Loop until we have converted the entire number. Output at least one + // character (i.e. '0'). + char* ptr = start; + do { + // Make sure there is still enough space left in our output buffer. + if (++n > sz) { + buf[0] = '\000'; + return nullptr; + } + + // Output the next digit. + *ptr++ = "0123456789abcdef"[j % static_cast(base)]; + j /= static_cast(base); + + if (padding > 0) + padding--; + } while (j > 0 || padding > 0); + + // Terminate the output with a NUL character. + *ptr = '\000'; + + // Conversion to ASCII actually resulted in the digits being in reverse + // order. We can't easily generate them in forward order, as we can't tell + // the number of characters needed until we are done converting. + // So, now, we reverse the string (except for the possible "-" sign). + while (--ptr > start) { + char ch = *ptr; + *ptr = *start; + *start++ = ch; + } + return buf; +} + +} // namespace internal + +size_t CollectStackTrace(void** trace, size_t count) { + // NOTE: This code MUST be async-signal safe (it's used by in-process + // stack dumping signal handler). NO malloc or stdio is allowed here. + + // Though the SbSystemGetStack API documentation does not specify any possible + // negative return values, we take no chance. + count = std::max(SbSystemGetStack(trace, count), 0); + + // We can remove this call from the stack trace, since we know it is always + // going to be in it. + for (size_t i = 1; i < count; ++i) { + trace[i - 1] = trace[i]; + } + return count; +} + +void StackTrace::PrintWithPrefix(const char* prefix_string) const { + // NOTE: This code MUST be async-signal safe (it's used by in-process + // stack dumping signal handler). NO malloc or stdio is allowed here. + PrintBacktraceOutputHandler handler; + ProcessBacktrace(trace_, count_, prefix_string, &handler); +} + +void StackTrace::OutputToStreamWithPrefix(std::ostream* os, + const char* prefix_string) const { + StreamBacktraceOutputHandler handler(os); + ProcessBacktrace(trace_, count_, prefix_string, &handler); +} + +bool EnableInProcessStackDumping() { + return true; +} + +} // namespace debug +} // namespace base diff --git a/base/debug/stack_trace_unittest.cc b/base/debug/stack_trace_unittest.cc index 4759f2791829..74793e5e4e56 100644 --- a/base/debug/stack_trace_unittest.cc +++ b/base/debug/stack_trace_unittest.cc @@ -33,7 +33,7 @@ typedef MultiProcessTest StackTraceTest; typedef testing::Test StackTraceTest; #endif -#if !defined(__UCLIBC__) && !defined(_AIX) +#if !defined(__UCLIBC__) && !defined(_AIX) && !defined(COMPILER_MSVC) // StackTrace::OutputToStream() is not implemented under uclibc, nor AIX. // See https://crbug.com/706728 @@ -157,7 +157,8 @@ TEST_F(StackTraceTest, DebugOutputToStreamWithNullPrefix) { #endif // !defined(__UCLIBC__) && !defined(_AIX) #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) -#if !BUILDFLAG(IS_IOS) +// Starboard does not support relative file paths, needed by |SpawnChild()|. +#if !BUILDFLAG(IS_IOS) && !defined(STARBOARD) static char* newArray() { // Clang warns about the mismatched new[]/delete if they occur in the same // function. @@ -348,7 +349,8 @@ TEST_F(StackTraceTest, MAYBE_TraceStackFramePointers) { // sometimes we read fp / pc from the place that previously held // uninitialized value. // TODO(crbug.com/1132511): Enable this test on Fuchsia. -#if defined(MEMORY_SANITIZER) || BUILDFLAG(IS_FUCHSIA) +#if defined(MEMORY_SANITIZER) || BUILDFLAG(IS_FUCHSIA) || \ + defined(STARBOARD) && defined(ADDRESS_SANITIZER) || SB_IS(EVERGREEN) #define MAYBE_TraceStackFramePointersFromBuffer \ DISABLED_TraceStackFramePointersFromBuffer #else @@ -421,6 +423,7 @@ TEST(CheckExitCodeAfterSignalHandlerDeathTest, #endif // #if !defined(ADDRESS_SANITIZER) && !defined(UNDEFINED_SANITIZER) +#if !defined(STARBOARD) TEST(CheckExitCodeAfterSignalHandlerDeathTest, CheckSIGILL) { auto const raise_sigill = []() { #if defined(ARCH_CPU_X86_FAMILY) @@ -434,6 +437,7 @@ TEST(CheckExitCodeAfterSignalHandlerDeathTest, CheckSIGILL) { EXPECT_EXIT(raise_sigill(), ::testing::KilledBySignal(SIGILL), ""); } +#endif // !defined(STARBOARD) #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) diff --git a/base/environment.cc b/base/environment.cc index f3019d317c43..d0db2cc86b3b 100644 --- a/base/environment.cc +++ b/base/environment.cc @@ -72,6 +72,8 @@ class EnvironmentImpl : public Environment { if (result) *result = env_value; return true; +#else + return false; #endif } @@ -83,6 +85,8 @@ class EnvironmentImpl : public Environment { #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) // On success, zero is returned. return !setenv(variable_name.data(), new_value.c_str(), 1); +#else + return false; #endif } @@ -93,6 +97,8 @@ class EnvironmentImpl : public Environment { #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) // On success, zero is returned. return !unsetenv(variable_name.data()); +#else + return false; #endif } }; diff --git a/base/environment.h b/base/environment.h index 894236b7355b..4a681c8b6cb1 100644 --- a/base/environment.h +++ b/base/environment.h @@ -49,7 +49,7 @@ class BASE_EXPORT Environment { #if BUILDFLAG(IS_WIN) using NativeEnvironmentString = std::wstring; -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) using NativeEnvironmentString = std::string; #endif using EnvironmentMap = diff --git a/base/export_template.h b/base/export_template.h index 5ce61094aaab..f3c9fa487338 100644 --- a/base/export_template.h +++ b/base/export_template.h @@ -85,8 +85,9 @@ // EXPORT_TEMPLATE_STYLE_MATCH_DECLSPEC_dllimport // DEFAULT #define EXPORT_TEMPLATE_STYLE(foo_export) EXPORT_TEMPLATE_STYLE_2(foo_export) +#define CR_EXPAND_ARG(arg) arg #define EXPORT_TEMPLATE_STYLE_2(foo_export) \ - EXPORT_TEMPLATE_STYLE_MATCH_foj3FJo5StF0OvIzl7oMxA##foo_export + CR_EXPAND_ARG(EXPORT_TEMPLATE_STYLE_MATCH_foj3FJo5StF0OvIzl7oMxA##foo_export) // Internal helper macros for EXPORT_TEMPLATE_STYLE. // diff --git a/base/feature_list_unittest.cc b/base/feature_list_unittest.cc index 0f9e6e02d648..3899113db394 100644 --- a/base/feature_list_unittest.cc +++ b/base/feature_list_unittest.cc @@ -625,6 +625,7 @@ TEST_F(FeatureListTest, UninitializedInstance_IsEnabledReturnsFalse) { FeatureList::RestoreInstanceForTesting(std::move(original_feature_list)); } +#if !defined(STARBOARD) TEST_F(FeatureListTest, StoreAndRetrieveFeaturesFromSharedMemory) { std::unique_ptr feature_list(new base::FeatureList); @@ -690,6 +691,8 @@ TEST_F(FeatureListTest, StoreAndRetrieveAssociatedFeaturesFromSharedMemory) { EXPECT_EQ(associated_trial2, trial2); } +#endif // !defined(STARBOARD) + #if BUILDFLAG(ENABLE_BANNED_BASE_FEATURE_PREFIX) && \ defined(GTEST_HAS_DEATH_TEST) using FeatureListDeathTest = FeatureListTest; diff --git a/base/files/file.cc b/base/files/file.cc index ae486b403a8c..aa4e59563d78 100644 --- a/base/files/file.cc +++ b/base/files/file.cc @@ -16,7 +16,9 @@ #include "base/trace_event/base_tracing.h" #include "build/build_config.h" -#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#if defined(STARBOARD) +#include "starboard/types.h" +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) #include #endif @@ -41,7 +43,8 @@ File::File(PlatformFile platform_file) : File(platform_file, false) {} File::File(ScopedPlatformFile platform_file, bool async) : file_(std::move(platform_file)), error_details_(FILE_OK), async_(async) { -#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#if defined(STARBOARD) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) DCHECK_GE(file_.get(), -1); #endif } @@ -50,7 +53,8 @@ File::File(PlatformFile platform_file, bool async) : file_(platform_file), error_details_(FILE_OK), async_(async) { -#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#if defined(STARBOARD) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) DCHECK_GE(platform_file, -1); #endif } @@ -59,10 +63,19 @@ File::File(Error error_details) : error_details_(error_details) {} File::File(File&& other) : file_(other.TakePlatformFile()), +#if defined(STARBOARD) + file_name_(other.file_name_), +#endif tracing_path_(other.tracing_path_), error_details_(other.error_details()), created_(other.created()), - async_(other.async_) {} + async_(other.async_) +#if defined(STARBOARD) + , + append_(other.append_) +#endif +{ +} File::~File() { // Go through the AssertIOAllowed logic. @@ -76,13 +89,18 @@ File& File::operator=(File&& other) { error_details_ = other.error_details(); created_ = other.created(); async_ = other.async_; +#if defined(STARBOARD) + file_name_ = other.file_name_; + append_ = other.append_; +#endif return *this; } #if !BUILDFLAG(IS_NACL) void File::Initialize(const FilePath& path, uint32_t flags) { if (path.ReferencesParent()) { -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +#elif BUILDFLAG(IS_WIN) ::SetLastError(ERROR_ACCESS_DENIED); #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) errno = EACCES; @@ -95,6 +113,9 @@ void File::Initialize(const FilePath& path, uint32_t flags) { if (FileTracing::IsCategoryEnabled()) tracing_path_ = path; SCOPED_FILE_TRACE("Initialize"); +#if defined(STARBOARD) + file_name_= path.AsUTF8Unsafe(); +#endif DoInitialize(path, flags); } #endif diff --git a/base/files/file.h b/base/files/file.h index 35abbc2e4cb2..e1b57553f94f 100644 --- a/base/files/file.h +++ b/base/files/file.h @@ -22,7 +22,11 @@ struct stat; namespace base { +#if defined(STARBOARD) +using stat_wrapper_t = struct ::stat; +#else using stat_wrapper_t = struct stat; +#endif // Thin wrapper around an OS-level file. // Note that this class does not provide any support for asynchronous IO, other @@ -118,7 +122,7 @@ class BASE_EXPORT File { struct BASE_EXPORT Info { Info(); ~Info(); -#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) // Fills this struct with values from |stat_info|. void FromStat(const stat_wrapper_t& stat_info); #endif @@ -353,7 +357,9 @@ class BASE_EXPORT File { bool DeleteOnClose(bool delete_on_close); #endif -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) + static Error OSErrorToFileError(SbSystemError sb_system_error); +#elif BUILDFLAG(IS_WIN) static Error OSErrorToFileError(DWORD last_error); #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) static Error OSErrorToFileError(int saved_errno); @@ -368,11 +374,14 @@ class BASE_EXPORT File { // Converts an error value to a human-readable form. Used for logging. static std::string ErrorToString(Error error); -#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) // Wrapper for stat() or stat64(). static int Stat(const char* path, stat_wrapper_t* sb); static int Fstat(int fd, stat_wrapper_t* sb); +# if !defined(STARBOARD) + // Starboard does not support lstat yet. static int Lstat(const char* path, stat_wrapper_t* sb); +#endif #endif // This function can be used to augment `flags` with the correct flags @@ -389,6 +398,12 @@ class BASE_EXPORT File { return flags; } +#if defined(STARBOARD) + std::string GetFileName() { + return file_name_; + } +#endif + private: friend class FileTracing::ScopedTrace; @@ -410,6 +425,12 @@ class BASE_EXPORT File { Error error_details_ = FILE_ERROR_FAILED; bool created_ = false; bool async_ = false; + +#if defined(STARBOARD) + bool delete_on_close_; + std::string file_name_; + bool append_ = false; +#endif }; } // namespace base diff --git a/base/files/file_enumerator.h b/base/files/file_enumerator.h index cbcfcafbaf97..b970055bc8aa 100644 --- a/base/files/file_enumerator.h +++ b/base/files/file_enumerator.h @@ -17,7 +17,11 @@ #include "base/time/time.h" #include "build/build_config.h" -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +#include +#include +#include "starboard/file.h" +#elif BUILDFLAG(IS_WIN) #include "base/win/windows_types.h" #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) #include @@ -58,25 +62,25 @@ class BASE_EXPORT FileEnumerator { // On POSIX systems, this is rounded down to the second. Time GetLastModifiedTime() const; -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) || BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) + const stat_wrapper_t& stat() const { return stat_; } +#elif BUILDFLAG(IS_WIN) // Note that the cAlternateFileName (used to hold the "short" 8.3 name) // of the WIN32_FIND_DATA will be empty. Since we don't use short file // names, we tell Windows to omit it which speeds up the query slightly. const WIN32_FIND_DATA& find_data() const { return *ChromeToWindowsType(&find_data_); } -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) - const stat_wrapper_t& stat() const { return stat_; } #endif private: friend class FileEnumerator; -#if BUILDFLAG(IS_WIN) - CHROME_WIN32_FIND_DATA find_data_; -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#if defined(STARBOARD) || BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) stat_wrapper_t stat_; FilePath filename_; +#elif BUILDFLAG(IS_WIN) + CHROME_WIN32_FIND_DATA find_data_; #endif }; @@ -92,7 +96,8 @@ class BASE_EXPORT FileEnumerator { // called. NAMES_ONLY = 1 << 3, -#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#if defined(STARBOARD) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) SHOW_SYM_LINKS = 1 << 4, #endif }; @@ -188,7 +193,15 @@ class BASE_EXPORT FileEnumerator { bool IsPatternMatched(const FilePath& src) const; -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) + std::vector ReadDirectory(const FilePath& source); + + // The files in the current directory + std::vector directory_entries_; + + // The next entry to use from the directory_entries_ vector + size_t current_directory_entry_; +#elif BUILDFLAG(IS_WIN) const WIN32_FIND_DATA& find_data() const { return *ChromeToWindowsType(&find_data_); } diff --git a/base/files/file_enumerator_starboard.cc b/base/files/file_enumerator_starboard.cc new file mode 100644 index 000000000000..a4ebb01e4f9a --- /dev/null +++ b/base/files/file_enumerator_starboard.cc @@ -0,0 +1,228 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "base/files/file_enumerator.h" + +#include + +#include "base/files/file_util.h" +#include "base/logging.h" +#include "base/threading/thread_restrictions.h" +#include "starboard/common/log.h" +#include "starboard/common/string.h" +#include "starboard/configuration_constants.h" +#include "starboard/directory.h" +#include "starboard/file.h" +#include "starboard/memory.h" + +namespace base { + +// FileEnumerator::FileInfo ---------------------------------------------------- + +FileEnumerator::FileInfo::FileInfo() { + memset(&stat_, 0, sizeof(stat_)); +} + +bool FileEnumerator::FileInfo::IsDirectory() const { + return S_ISDIR(stat_.st_mode); +} + +FilePath FileEnumerator::FileInfo::GetName() const { + return filename_; +} + +int64_t FileEnumerator::FileInfo::GetSize() const { + return stat_.st_size; +} + +base::Time FileEnumerator::FileInfo::GetLastModifiedTime() const { + return base::Time::FromTimeT(stat_.st_mtime); +} + +// FileEnumerator -------------------------------------------------------------- + +FileEnumerator::FileEnumerator(const FilePath& root_path, + bool recursive, + int file_type) + : FileEnumerator(root_path, + recursive, + file_type, + FilePath::StringType(), + FolderSearchPolicy::MATCH_ONLY) {} + +FileEnumerator::FileEnumerator(const FilePath& root_path, + bool recursive, + int file_type, + const FilePath::StringType& pattern) + : FileEnumerator(root_path, + recursive, + file_type, + pattern, + FolderSearchPolicy::MATCH_ONLY) {} + +FileEnumerator::FileEnumerator(const FilePath& root_path, + bool recursive, + int file_type, + const FilePath::StringType& pattern, + FolderSearchPolicy folder_search_policy) + : FileEnumerator(root_path, + recursive, + file_type, + pattern, + folder_search_policy, + ErrorPolicy::IGNORE_ERRORS) {} + +FileEnumerator::FileEnumerator(const FilePath& root_path, + bool recursive, + int file_type, + const FilePath::StringType& pattern, + FolderSearchPolicy folder_search_policy, + ErrorPolicy error_policy) + : current_directory_entry_(0), + root_path_(root_path), + recursive_(recursive), + file_type_(file_type), + pattern_(pattern), + folder_search_policy_(folder_search_policy), + error_policy_(error_policy) { + // INCLUDE_DOT_DOT must not be specified if recursive. + DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); + // The Windows version of this code appends the pattern to the root_path, + // potentially only matching against items in the top-most directory. + // Do the same here. + if (pattern.empty()) { + pattern_ = FilePath::StringType(); + } + pending_paths_.push(root_path); +} + +FileEnumerator::~FileEnumerator() = default; + +// static +std::vector FileEnumerator::ReadDirectory( + const FilePath& source) { + internal::AssertBlockingAllowed(); + + DIR* directory = opendir(source.value().c_str()); + if (!(directory)) { +#if BUILDFLAG(IS_WIN) + error_ = File::Error::FILE_ERROR_FAILED; +#else + error_ = File::Error::FILE_ERROR_NOT_A_DIRECTORY; +#endif // BUILDFLAG(IS_WIN) + return std::vector(); + } + + auto GenerateEntry = [source](std::string filename) { + FileEnumerator::FileInfo info; + info.filename_ = FilePath(filename); + + FilePath full_name = source.Append(filename); + // TODO: Make sure this follows symlinks on relevant platforms. + if (stat(full_name.value().c_str(), &info.stat_) != 0) { + DPLOG(ERROR) << "Couldn't stat on " << full_name.value(); + memset(&info.stat_, 0, sizeof(info.stat_)); + } + return info; + }; + + std::vector ret; + // We test if SbDirectoryGetNext returns the parent directory, i.e. |..|, + // because whether or not it is returned is platform-dependent and we need to + // be able to guarantee it is returned when the INCLUDE_DOT_DOT bitflag is + // set. + bool found_dot_dot = false; + + std::vector entry(kSbFileMaxName); + + struct dirent dirent_buffer; + struct dirent* dirent; + while (true) { + if (entry.size() < kSbFileMaxName || !directory || !entry.data()) { + break; + } + int result = readdir_r(directory, &dirent_buffer, &dirent); + if (result || !dirent) { + break; + } + starboard::strlcpy(entry.data(), dirent->d_name, entry.size()); + + const char dot_dot_str[] = ".."; + if (!strncmp(entry.data(), dot_dot_str, sizeof(dot_dot_str))) { + found_dot_dot = true; + } + ret.push_back(GenerateEntry(entry.data())); + + } + + if ((INCLUDE_DOT_DOT & file_type_) && !found_dot_dot) { + ret.push_back(GenerateEntry("..")); + } + + closedir(directory); + return ret; +} + +FilePath FileEnumerator::Next() { + internal::AssertBlockingAllowed(); + + ++current_directory_entry_; + + // While we've exhausted the entries in the current directory, do the next + while (current_directory_entry_ >= directory_entries_.size()) { + if (pending_paths_.empty()) { + return FilePath(); + } + + root_path_ = pending_paths_.top(); + root_path_ = root_path_.StripTrailingSeparators(); + pending_paths_.pop(); + + std::vector entries = ReadDirectory(root_path_); + + directory_entries_.clear(); + current_directory_entry_ = 0; + for (const auto& file_info : entries) { + FilePath full_path = root_path_.Append(file_info.filename_); + if (ShouldSkip(full_path)) { + continue; + } + + if (pattern_.size()) { + NOTREACHED() << "Patterns not supported in Starboard."; + continue; + } + + if (recursive_ && file_info.IsDirectory()) { + pending_paths_.push(full_path); + } + + if ((file_info.IsDirectory() && (file_type_ & DIRECTORIES)) || + (!file_info.IsDirectory() && (file_type_ & FILES)) || + (file_type_ & NAMES_ONLY)) { + directory_entries_.push_back(file_info); + } + } + } + + return root_path_.Append( + directory_entries_[current_directory_entry_].filename_); +} + +FileEnumerator::FileInfo FileEnumerator::GetInfo() const { + DCHECK(!(file_type_ & FileType::NAMES_ONLY)); + return directory_entries_[current_directory_entry_]; +} + +} // namespace base diff --git a/base/files/file_enumerator_unittest.cc b/base/files/file_enumerator_unittest.cc index a30443db811b..8ef751ad7692 100644 --- a/base/files/file_enumerator_unittest.cc +++ b/base/files/file_enumerator_unittest.cc @@ -103,7 +103,7 @@ bool CreateDummyFile(const FilePath& path) { bool GetFileInfo(const FilePath& file_path, File::Info& info) { // FLAG_WIN_BACKUP_SEMANTICS: Needed to open directories on Windows. File f(file_path, - File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WIN_BACKUP_SEMANTICS); + File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WIN_BACKUP_SEMANTICS); if (!f.IsValid()) { LOG(ERROR) << "Could not open " << file_path.value() << ": " << File::ErrorToString(f.error_details()); @@ -184,6 +184,8 @@ TEST(FileEnumerator, SingleFileInFolderForDirSearch) { } } +// Starboard does not support patterns. +#if !defined(STARBOARD) TEST(FileEnumerator, SingleFileInFolderWithFiltering) { ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -202,7 +204,10 @@ TEST(FileEnumerator, SingleFileInFolderWithFiltering) { EXPECT_THAT(files, IsEmpty()); } } +#endif // !defined(STARBOARD) +// Starboard does not support patterns. +#if !defined(STARBOARD) TEST(FileEnumerator, TwoFilesInFolder) { ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -231,6 +236,7 @@ TEST(FileEnumerator, TwoFilesInFolder) { EXPECT_THAT(files, UnorderedElementsAre(foo_txt, bar_txt)); } } +#endif // !defined(STARBOARD) TEST(FileEnumerator, SingleFolderInFolderForFileSearch) { ScopedTempDir temp_dir; @@ -264,6 +270,8 @@ TEST(FileEnumerator, SingleFolderInFolderForDirSearch) { } } +// Starboard does not support patterns. +#if !defined(STARBOARD) TEST(FileEnumerator, TwoFoldersInFolder) { ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -285,6 +293,7 @@ TEST(FileEnumerator, TwoFoldersInFolder) { EXPECT_THAT(files, ElementsAre(subdir_foo)); } } +#endif // !defined(STARBOARD) TEST(FileEnumerator, FolderAndFileInFolder) { ScopedTempDir temp_dir; @@ -354,6 +363,8 @@ TEST(FileEnumerator, FileInSubfolder) { } } +// Starboard does not support patterns. +#if !defined(STARBOARD) TEST(FileEnumerator, FilesInSubfoldersWithFiltering) { ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -391,6 +402,7 @@ TEST(FileEnumerator, FilesInSubfoldersWithFiltering) { FileEnumerator::FolderSearchPolicy::ALL); EXPECT_THAT(files, UnorderedElementsAre(subdir_foo, foo_foo, bar_foo)); } +#endif // !defined(STARBOARD) TEST(FileEnumerator, InvalidDirectory) { ScopedTempDir temp_dir; @@ -415,7 +427,7 @@ TEST(FileEnumerator, InvalidDirectory) { #endif } -#if BUILDFLAG(IS_POSIX) +#if BUILDFLAG(IS_POSIX) && !defined(STARBOARD) TEST(FileEnumerator, SymLinkLoops) { ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -508,7 +520,15 @@ TEST(FileEnumerator, GetInfoRecursive) { // all the files. for (TestDirectory& dir : directories) { const FilePath dir_path = temp_dir.GetPath().Append(dir.name); +#if defined(STARBOARD) +#ifdef _WIN32 +// TODO: Reable this test when support directory open in base::File for Windows. +// Below tests would fail because we are now using _open from instead of +// CreateFile from . +#else ASSERT_TRUE(GetFileInfo(dir_path, dir.info)); +#endif +#endif } FileEnumerator file_enumerator( @@ -518,6 +538,10 @@ TEST(FileEnumerator, GetInfoRecursive) { auto info = file_enumerator.GetInfo(); bool found = false; if (info.IsDirectory()) { +#if defined(STARBOARD) +#ifdef _WIN32 +// Reable this test when support directory open in base::File for Windows. +#else for (TestDirectory& dir : directories) { if (info.GetName() == dir.name) { CheckDirectoryAgainstInfo(info, dir); @@ -525,6 +549,8 @@ TEST(FileEnumerator, GetInfoRecursive) { break; } } +#endif +#endif } else { for (TestFile& file : files) { if (info.GetName() == file.path.BaseName()) { @@ -534,14 +560,25 @@ TEST(FileEnumerator, GetInfoRecursive) { } } } - +#if defined(STARBOARD) +#ifdef _WIN32 +// Reable this test when support directory open in base::File for Windows. +#else EXPECT_TRUE(found) << "Got unexpected result " << info.GetName().value(); +#endif +#endif } + #if defined(STARBOARD) +#ifdef _WIN32 +// Reable this test when support directory open in base::File for Windows. +#else for (const TestDirectory& dir : directories) { EXPECT_TRUE(dir.found) << "Directory " << dir.name.value() << " was not returned"; } +#endif +#endif for (const TestFile& file : files) { EXPECT_TRUE(file.found) << "File " << file.path.value() << " was not returned"; @@ -557,6 +594,10 @@ TEST(FileEnumerator, GetInfoRecursive) { // a bug in Windows, not us -- you can see it with the "dir" command (notice // that the time of . and .. always match). Skip this test. // https://crbug.com/1119546 +#elif defined(STARBOARD) +#ifdef _WIN32 +// Reable this test when support directory open in base::File for Windows. +#endif #else // Tests that FileEnumerator::GetInfo() returns the correct info for the .. // directory. diff --git a/base/files/file_path.cc b/base/files/file_path.cc index 59bbc6e15da4..ede6ab4be001 100644 --- a/base/files/file_path.cc +++ b/base/files/file_path.cc @@ -442,7 +442,7 @@ FilePath FilePath::InsertBeforeExtensionASCII(StringPiece suffix) DCHECK(IsStringASCII(suffix)); #if BUILDFLAG(IS_WIN) return InsertBeforeExtension(UTF8ToWide(suffix)); -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) return InsertBeforeExtension(suffix); #endif } @@ -469,7 +469,7 @@ FilePath FilePath::AddExtensionASCII(StringPiece extension) const { DCHECK(IsStringASCII(extension)); #if BUILDFLAG(IS_WIN) return AddExtension(UTF8ToWide(extension)); -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) return AddExtension(extension); #endif } @@ -569,7 +569,7 @@ FilePath FilePath::AppendASCII(StringPiece component) const { DCHECK(base::IsStringASCII(component)); #if BUILDFLAG(IS_WIN) return Append(UTF8ToWide(component)); -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) return Append(component); #endif } @@ -666,7 +666,7 @@ FilePath FilePath::FromUTF16Unsafe(StringPiece16 utf16) { return FilePath(AsWStringPiece(utf16)); } -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) // See file_path.h for a discussion of the encoding of paths on POSIX // platforms. These encoding conversion functions are not quite correct. @@ -726,7 +726,7 @@ FilePath FilePath::FromUTF16Unsafe(StringPiece16 utf16) { void FilePath::WriteToPickle(Pickle* pickle) const { #if BUILDFLAG(IS_WIN) pickle->WriteString16(AsStringPiece16(path_)); -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) pickle->WriteString(path_); #else #error Unsupported platform @@ -739,7 +739,7 @@ bool FilePath::ReadFromPickle(PickleIterator* iter) { if (!iter->ReadString16(&path)) return false; path_ = UTF16ToWide(path); -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) if (!iter->ReadString(&path_)) return false; #else @@ -1338,7 +1338,7 @@ int FilePath::CompareIgnoreCase(StringPieceType string1, return HFSFastUnicodeCompare(hfs1, hfs2); } -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) // Generic Posix system comparisons. int FilePath::CompareIgnoreCase(StringPieceType string1, diff --git a/base/files/file_path.h b/base/files/file_path.h index e8c5022db3fb..cc912767e7a1 100644 --- a/base/files/file_path.h +++ b/base/files/file_path.h @@ -116,7 +116,7 @@ // enabled and disabled independently, to aid testing. These #defines are // here so that the same setting can be used in both the implementation and // in the unit test. -#if BUILDFLAG(IS_WIN) +#if BUILDFLAG(IS_WIN) || defined(COMPILER_MSVC) #define FILE_PATH_USES_DRIVE_LETTERS #define FILE_PATH_USES_WIN_SEPARATORS #endif // BUILDFLAG(IS_WIN) @@ -126,7 +126,7 @@ // base::StringPrintf("Path is %" PRFilePath ".\n", path.value().c_str()); #if BUILDFLAG(IS_WIN) #define PRFilePath "ls" -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) #define PRFilePath "s" #endif // BUILDFLAG(IS_WIN) @@ -140,7 +140,7 @@ #define FILE_PATH_LITERAL_INTERNAL(x) L##x #define FILE_PATH_LITERAL(x) FILE_PATH_LITERAL_INTERNAL(x) -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) #define FILE_PATH_LITERAL(x) x #endif // BUILDFLAG(IS_WIN) @@ -158,7 +158,7 @@ class BASE_EXPORT FilePath { // On Windows, for Unicode-aware applications, native pathnames are wchar_t // arrays encoded in UTF-16. typedef std::wstring StringType; -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) // On most platforms, native pathnames are char arrays, and the encoding // may or may not be specified. On Mac OS X, native pathnames are encoded // in UTF-8. diff --git a/base/files/file_path_unittest.cc b/base/files/file_path_unittest.cc index 9673a48bc302..14e228590538 100644 --- a/base/files/file_path_unittest.cc +++ b/base/files/file_path_unittest.cc @@ -335,7 +335,7 @@ TEST_F(FilePathTest, Append) { // handle the case when AppendASCII is passed UTF8 #if BUILDFLAG(IS_WIN) std::string ascii = WideToUTF8(leaf); -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) std::string ascii = leaf; #endif observed_str = root.AppendASCII(ascii); diff --git a/base/files/file_proxy.cc b/base/files/file_proxy.cc index c3b80839dedc..67c962997e4f 100644 --- a/base/files/file_proxy.cc +++ b/base/files/file_proxy.cc @@ -6,6 +6,7 @@ #include #include +#include #include "base/files/file.h" #include "base/files/file_util.h" diff --git a/base/files/file_proxy_unittest.cc b/base/files/file_proxy_unittest.cc index a8bcc58d89f9..f8a0f4ab7c21 100644 --- a/base/files/file_proxy_unittest.cc +++ b/base/files/file_proxy_unittest.cc @@ -192,8 +192,10 @@ TEST_F(FileProxyTest, Close) { EXPECT_EQ(File::FILE_OK, error_); EXPECT_FALSE(proxy.IsValid()); +#if !defined(STARBOARD) // Now it should pass on all platforms. EXPECT_TRUE(base::Move(TestPath(), TestDirPath().AppendASCII("new"))); +#endif } TEST_F(FileProxyTest, CreateTemporary) { @@ -256,6 +258,7 @@ TEST_F(FileProxyTest, SetAndTake) { EXPECT_TRUE(file.IsValid()); } +#if !defined(STARBOARD) TEST_F(FileProxyTest, DuplicateFile) { FileProxy proxy(file_task_runner()); CreateProxy(File::FLAG_CREATE | File::FLAG_WRITE, &proxy); @@ -272,6 +275,7 @@ TEST_F(FileProxyTest, DuplicateFile) { EXPECT_FALSE(invalid_proxy.IsValid()); EXPECT_FALSE(invalid_duplicate.IsValid()); } +#endif TEST_F(FileProxyTest, GetInfo) { // Setup. @@ -351,7 +355,7 @@ TEST_F(FileProxyTest, WriteAndFlush) { } } -#if BUILDFLAG(IS_ANDROID) +#if BUILDFLAG(IS_ANDROID) || defined(STARBOARD) // Flaky on Android, see http://crbug.com/489602 #define MAYBE_SetTimes DISABLED_SetTimes #else diff --git a/base/files/file_starboard.cc b/base/files/file_starboard.cc new file mode 100644 index 000000000000..393c6b5d095b --- /dev/null +++ b/base/files/file_starboard.cc @@ -0,0 +1,497 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Adapted from platform_file_posix.cc +#include "base/logging.h" +#include "base/files/file_starboard.h" + +#include +#include +#include +#include + +#include "base/files/file.h" +#include "base/files/file_path.h" +#include "base/notreached.h" +#include "base/posix/eintr_wrapper.h" +#include "base/strings/stringprintf.h" +#include "base/threading/thread_restrictions.h" +#include "starboard/common/metrics/stats_tracker.h" +#include "starboard/file.h" + +namespace base { + +void RecordFileWriteStat(int write_file_result) { + auto& stats_tracker = + starboard::StatsTrackerContainer::GetInstance()->stats_tracker(); + if (write_file_result <= 0) { + stats_tracker.FileWriteFail(); + } else { + stats_tracker.FileWriteSuccess(); + stats_tracker.FileWriteBytesWritten(/*bytes_written=*/write_file_result); + } +} + +// Make sure our Whence mappings match the system headers. +static_assert(File::FROM_BEGIN == static_cast(SEEK_SET) && + File::FROM_CURRENT == static_cast(SEEK_CUR) && + File::FROM_END == static_cast(SEEK_END), + "Whence enums from base must match those of Starboard."); + +void File::Info::FromStat(const stat_wrapper_t& stat_info) { + is_directory = S_ISDIR(stat_info.st_mode); + 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 BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA) + time_t last_modified_sec = stat_info.st_mtim.tv_sec; + int64_t last_modified_nsec = stat_info.st_mtim.tv_nsec; + time_t last_accessed_sec = stat_info.st_atim.tv_sec; + int64_t last_accessed_nsec = stat_info.st_atim.tv_nsec; + time_t creation_time_sec = stat_info.st_ctim.tv_sec; + int64_t creation_time_nsec = stat_info.st_ctim.tv_nsec; +#elif BUILDFLAG(IS_ANDROID) + time_t last_modified_sec = stat_info.st_mtime; + int64_t last_modified_nsec = stat_info.st_mtime_nsec; + time_t last_accessed_sec = stat_info.st_atime; + 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 BUILDFLAG(IS_APPLE) + 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 BUILDFLAG(IS_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; + int64_t last_accessed_nsec = stat_info.st_atimespec.tv_nsec; + time_t creation_time_sec = stat_info.st_ctimespec.tv_sec; + int64_t creation_time_nsec = stat_info.st_ctimespec.tv_nsec; +#else + time_t last_modified_sec = stat_info.st_mtime; + int64_t last_modified_nsec = 0; + time_t last_accessed_sec = stat_info.st_atime; + int64_t last_accessed_nsec = 0; + time_t creation_time_sec = stat_info.st_ctime; + int64_t creation_time_nsec = 0; +#endif + + last_modified = + Time::FromTimeT(last_modified_sec) + + Microseconds(last_modified_nsec / Time::kNanosecondsPerMicrosecond); + + last_accessed = + Time::FromTimeT(last_accessed_sec) + + Microseconds(last_accessed_nsec / Time::kNanosecondsPerMicrosecond); + + creation_time = + Time::FromTimeT(creation_time_sec) + + Microseconds(creation_time_nsec / Time::kNanosecondsPerMicrosecond); +} + +bool File::IsValid() const { + return file_.is_valid(); +} + +PlatformFile File::GetPlatformFile() const { + return file_.get(); +} + +PlatformFile File::TakePlatformFile() { + return file_.release(); +} + +void File::Close() { + if (!IsValid()) + return; + + SCOPED_FILE_TRACE("Close"); + internal::AssertBlockingAllowed(); + file_.reset(); +} + +int64_t File::Seek(Whence whence, int64_t offset) { + internal::AssertBlockingAllowed(); + DCHECK(IsValid()); + + SCOPED_FILE_TRACE_WITH_SIZE("Seek", offset); + return lseek(file_.get(), static_cast(offset), + static_cast(whence)); +} + +int File::Read(int64_t offset, char* data, int size) { + internal::AssertBlockingAllowed(); + DCHECK(IsValid()); + if (size < 0) { + return -1; + } + + SCOPED_FILE_TRACE_WITH_SIZE("Read", size); + + int original_position = lseek(file_.get(), 0, static_cast(SEEK_CUR)); + if (original_position < 0) { + return -1; + } + + int position = + lseek(file_.get(), static_cast(offset), static_cast(SEEK_SET)); + int result = 0; + if (position == offset) { + result = ReadAtCurrentPos(data, size); + } + + // Restore position regardless of result of write. + position = + lseek(file_.get(), static_cast(original_position), static_cast(SEEK_SET)); + if (result < 0) { + return result; + } + + if (position < 0) { + return -1; + } + + return result; +} + +int File::ReadAtCurrentPos(char* data, int size) { + internal::AssertBlockingAllowed(); + DCHECK(IsValid()); + if (size < 0) + return -1; + + SCOPED_FILE_TRACE_WITH_SIZE("ReadAtCurrentPos", size); + + return starboard::ReadAll(file_.get(), data, size); +} + +int File::ReadNoBestEffort(int64_t offset, char* data, int size) { + internal::AssertBlockingAllowed(); + DCHECK(IsValid()); + SCOPED_FILE_TRACE_WITH_SIZE("ReadNoBestEffort", size); + + int original_position = lseek( + file_.get(), 0, static_cast(SEEK_CUR)); + if (original_position < 0) { + return -1; + } + + int position = + lseek(file_.get(), static_cast(offset), static_cast(SEEK_SET)); + int result = 0; + if (position == offset) { + result = read(file_.get(), data, size); + } + + // Restore position regardless of result of read. + position = lseek(file_.get(), static_cast(original_position), + static_cast(SEEK_SET)); + if (result < 0) { + return result; + } + + if (position < 0) { + return -1; + } + + return result; +} + +int File::ReadAtCurrentPosNoBestEffort(char* data, int size) { + internal::AssertBlockingAllowed(); + DCHECK(IsValid()); + if (size < 0) + return -1; + + SCOPED_FILE_TRACE_WITH_SIZE("ReadAtCurrentPosNoBestEffort", size); + return read(file_.get(), data, size); +} + +int File::Write(int64_t offset, const char* data, int size) { + internal::AssertBlockingAllowed(); + if (append_) { + return WriteAtCurrentPos(data, size); + } + + int original_position = lseek(file_.get(), 0, static_cast(SEEK_CUR)); + if (original_position < 0) { + return -1; + } + + int64_t position = + lseek(file_.get(), static_cast(offset), static_cast(SEEK_SET)); + + int result = 0; + if (position == offset) { + result = WriteAtCurrentPos(data, size); + } + + // Restore position regardless of result of write. + position = lseek(file_.get(), static_cast(original_position), + static_cast(SEEK_SET)); + if (result < 0) { + return result; + } + + if (position < 0) { + return -1; + } + + return result; +} + +int File::WriteAtCurrentPos(const char* data, int size) { + internal::AssertBlockingAllowed(); + DCHECK(IsValid()); + if (size < 0) + return -1; + + SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPos", size); + + int bytes_written = 0; + long rv; + do { + rv = HANDLE_EINTR(write(file_.get(), data + bytes_written, + static_cast(size - bytes_written))); + if (rv <= 0) + break; + + bytes_written += rv; + } while (bytes_written < size); + + return bytes_written ? bytes_written : checked_cast(rv); +} + +int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) { + internal::AssertBlockingAllowed(); + DCHECK(IsValid()); + if (size < 0) + return -1; + + SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPosNoBestEffort", size); + int write_result = write(file_.get(), data, size); + RecordFileWriteStat(write_result); + return write_result; +} + +int64_t File::GetLength() { + DCHECK(IsValid()); + + SCOPED_FILE_TRACE("GetLength"); + + stat_wrapper_t file_info; + if (Fstat(file_.get(), &file_info)) + return -1; + + return file_info.st_size; +} + +bool File::SetLength(int64_t length) { + internal::AssertBlockingAllowed(); + DCHECK(IsValid()); + SCOPED_FILE_TRACE_WITH_SIZE("SetLength", length); + return !ftruncate(file_.get(), length); +} + +bool File::SetTimes(Time last_access_time, Time last_modified_time) { + internal::AssertBlockingAllowed(); + DCHECK(IsValid()); + + SCOPED_FILE_TRACE("SetTimes"); + + NOTIMPLEMENTED(); + return false; +} + +bool File::GetInfo(Info* info) { + DCHECK(IsValid()); + + SCOPED_FILE_TRACE("GetInfo"); + + stat_wrapper_t file_info; + if (Fstat(file_.get(), &file_info)) + return false; + + info->FromStat(file_info); + return true; +} + +File::Error File::GetLastFileError() { + return base::File::OSErrorToFileError(errno); +} + +// Static. +int File::Stat(const char* path, stat_wrapper_t* sb) { + internal::AssertBlockingAllowed(); + return stat(path, sb); +} +int File::Fstat(int fd, stat_wrapper_t* sb) { + internal::AssertBlockingAllowed(); + return fstat(fd, sb); +} + +// Static. +File::Error File::OSErrorToFileError(int saved_errno) { + switch (saved_errno) { + case EACCES: + case EISDIR: + case EROFS: + case EPERM: + return FILE_ERROR_ACCESS_DENIED; + case EBUSY: +#if !BUILDFLAG(IS_NACL) // ETXTBSY not defined by NaCl. + case ETXTBSY: +#endif + return FILE_ERROR_IN_USE; + case EEXIST: + return FILE_ERROR_EXISTS; + case EIO: + return FILE_ERROR_IO; + case ENOENT: + return FILE_ERROR_NOT_FOUND; + case ENFILE: // fallthrough + case EMFILE: + return FILE_ERROR_TOO_MANY_OPENED; + case ENOMEM: + return FILE_ERROR_NO_MEMORY; + case ENOSPC: + return FILE_ERROR_NO_SPACE; + case ENOTDIR: + return FILE_ERROR_NOT_A_DIRECTORY; + default: + // This function should only be called for errors. + DCHECK_NE(0, saved_errno); + return FILE_ERROR_FAILED; + } +} + +void File::DoInitialize(const FilePath& path, uint32_t flags) { + internal::AssertBlockingAllowed(); + DCHECK(!IsValid()); + + created_ = false; + append_ = flags & FLAG_APPEND; + file_name_ = path.AsUTF8Unsafe(); + + int open_flags = 0; + if (flags & FLAG_CREATE) { + open_flags = O_CREAT | O_EXCL; + } + + if (flags & FLAG_CREATE_ALWAYS) { + SB_DCHECK(!open_flags); + open_flags = O_CREAT | O_TRUNC; + } + + if (flags & FLAG_OPEN_TRUNCATED) { + SB_DCHECK(!open_flags); + SB_DCHECK(flags & FLAG_WRITE); + open_flags = O_TRUNC; + } + + if (!open_flags && !(flags & FLAG_OPEN) && !(flags & FLAG_OPEN_ALWAYS)) { + SB_NOTREACHED(); + errno = EOPNOTSUPP; + } + + if (flags & FLAG_WRITE || flags & FLAG_APPEND) { + if (flags & FLAG_READ) { + open_flags |= O_RDWR; + } else { + open_flags |= O_WRONLY; + } + } + +#if defined(O_LARGEFILE) + // Always add on O_LARGEFILE, regardless of compiler macros + open_flags |= O_LARGEFILE; +#endif + + SB_COMPILE_ASSERT(O_RDONLY == 0, O_RDONLY_must_equal_zero); + + int mode = S_IRUSR | S_IWUSR; + int descriptor = HANDLE_EINTR(open(path.value().c_str(), open_flags, mode)); + + if (flags & FLAG_OPEN_ALWAYS) { + if (descriptor < 0) { + open_flags |= O_CREAT; + descriptor = HANDLE_EINTR(open(path.value().c_str(), open_flags, mode)); + if (descriptor >= 0) + created_ = true; + } + } + + if (descriptor >= 0 && + (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE))) { + created_ = true; + } + + file_.reset(descriptor); + + if (!file_.is_valid()) { +#if defined(__ANDROID_API__) + bool can_read = flags & O_RDONLY; + bool can_write = flags & O_WRONLY; + if ((errno == 0) && (!can_read || can_write)) { + error_details_ = FILE_ERROR_ACCESS_DENIED; + } else { + error_details_ = File::GetLastFileError(); + } +#else + error_details_ = File::GetLastFileError(); +#endif + } else { + error_details_ = FILE_OK; + if (append_) { + lseek(file_.get(), 0, SEEK_END); + } + } + + if (flags & FLAG_DELETE_ON_CLOSE) { + NOTREACHED() << "Not supported on Starboard platforms right now."; + } + + async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC); +} + +bool File::Flush() { + internal::AssertBlockingAllowed(); + DCHECK(IsValid()); + SCOPED_FILE_TRACE("Flush"); + + return !fsync(file_.get()); +} + +void File::SetPlatformFile(PlatformFile file) { + DCHECK(!file_.is_valid()); + file_.reset(file); +} + +File File::Duplicate() const { + NOTREACHED(); + return File(); +} + +} // namespace base diff --git a/base/files/file_starboard.h b/base/files/file_starboard.h new file mode 100644 index 000000000000..81f887442894 --- /dev/null +++ b/base/files/file_starboard.h @@ -0,0 +1,24 @@ +// Copyright 2023 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef BASE_FILES_FILE_STARBOARD_H_ +#define BASE_FILES_FILE_STARBOARD_H_ + +namespace base { + +void RecordFileWriteStat(int write_file_result); + +} // namespace base + +#endif // BASE_FILES_FILE_STARBOARD_H_ diff --git a/base/files/file_unittest.cc b/base/files/file_unittest.cc index 2aa523b26f96..e8d88d20ce51 100644 --- a/base/files/file_unittest.cc +++ b/base/files/file_unittest.cc @@ -89,6 +89,7 @@ TEST(FileTest, Create) { EXPECT_FALSE(file.IsValid()); } +#if !defined(STARBOARD) { // Create a file that exists. File file(file_path, base::File::FLAG_CREATE | base::File::FLAG_READ); @@ -97,6 +98,7 @@ TEST(FileTest, Create) { EXPECT_EQ(base::File::FILE_ERROR_EXISTS, file.error_details()); EXPECT_EQ(base::File::FILE_ERROR_EXISTS, base::File::GetLastFileError()); } +#endif { // Create or overwrite a file. @@ -107,6 +109,7 @@ TEST(FileTest, Create) { EXPECT_EQ(base::File::FILE_OK, file.error_details()); } +#if !defined(STARBOARD) { // Create a delete-on-close file. file_path = temp_dir.GetPath().AppendASCII("create_file_2"); @@ -119,8 +122,10 @@ TEST(FileTest, Create) { } EXPECT_FALSE(base::PathExists(file_path)); +#endif // !defined(STARBOARD) } +#if !defined(STARBOARD) TEST(FileTest, SelfSwap) { base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -130,6 +135,7 @@ TEST(FileTest, SelfSwap) { std::swap(file, file); EXPECT_TRUE(file.IsValid()); } +#endif // !defined(STARBOARD) TEST(FileTest, Async) { base::ScopedTempDir temp_dir; @@ -137,18 +143,28 @@ TEST(FileTest, Async) { FilePath file_path = temp_dir.GetPath().AppendASCII("create_file"); { +#if defined(STARBOARD) + File file(file_path, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_ASYNC + | base::File::FLAG_READ); +#else File file(file_path, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_ASYNC); +#endif EXPECT_TRUE(file.IsValid()); EXPECT_TRUE(file.async()); } { +#if defined(STARBOARD) + File file(file_path, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ); +#else File file(file_path, base::File::FLAG_OPEN_ALWAYS); +#endif EXPECT_TRUE(file.IsValid()); EXPECT_FALSE(file.async()); } } +#if !defined(STARBOARD) TEST(FileTest, DeleteOpenFile) { base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -174,6 +190,7 @@ TEST(FileTest, DeleteOpenFile) { same_file.Close(); EXPECT_FALSE(base::PathExists(file_path)); } +#endif // !defined(STARBOARD) TEST(FileTest, ReadWrite) { base::ScopedTempDir temp_dir; @@ -370,11 +387,25 @@ TEST(FileTest, Length) { #if !BUILDFLAG(IS_FUCHSIA) // Fuchsia doesn't seem to support big files. // Expand the file past the 4 GB limit. - const int64_t kBigFileLength = 5'000'000'000; - EXPECT_TRUE(file.SetLength(kBigFileLength)); - EXPECT_EQ(kBigFileLength, file.GetLength()); - EXPECT_TRUE(GetFileSize(file_path, &file_size)); - EXPECT_EQ(kBigFileLength, file_size); +#if defined(STARBOARD) +#if SB_IS(32_BIT) +// TODO: After POSIX migration WIN32 uses _chsize in ftruncate, and it +// does not support big file: +// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/chsize?view=msvc-170 +// Before POSXI migration WIN32 was implemented with SetFilePointerEx : +// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfilepointerex +#else + // TODO: Checking why SB_IS(32_BIT) is not set for WIN32, and we have to dynamically check + // sizeof(long) to filter out WIN32. + if (sizeof(long) == 8) { + const int64_t kBigFileLength = 5'000'000'000; + EXPECT_TRUE(file.SetLength(kBigFileLength)); + EXPECT_EQ(kBigFileLength, file.GetLength()); + EXPECT_TRUE(GetFileSize(file_path, &file_size)); + EXPECT_EQ(kBigFileLength, file_size); + } +#endif +#endif #endif // Close the file and reopen with base::File::FLAG_CREATE_ALWAYS, and make @@ -435,7 +466,7 @@ TEST(FileTest, DISABLED_TouchGetInfo) { EXPECT_FALSE(info.is_symbolic_link); // ext2/ext3 and HPS/HPS+ seem to have a timestamp granularity of 1s. -#if BUILDFLAG(IS_POSIX) +#if BUILDFLAG(IS_POSIX) && !defined(STARBOARD) EXPECT_EQ(info.last_accessed.ToTimeVal().tv_sec, new_last_accessed.ToTimeVal().tv_sec); EXPECT_EQ(info.last_modified.ToTimeVal().tv_sec, @@ -540,6 +571,7 @@ TEST(FileTest, Seek) { EXPECT_EQ(kOffset, file.Seek(base::File::FROM_END, -kOffset)); } +#if !defined(STARBOARD) TEST(FileTest, Duplicate) { base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -566,7 +598,9 @@ TEST(FileTest, Duplicate) { ASSERT_EQ(kDataLen, file2.Read(0, &buf[0], kDataLen)); ASSERT_EQ(std::string(kData, kDataLen), std::string(&buf[0], kDataLen)); } +#endif +#if !defined(STARBOARD) TEST(FileTest, DuplicateDeleteOnClose) { base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -582,8 +616,9 @@ TEST(FileTest, DuplicateDeleteOnClose) { file2.Close(); ASSERT_FALSE(base::PathExists(file_path)); } +#endif -#if BUILDFLAG(ENABLE_BASE_TRACING) +#if BUILDFLAG(ENABLE_BASE_TRACING) && !defined(STARBOARD) TEST(FileTest, TracedValueSupport) { base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -609,14 +644,31 @@ TEST(FileTest, MAYBE_WriteDataToLargeOffset) { base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); FilePath file_path = temp_dir.GetPath().AppendASCII("file"); +#if defined(STARBOARD) + File file(file_path, (base::File::FLAG_CREATE | base::File::FLAG_READ | + base::File::FLAG_WRITE)); +#else File file(file_path, (base::File::FLAG_CREATE | base::File::FLAG_READ | base::File::FLAG_WRITE | base::File::FLAG_DELETE_ON_CLOSE)); +#endif ASSERT_TRUE(file.IsValid()); const char kData[] = "this file is sparse."; const int kDataLen = sizeof(kData) - 1; - const int64_t kLargeFileOffset = (1LL << 31); + int64_t kLargeFileOffset; + +// TODO: Checking why SB_IS(32_BIT) is not set for WIN32, and we have to dynamically check +// sizeof(long) to filter out WIN32. +#if defined(STARBOARD) + if (sizeof(long) == 4) { + kLargeFileOffset = (1LL << 31) - 2; + } else { + kLargeFileOffset = (1LL << 31); +} +#else // defined(STARBOARD) +kLargeFileOffset = (1LL << 31); +#endif // defined(STARBOARD) // If the file fails to write, it is probably we are running out of disk space // and the file system doesn't support sparse file. diff --git a/base/files/file_util.cc b/base/files/file_util.cc index 48288660236d..ab43bab9b063 100644 --- a/base/files/file_util.cc +++ b/base/files/file_util.cc @@ -52,6 +52,7 @@ void RunAndReply(OnceCallback action_callback, #endif // !BUILDFLAG(IS_WIN) +#if !defined(STARBOARD) bool ReadStreamToSpanWithMaxSize( FILE* stream, size_t max_size, @@ -131,6 +132,7 @@ bool ReadStreamToSpanWithMaxSize( return read_status; } +#endif } // namespace @@ -172,7 +174,8 @@ bool Move(const FilePath& from_path, const FilePath& to_path) { } bool CopyFileContents(File& infile, File& outfile) { -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) +#if defined(COBALT_PENDING_CLEAN_UP) +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) bool retry_slow = false; bool res = internal::CopyFileContentsWithSendfile(infile, outfile, retry_slow); @@ -217,6 +220,24 @@ bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) { // We open the file in binary format even if they are text files because // we are just comparing that bytes are exactly same in both files and not // doing anything smart with text formatting. +#ifdef COBALT_PENDING_CLEAN_UP + // std::ifstream doesn't work on all our platforms. + base::File file1(filename1, base::File::FLAG_OPEN | base::File::FLAG_READ); + base::File file2(filename2, base::File::FLAG_OPEN | base::File::FLAG_READ); + auto file1_length = file1.GetLength(); + if (file1_length != file2.GetLength()) { + return false; + } + std::unique_ptr file1_content(new char[file1_length]()); + std::unique_ptr file2_content(new char[file1_length]()); + if (file1.ReadAtCurrentPos(file1_content.get(), file1_length) != file1_length || + file2.ReadAtCurrentPos(file2_content.get(), file1_length) != file1_length) { + return false; + } + + return memcmp(file1_content.get(), file2_content.get(), + file1_length) == 0; +#else #if BUILDFLAG(IS_WIN) std::ifstream file1(filename1.value().c_str(), std::ios::in | std::ios::binary); @@ -250,8 +271,10 @@ bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) { file1.close(); file2.close(); return true; +#endif } +#if !defined(COBALT_PENDING_CLEAN_UP) bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) { #if BUILDFLAG(IS_WIN) std::ifstream file1(filename1.value().c_str(), std::ios::in); @@ -296,7 +319,9 @@ bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) { return true; } +#endif // !defined(COBALT_PENDING_CLEAN_UP) +#if !defined(STARBOARD) bool ReadStreamToString(FILE* stream, std::string* contents) { return ReadStreamToStringWithMaxSize( stream, std::numeric_limits::max(), contents); @@ -321,12 +346,15 @@ bool ReadStreamToStringWithMaxSize(FILE* stream, } return read_successs; } +#endif absl::optional> ReadFileToBytes(const FilePath& path) { if (path.ReferencesParent()) { return absl::nullopt; } +#ifndef COBALT_PENDING_CLEAN_UP + // TODO(b/298237462): Implement ScopedFILE for Starboard. ScopedFILE file_stream(OpenFile(path, "rb")); if (!file_stream) { return absl::nullopt; @@ -341,6 +369,13 @@ absl::optional> ReadFileToBytes(const FilePath& path) { })) { return absl::nullopt; } +#else + std::string contents; + if (!ReadFileToString(path, &contents)) { + return absl::nullopt; + } + std::vector bytes(contents.begin(), contents.end()); +#endif return bytes; } @@ -356,10 +391,48 @@ bool ReadFileToStringWithMaxSize(const FilePath& path, contents->clear(); if (path.ReferencesParent()) return false; +#if defined(COBALT_PENDING_CLEAN_UP) + base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ); + if (!file.IsValid()) { + return false; + } + + // Use a smaller buffer than in Chromium so we don't run out of stack space. + const size_t kBufferSize = 1 << 12; + char buf[kBufferSize]; + size_t len; + size_t size = 0; + bool read_status = true; + + while ((len = file.ReadAtCurrentPos(buf, sizeof(buf))) > 0) { + if (contents) { + size_t bytes_to_add = std::min(len, max_size - size); + if (size + bytes_to_add > contents->max_size()) { + read_status = false; + break; + } + contents->append(buf, std::min(len, max_size - size)); + } + + if ((max_size - size) < len) { + read_status = false; + break; + } + + size += len; + } + + if (contents) { + contents->resize(contents->size()); + } + read_status = read_status && file.IsValid(); + return read_status; +#else ScopedFILE file_stream(OpenFile(path, "rb")); if (!file_stream) return false; return ReadStreamToStringWithMaxSize(file_stream.get(), max_size, contents); +#endif // defined(COBALT_PENDING_CLEAN_UP) } bool IsDirectoryEmpty(const FilePath& dir_path) { @@ -417,6 +490,7 @@ bool TouchFile(const FilePath& path, return file.SetTimes(last_accessed, last_modified); } +#if !defined(COBALT_PENDING_CLEAN_UP) bool CloseFile(FILE* file) { if (file == nullptr) return true; @@ -440,6 +514,7 @@ bool TruncateFile(FILE* file) { #endif return true; } +#endif // !defined(COBALT_PENDING_CLEAN_UP) bool WriteFile(const FilePath& filename, span data) { int size = checked_cast(data.size()); diff --git a/base/files/file_util.h b/base/files/file_util.h index 0b0c7df1517e..c47f00265f41 100644 --- a/base/files/file_util.h +++ b/base/files/file_util.h @@ -217,10 +217,12 @@ BASE_EXPORT bool DirectoryExists(const FilePath& path); BASE_EXPORT bool ContentsEqual(const FilePath& filename1, const FilePath& filename2); +#if defined(COBALT_PENDING_CLEAN_UP) // Returns true if the contents of the two text files given are equal, false // otherwise. This routine treats "\r\n" and "\n" as equivalent. BASE_EXPORT bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2); +#endif // !defined(COBALT_PENDING_CLEAN_UP) // Reads the file at |path| and returns a vector of bytes on success, and // nullopt on error. For security reasons, a |path| containing path traversal @@ -250,6 +252,7 @@ BASE_EXPORT bool ReadFileToStringWithMaxSize(const FilePath& path, std::string* contents, size_t max_size); +#if !defined(STARBOARD) // As ReadFileToString, but reading from an open stream after seeking to its // start (if supported by the stream). This can also be used to read the whole // file from a file descriptor by converting the file descriptor into a stream @@ -261,6 +264,7 @@ BASE_EXPORT bool ReadStreamToString(FILE* stream, std::string* contents); BASE_EXPORT bool ReadStreamToStringWithMaxSize(FILE* stream, size_t max_size, std::string* contents); +#endif #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) @@ -507,8 +511,10 @@ BASE_EXPORT bool TouchFile(const FilePath& path, // configured to not be propagated to child processes. BASE_EXPORT FILE* OpenFile(const FilePath& filename, const char* mode); +#if !defined(COBALT_PENDING_CLEAN_UP) // Closes file opened by OpenFile. Returns true on success. BASE_EXPORT bool CloseFile(FILE* file); +#endif // Associates a standard FILE stream with an existing File. Note that this // functions take ownership of the existing File. @@ -517,9 +523,11 @@ BASE_EXPORT FILE* FileToFILE(File file, const char* mode); // Returns a new handle to the file underlying |file_stream|. BASE_EXPORT File FILEToFile(FILE* file_stream); +#if !defined(COBALT_PENDING_CLEAN_UP) // Truncates an open file to end at the location of the current file pointer. // This is a cross-platform analog to Windows' SetEndOfFile() function. BASE_EXPORT bool TruncateFile(FILE* file); +#endif // !defined(COBALT_PENDING_CLEAN_UP) // Reads at most the given number of bytes from the file into the buffer. // Returns the number of read bytes, or -1 on error. @@ -700,7 +708,8 @@ BASE_EXPORT bool CopyAndDeleteDirectory(const FilePath& from_path, const FilePath& to_path); #endif // BUILDFLAG(IS_WIN) -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) +#if defined(COBALT_PENDING_CLEAN_UP) +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) // CopyFileContentsWithSendfile will use the sendfile(2) syscall to perform a // file copy without moving the data between kernel and userspace. This is much // more efficient than sequences of read(2)/write(2) calls. The |retry_slow| diff --git a/base/files/file_util_starboard.cc b/base/files/file_util_starboard.cc new file mode 100644 index 000000000000..bcec62a16edd --- /dev/null +++ b/base/files/file_util_starboard.cc @@ -0,0 +1,491 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Adapted from file_util_posix.cc. + +#include "base/files/file_util.h" + +#include +#include +#include +#include + +#include +#include + +#include "base/base_paths.h" +#include "base/files/file_enumerator.h" +#include "base/files/file_path.h" +#include "base/files/platform_file.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/threading/scoped_blocking_call.h" +#include "base/threading/thread_restrictions.h" +#include "base/time/time.h" +#include "starboard/configuration_constants.h" +#include "starboard/common/file.h" +#include "starboard/directory.h" +#include "base/strings/strcat.h" +#include "starboard/system.h" + +namespace base { + +namespace { + +// The list of portable filename characters as per POSIX. This should be the +// lowest-common-denominator of acceptable filename characters. +const char kPortableFilenameCharacters[] = { + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "_-" +}; +const int kPortableFilenameCharactersLength = + SB_ARRAY_SIZE_INT(kPortableFilenameCharacters) - 1; + +// 8 characters = 65 ^ 8 possible filenames, which gives a nice wide space for +// avoiding collisions. +const char kTempSubstitution[] = "XXXXXXXX"; +const int kTempSubstitutionLength = SB_ARRAY_SIZE_INT(kTempSubstitution) - 1; + +std::string TempFileName() { + return std::string(".com.youtube.Cobalt.XXXXXXXX"); +} + +// Takes the template defined in |in_out_template| and destructively replaces +// any 'X' characters at the end with randomized portable filename characters. +void GenerateTempFileName(FilePath::StringType *in_out_template) { + size_t template_start = in_out_template->find(kTempSubstitution); + if (template_start == FilePath::StringType::npos) { + // No pattern. + return; + } + + for (int i = 0; i < kTempSubstitutionLength; ++i) { + uint64_t random = SbSystemGetRandomUInt64(); + int index = random % kPortableFilenameCharactersLength; + (*in_out_template)[template_start + i] = kPortableFilenameCharacters[index]; + } +} + +// Creates a random filename based on the TempFileName() pattern, creates the +// file, leaving it open, placing the path in |out_path| and returning the open +// file. +int CreateAndOpenTemporaryFile(FilePath directory, FilePath *out_path) { + internal::AssertBlockingAllowed(); + DCHECK(out_path); + FilePath path = directory.Append(TempFileName()); + FilePath::StringType tmpdir_string = path.value(); + GenerateTempFileName(&tmpdir_string); + *out_path = FilePath(tmpdir_string); + return open(tmpdir_string.c_str(), O_CREAT | O_EXCL | O_WRONLY, + S_IRUSR | S_IWUSR); +} + +// Retries creating a temporary file until it can win the race to create a +// unique one. +int CreateAndOpenTemporaryFileSafely(FilePath directory, + FilePath *out_path) { + int file = -1; + while (file < 0) { + file = CreateAndOpenTemporaryFile(directory, out_path); + } + return file; +} + +bool CreateTemporaryDirInDirImpl(const FilePath &base_dir, + const FilePath::StringType &name_tmpl, + FilePath *new_dir) { + internal::AssertBlockingAllowed(); + DCHECK(name_tmpl.find(kTempSubstitution) != FilePath::StringType::npos) + << "Directory name template must contain \"XXXXXXXX\"."; + + FilePath dir_template = base_dir.Append(name_tmpl); + std::string dir_template_string = dir_template.value(); + FilePath sub_dir; + while (true) { + std::string sub_dir_string = dir_template_string; + GenerateTempFileName(&sub_dir_string); + sub_dir = FilePath(sub_dir_string); + if (!DirectoryExists(sub_dir)) { + break; + } + } + + // NOTE: This is not as secure as mkdtemp, because it doesn't atomically + // guarantee that there is no collision. But, with 8 X's it should be good + // enough for our purposes. + if (!CreateDirectory(sub_dir)) { + DPLOG(ERROR) << "CreateDirectory"; + return false; + } + + *new_dir = sub_dir; + return true; +} + +} // namespace + +FilePath FormatTemporaryFileName(FilePath::StringPieceType identifier) { + StringPiece prefix = ".com.youtube.Cobalt.XXXXXXXX"; + return FilePath(StrCat({".", prefix, ".", identifier})); +} + +bool AbsolutePath(FilePath* path) { + // We don't have cross-platform tools for this, so we will just return true if + // the path is already absolute. + return path->IsAbsolute(); +} + +// Borrowed from file_util_posix.cc +bool DoDeleteFile(const FilePath& path, bool recursive) { + ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK); + // Reset errno in the beginning of the funciton + errno = 0; + + const char* path_str = path.value().c_str(); + stat_wrapper_t file_info; + if (::stat(path_str, &file_info) != 0) { + return (errno == ENOENT || errno == ENOTDIR); + } + if (!S_ISDIR(file_info.st_mode)) { + return (unlink(path_str) == 0) || (errno == ENOENT); + } + if (!recursive){ + return (rmdir(path_str) == 0) || (errno == ENOENT); + } + + bool success = true; + stack directories; + directories.push(path.value()); + FileEnumerator traversal(path, true, + FileEnumerator::FILES | FileEnumerator::DIRECTORIES); + for (FilePath current = traversal.Next(); !current.empty(); + current = traversal.Next()) { + if (traversal.GetInfo().IsDirectory()) { + directories.push(current.value()); + } + else { + success &= (unlink(current.value().c_str()) == 0) || (errno == ENOENT); + } + } + + while (!directories.empty()) { + FilePath dir = FilePath(directories.top()); + directories.pop(); + success &= (rmdir(dir.value().c_str()) == 0) || (errno == ENOENT); + } + return success; +} + +bool DeleteFile(const FilePath& path) { + return DoDeleteFile(path, /*recursive=*/false); +} + +bool DeletePathRecursively(const FilePath& path) { + return DoDeleteFile(path, /*recursive=*/true); +} + +bool DieFileDie(const FilePath& file, bool recurse) { + // There is no need to workaround Windows problems on POSIX. + // Just pass-through. + if (recurse) + return DeletePathRecursively(file); + return DeleteFile(file); +} + +bool EvictFileFromSystemCache(const FilePath& file) { + NOTIMPLEMENTED(); + return false; +} + +bool ReplaceFile(const FilePath& from_path, + const FilePath& to_path, + File::Error* error) { + ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK); + if (!CopyFile(from_path, to_path)) { + return false; + } + return DeleteFile(from_path); +} + +// Mac has its own implementation, this is for all other Posix systems. +bool CopyFile(const FilePath& from_path, const FilePath& to_path) { + ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK); + File infile; + infile = File(from_path, File::FLAG_OPEN | File::FLAG_READ); + if (!infile.IsValid()) + return false; + + File outfile(to_path, File::FLAG_WRITE | File::FLAG_CREATE_ALWAYS); + if (!outfile.IsValid()) + return false; + + return CopyFileContents(infile, outfile); +} + +FILE* OpenFile(const FilePath& filename, const char* mode) { + NOTIMPLEMENTED(); + return nullptr; +} + +bool PathExists(const FilePath &path) { + internal::AssertBlockingAllowed(); + struct ::stat file_info; + return ::stat(path.value().c_str(), &file_info) == 0; +} + +bool PathIsReadable(const FilePath &path) { + internal::AssertBlockingAllowed(); + return starboard::FileCanOpen(path.value().c_str(), O_CREAT | O_RDONLY); +} + +bool PathIsWritable(const FilePath &path) { + internal::AssertBlockingAllowed(); + return starboard::FileCanOpen(path.value().c_str(), O_CREAT | O_WRONLY); +} + +bool DirectoryExists(const FilePath& path) { + internal::AssertBlockingAllowed(); + struct stat info; + if (stat(path.value().c_str(), &info) != 0) { + return false; + } + + return S_ISDIR(info.st_mode); +} + +bool GetTempDir(FilePath *path) { + std::vector buffer(kSbFileMaxPath + 1, 0); + bool result = + SbSystemGetPath(kSbSystemPathTempDirectory, buffer.data(), buffer.size()); + if (!result) { + return false; + } + + *path = FilePath(buffer.data()); + if (DirectoryExists(*path)) { + return true; + } + + return CreateDirectory(*path); +} + +bool GetShmemTempDir(FilePath *path, bool executable) { + return GetTempDir(path); +} + +FilePath GetHomeDir() { + FilePath path; + bool result = PathService::Get(base::DIR_CACHE, &path); + DCHECK(result); + return path; +} + +ScopedFILE CreateAndOpenTemporaryStreamInDir(const FilePath& dir, + FilePath* path) { + NOTIMPLEMENTED(); + ScopedFILE stream; + return stream; +} + +File CreateAndOpenTemporaryFileInDir(const FilePath &dir, FilePath *temp_file) { + internal::AssertBlockingAllowed(); + DCHECK(temp_file); + int file = CreateAndOpenTemporaryFileSafely(dir, temp_file); + return file >= 0 ? File(std::move(file)) : File(File::GetLastFileError()); +} + +bool CreateTemporaryFileInDir(const FilePath &dir, FilePath *temp_file) { + internal::AssertBlockingAllowed(); + DCHECK(temp_file); + int file = CreateAndOpenTemporaryFileSafely(dir, temp_file); + return ((file >= 0) && !::close(file)); +} + +bool CreateTemporaryDirInDir(const FilePath &base_dir, + const FilePath::StringType &prefix, + FilePath *new_dir) { + FilePath::StringType mkdtemp_template = prefix + kTempSubstitution; + return CreateTemporaryDirInDirImpl(base_dir, mkdtemp_template, new_dir); +} + +bool CreateNewTempDirectory(const FilePath::StringType &prefix, + FilePath *new_temp_path) { + FilePath tmpdir; + if (!GetTempDir(&tmpdir)) { + return false; + } + + return CreateTemporaryDirInDirImpl(tmpdir, TempFileName(), new_temp_path); +} + +bool CreateDirectoryAndGetError(const FilePath &full_path, File::Error* error) { + internal::AssertBlockingAllowed(); + + // Fast-path: can the full path be resolved from the full path? + if (mkdir(full_path.value().c_str(), 0700) == 0 + || DirectoryExists(full_path)) { + return true; + } + + // Slow-path: iterate through the paths and resolve from the root + // to the leaf. + std::vector subpaths; + + // Collect a list of all parent directories. + FilePath last_path = full_path; + subpaths.push_back(full_path); + for (FilePath path = full_path.DirName(); + path.value() != last_path.value(); + path = path.DirName()) { + subpaths.push_back(path); + last_path = path; + } + + // Iterate through the parents and create the missing ones. + for (std::vector::reverse_iterator i = subpaths.rbegin(); + i != subpaths.rend(); ++i) { + if (DirectoryExists(*i)) { + continue; + } + + struct ::stat info; + if (mkdir(i->value().c_str(), 0700) != 0 && + !(::stat(i->value().c_str(), &info) == 0 && S_ISDIR(info.st_mode))){ + if (error) + *error = File::OSErrorToFileError(SbSystemGetLastError()); + return false; + } + } + + return true; +} + +bool NormalizeFilePath(const FilePath& path, FilePath* normalized_path) { + internal::AssertBlockingAllowed(); + // Only absolute paths are supported in Starboard. + if (!path.IsAbsolute()) { + return false; + } + *normalized_path = path; + return true; +} + +bool IsLink(const FilePath &file_path) { + internal::AssertBlockingAllowed(); + struct stat info; + + // If we can't stat on the file, it's safe to assume that the + // file won't at least be a 'followable' link. + if (stat(file_path.value().c_str(), &info) != 0) { + return false; + } + + return S_ISLNK(info.st_mode); +} + +bool GetFileInfo(const FilePath &file_path, File::Info *results) { + stat_wrapper_t file_info; + if (::stat(file_path.value().c_str(), &file_info) != 0) + return false; + + results->FromStat(file_info); + return true; +} + +int ReadFile(const FilePath &filename, char *data, int size) { + internal::AssertBlockingAllowed(); + + base::File file(filename, base::File::FLAG_OPEN | base::File::FLAG_READ); + if (!file.IsValid()) { + DLOG(ERROR) << "ReadFile(" << filename.value() << "): Unable to open."; + return -1; + } + + // We use a best-effort read here. + return file.ReadAtCurrentPos(data, size); +} + +int WriteFile(const FilePath &filename, const char *data, int size) { + internal::AssertBlockingAllowed(); + + base::File file( + filename, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); + if (!file.IsValid()) { + DLOG(ERROR) << "WriteFile(" << filename.value() << "): Unable to open."; + return -1; + } + + // We use a best-effort write here. + return file.WriteAtCurrentPos(data, size); +} + +bool AppendToFile(const FilePath &filename, const char *data, int size) { + internal::AssertBlockingAllowed(); + base::File file(filename, base::File::FLAG_OPEN | base::File::FLAG_WRITE); + if (!file.IsValid()) { + DLOG(ERROR) << "AppendToFile(" << filename.value() << "): Unable to open."; + return false; + } + + if (file.Seek(base::File::FROM_END, 0) == -1) { + DLOG(ERROR) << "AppendToFile(" << filename.value() + << "): Unable to truncate."; + return false; + } + + return file.WriteAtCurrentPos(data, size) == size; +} + +bool AppendToFile(const FilePath& filename, StringPiece data) { + return AppendToFile(filename, data.data(), data.size()); +} + +bool HasFileBeenModifiedSince(const FileEnumerator::FileInfo &file_info, + const base::Time &cutoff_time) { + return file_info.GetLastModifiedTime() >= cutoff_time; +} + +bool GetCurrentDirectory(FilePath* dir) { + // Not supported on Starboard. + NOTREACHED(); + return false; +} + +bool SetCurrentDirectory(const FilePath& path) { + // Not supported on Starboard. + NOTREACHED(); + return false; +} + +FilePath MakeAbsoluteFilePath(const FilePath& input) { + internal::AssertBlockingAllowed(); + // Only absolute paths are supported in Starboard. + DCHECK(input.IsAbsolute()); + return input; +} + +namespace internal { + +bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path) { + internal::AssertBlockingAllowed(); + // Moving files is not supported in Starboard. + NOTREACHED(); + return false; +} + +} // internal + +} // namespace base diff --git a/base/files/file_util_unittest.cc b/base/files/file_util_unittest.cc index 42dd27e90ced..db8776c0ab5c 100644 --- a/base/files/file_util_unittest.cc +++ b/base/files/file_util_unittest.cc @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -46,6 +47,7 @@ #include "base/threading/thread.h" #include "base/time/time.h" #include "build/build_config.h" +#include "starboard/common/file.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/multiprocess_func_list.h" #include "testing/platform_test.h" @@ -219,7 +221,7 @@ class ReparsePoint { #endif -#if BUILDFLAG(IS_MAC) +#if BUILDFLAG(IS_MAC) && !defined(STARBOARD) // Provide a simple way to change the permissions bits on |path| in tests. // ASSERT failures will return, but not stop the test. Caller should wrap // calls to this function in ASSERT_NO_FATAL_FAILURE(). @@ -235,10 +237,10 @@ void ChangePosixFilePermissions(const FilePath& path, mode &= ~mode_bits_to_clear; ASSERT_TRUE(SetPosixFilePermissions(path, mode)); } -#endif // BUILDFLAG(IS_MAC) +#endif // BUILDFLAG(IS_MAC) && !defined(STARBOARD) // Fuchsia doesn't support file permissions. -#if !BUILDFLAG(IS_FUCHSIA) +#if !BUILDFLAG(IS_FUCHSIA) && !defined(STARBOARD) // Sets the source file to read-only. void SetReadOnly(const FilePath& path, bool read_only) { #if BUILDFLAG(IS_WIN) @@ -282,7 +284,7 @@ bool IsReadOnly(const FilePath& path) { #endif // BUILDFLAG(IS_WIN) } -#endif // BUILDFLAG(IS_FUCHSIA) +#endif // BUILDFLAG(IS_FUCHSIA) && !defined(STARBOARD) const wchar_t bogus_content[] = L"I'm cannon fodder."; @@ -334,6 +336,16 @@ class FindResultCollector { // Simple function to dump some text into a new file. void CreateTextFile(const FilePath& filename, const std::wstring& contents) { +#if defined(STARBOARD) + const std::string contents_ascii = UTF16ToASCII(WideToUTF16(contents)); + int file = + open(filename.value().c_str(), O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR); + SB_CHECK(file >= 0); + int ret = starboard::WriteAll(file, contents_ascii.data(),contents_ascii.size()); + SB_CHECK(ret == + contents_ascii.size()); + SB_CHECK(!::close(file)); +#else // !defined(STARBOARD) std::wofstream file; #if BUILDFLAG(IS_WIN) file.open(filename.value().c_str()); @@ -343,10 +355,20 @@ void CreateTextFile(const FilePath& filename, ASSERT_TRUE(file.is_open()); file << contents; file.close(); +#endif // defined(STARBOARD) } // Simple function to take out some text from a file. std::wstring ReadTextFile(const FilePath& filename) { +#if defined(STARBOARD) + const int size_in_bytes = 64 * sizeof(wchar_t); + char contents[size_in_bytes]{0}; + int file = open(filename.value().c_str(), 0, S_IRUSR | S_IWUSR); + SB_CHECK(file >= 0); + SB_CHECK(starboard::ReadAll(file, contents, size_in_bytes) != -1); + SB_CHECK(!::close(file)); + return UTF16ToWide(ASCIIToUTF16(contents)); +#else // !defined(STARBOARD) wchar_t contents[64]; std::wifstream file; #if BUILDFLAG(IS_WIN) @@ -358,8 +380,10 @@ std::wstring ReadTextFile(const FilePath& filename) { file.getline(contents, std::size(contents)); file.close(); return std::wstring(contents); +#endif // defined(STARBOARD) } +#if !defined(STARBOARD) // Sets |is_inheritable| to indicate whether or not |stream| is set up to be // inerhited into child processes (i.e., HANDLE_FLAG_INHERIT is set on the // underlying handle on Windows, or FD_CLOEXEC is not set on the underlying file @@ -383,8 +407,9 @@ void GetIsInheritable(FILE* stream, bool* is_inheritable) { #error Not implemented #endif } +#endif // !defined(STARBOARD) -#if BUILDFLAG(IS_POSIX) +#if BUILDFLAG(IS_POSIX) && !defined(STARBOARD) class ScopedWorkingDirectory { public: explicit ScopedWorkingDirectory(const FilePath& new_working_dir) { @@ -434,7 +459,7 @@ TEST_F(FileUtilTest, MakeAbsoluteFilePathNoResolveSymbolicLinks) { MakeAbsoluteFilePathNoResolveSymbolicLinks(FilePath("relative_file_path")) .has_value()); } -#endif // BUILDFLAG(IS_POSIX) +#endif // BUILDFLAG(IS_POSIX) && !defined(STARBOARD) TEST_F(FileUtilTest, FileAndDirectorySize) { // Create three files of 20, 30 and 3 chars (utf8). ComputeDirectorySize @@ -464,6 +489,8 @@ TEST_F(FileUtilTest, FileAndDirectorySize) { EXPECT_EQ(size_f1 + size_f2 + 3, computed_size); } +// Starboard only supports absolute file paths. +#if !defined(STARBOARD) TEST_F(FileUtilTest, NormalizeFilePathBasic) { // Create a directory under the test dir. Because we create it, // we know it is not a link. @@ -491,6 +518,7 @@ TEST_F(FileUtilTest, NormalizeFilePathBasic) { ASSERT_TRUE(normalized_file_a_path.DirName() .IsParent(normalized_file_b_path.DirName())); } +#endif // !defined(STARBOARD) #if BUILDFLAG(IS_WIN) @@ -1018,7 +1046,7 @@ INSTANTIATE_TEST_SUITE_P(EnforcementDisabled, #endif // BUILDFLAG(IS_WIN) -#if BUILDFLAG(IS_POSIX) +#if BUILDFLAG(IS_POSIX) && !defined(STARBOARD) TEST_F(FileUtilTest, CreateAndReadSymlinks) { FilePath link_from = temp_dir_.GetPath().Append(FPL("from_file")); @@ -1651,9 +1679,9 @@ TEST_F(FileUtilTest, CopyFileExecutablePermission) { EXPECT_EQ(0777, mode); } -#endif // BUILDFLAG(IS_POSIX) +#endif // BUILDFLAG(IS_POSIX) && !defined(STARBOARD) -#if !BUILDFLAG(IS_FUCHSIA) +#if !BUILDFLAG(IS_FUCHSIA) && !defined(STARBOARD) TEST_F(FileUtilTest, CopyFileACL) { // While FileUtilTest.CopyFile asserts the content is correctly copied over, @@ -1707,7 +1735,7 @@ TEST_F(FileUtilTest, CopyDirectoryACL) { ASSERT_FALSE(IsReadOnly(src_subdir)); } -#endif // !BUILDFLAG(IS_FUCHSIA) +#endif // !BUILDFLAG(IS_FUCHSIA) && !defined(STARBOARD) TEST_F(FileUtilTest, DeleteNonExistent) { FilePath non_existent = @@ -1912,6 +1940,7 @@ TEST_F(FileUtilTest, DeleteDirRecursiveWithOpenFile) { File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE); ASSERT_TRUE(PathExists(file_name3)); +#if !defined(STARBOARD) #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) // On Windows, holding the file open in sufficient to make it un-deletable. // The POSIX code is verifiable on Linux by creating an "immutable" file but @@ -1926,13 +1955,15 @@ TEST_F(FileUtilTest, DeleteDirRecursiveWithOpenFile) { ioctl(file1.GetPlatformFile(), FS_IOC_SETFLAGS, &flags); ioctl(file3.GetPlatformFile(), FS_IOC_SETFLAGS, &flags); } -#endif +#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) +#endif // !defined(STARBOARD) // 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. DeletePathRecursively(test_subdir); EXPECT_FALSE(PathExists(file_name2)); +#if !defined(STARBOARD) #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) // Make sure that the test can clean up after itself. if (file_attrs_supported) { @@ -1940,9 +1971,11 @@ TEST_F(FileUtilTest, DeleteDirRecursiveWithOpenFile) { ioctl(file1.GetPlatformFile(), FS_IOC_SETFLAGS, &flags); ioctl(file3.GetPlatformFile(), FS_IOC_SETFLAGS, &flags); } -#endif +#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) +#endif // !defined(STARBOARD) } +#if !defined(STARBOARD) #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) // This test will validate that files which would block when read result in a // failure on a call to ReadFileToStringNonBlocking. To accomplish this we will @@ -1977,7 +2010,10 @@ TEST_F(FileUtilTest, TestNonBlockingFileReadLinux) { EXPECT_EQ(result[0], 'a'); } #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) +#endif // !defined(STARBOARD) +// Starboard does not support moving. +#if !defined(STARBOARD) TEST_F(FileUtilTest, MoveFileNew) { // Create a file FilePath file_name_from = @@ -2110,7 +2146,10 @@ TEST_F(FileUtilTest, MoveExist) { EXPECT_TRUE(PathExists(dir_name_to)); EXPECT_TRUE(PathExists(file_name_to)); } +#endif // !defined(STARBOARD) +// Starboard does not support |CopyDirectory()|. +#if !defined(STARBOARD) TEST_F(FileUtilTest, CopyDirectoryRecursivelyNew) { // Create a directory. FilePath dir_name_from = @@ -2639,6 +2678,7 @@ TEST_F(FileUtilTest, CopyDirectoryExclFileOverFifo) { // Check that copying fails. EXPECT_FALSE(CopyDirectoryExcl(dir_name_from, dir_name_to, false)); } +#endif // !defined(STARBOARD) #endif // BUILDFLAG(IS_POSIX) TEST_F(FileUtilTest, CopyFile) { @@ -2699,6 +2739,8 @@ TEST_F(FileUtilTest, CopyFile) { EXPECT_TRUE(IsDirectoryEmpty(dest_dir)); } +// Starboard does not support DIR_TEST_DATA pointing to //base/test/data. +#if !defined(STARBOARD) // file_util winds up using autoreleased objects on the Mac, so this needs // to be a PlatformTest. typedef PlatformTest ReadOnlyFileUtilTest; @@ -2793,6 +2835,7 @@ TEST_F(ReadOnlyFileUtilTest, TextContentsEqual) { EXPECT_FALSE(TextContentsEqual(original_file, empty1_file)); EXPECT_TRUE(TextContentsEqual(blank_line_file, blank_line_crlf_file)); } +#endif // !defined(STARBOARD) // We don't need equivalent functionality outside of Windows. #if BUILDFLAG(IS_WIN) @@ -2856,6 +2899,8 @@ TEST_F(FileUtilTest, GetTempDirTest) { } #endif // BUILDFLAG(IS_WIN) +// Starboard does not support |OpenFile()|. +#if !defined(STARBOARD) // Test that files opened by OpenFile are not set up for inheritance into child // procs. TEST_F(FileUtilTest, OpenFileNoInheritance) { @@ -2882,6 +2927,7 @@ TEST_F(FileUtilTest, OpenFileNoInheritance) { ASSERT_TRUE(DeleteFile(file_path)); } } +#endif // !defined(STARBOARD) TEST_F(FileUtilTest, CreateAndOpenTemporaryFileInDir) { // Create a temporary file. @@ -2915,6 +2961,8 @@ TEST_F(FileUtilTest, CreateTemporaryFileTest) { EXPECT_TRUE(DeleteFile(i)); } +// Starboard does not support |CreateAndOpenTemporaryStream()|. +#if !defined(STARBOARD) TEST_F(FileUtilTest, CreateAndOpenTemporaryStreamTest) { FilePath names[3]; ScopedFILE fps[3]; @@ -2938,6 +2986,7 @@ TEST_F(FileUtilTest, CreateAndOpenTemporaryStreamTest) { EXPECT_TRUE(DeleteFile(names[i])); } } +#endif // !defined(STARBOARD) TEST_F(FileUtilTest, GetUniquePathTest) { // Create a unique temp directory and use it to generate a unique file path. @@ -2981,6 +3030,7 @@ TEST_F(FileUtilTest, GetUniquePathTest) { } } +#if !defined(STARBOARD) TEST_F(FileUtilTest, FileToFILE) { File file; FILE* stream = FileToFILE(std::move(file), "w"); @@ -3008,6 +3058,7 @@ TEST_F(FileUtilTest, FILEToFile) { ASSERT_EQ(fflush(stream.get()), 0); EXPECT_EQ(file.GetLength(), 5L); } +#endif // !defined(STARBOARD) #if BUILDFLAG(IS_WIN) TEST_F(FileUtilTest, GetSecureSystemTemp) { @@ -3054,6 +3105,7 @@ TEST_F(FileUtilTest, CreateNewTemporaryDirInDirTest) { } #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#if !defined(STARBOARD) TEST_F(FileUtilTest, GetShmemTempDirTest) { FilePath dir; EXPECT_TRUE(GetShmemTempDir(false, &dir)); @@ -3128,6 +3180,7 @@ TEST_F(FileUtilTest, AllocateFileRegionTest_DontTruncate) { ASSERT_TRUE(AllocateFileRegion(&file, 0, kTruncatedFileLength)); EXPECT_EQ(file.GetLength(), kTestFileLength); } +#endif // !defined(STARBOARD) #endif TEST_F(FileUtilTest, GetHomeDirTest) { @@ -3147,7 +3200,7 @@ TEST_F(FileUtilTest, CreateDirectoryTest) { #if BUILDFLAG(IS_WIN) FilePath test_path = test_root.Append(FILE_PATH_LITERAL("dir\\tree\\likely\\doesnt\\exist\\")); -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) FilePath test_path = test_root.Append(FILE_PATH_LITERAL("dir/tree/likely/doesnt/exist/")); #endif @@ -3305,6 +3358,8 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { EXPECT_TRUE(c4.HasFile(file2_abs)); EXPECT_EQ(4, c4.size()); +// Starboard does not support patterns. +#if !defined(STARBOARD) // Enumerate with a pattern. FileEnumerator f5(temp_dir_.GetPath(), true, FILES_AND_DIRECTORIES, FPL("dir*")); @@ -3315,6 +3370,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { EXPECT_TRUE(c5.HasFile(dir2inner)); EXPECT_TRUE(c5.HasFile(dir2innerfile)); EXPECT_EQ(5, c5.size()); +#endif // !defined(STARBOARD) #if BUILDFLAG(IS_WIN) { @@ -3517,7 +3573,7 @@ TEST_F(FileUtilTest, ReadFileToString) { EXPECT_EQ(0u, data.length()); } -#if !BUILDFLAG(IS_WIN) +#if !BUILDFLAG(IS_WIN) && !defined(COMPILER_MSVC) TEST_F(FileUtilTest, ReadFileToStringWithUnknownFileSize) { #if BUILDFLAG(IS_FUCHSIA) test::TaskEnvironment task_environment; @@ -3547,7 +3603,7 @@ TEST_F(FileUtilTest, ReadFileToStringWithUnknownFileSize) { #endif // !BUILDFLAG(IS_WIN) #if !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_FUCHSIA) && \ - !BUILDFLAG(IS_IOS) + !BUILDFLAG(IS_IOS) && !defined(STARBOARD) #define ChildMain WriteToPipeChildMain #define ChildMainString "WriteToPipeChildMain" @@ -3694,7 +3750,7 @@ TEST_F(FileUtilTest, ReadFileToStringWithNamedPipe) { ASSERT_EQ(0, unlink(pipe_path.value().c_str())); } #endif // !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_FUCHSIA) - // && !BUILDFLAG(IS_IOS) + // && !BUILDFLAG(IS_IOS) && !defined(STARBOARD) #if BUILDFLAG(IS_WIN) #define ChildMain WriteToPipeChildMain @@ -3915,6 +3971,7 @@ TEST_F(FileUtilTest, ReadFileToStringWithLargeFile) { EXPECT_EQ(std::string(kLargeFileSize - 1, 'c'), actual_data); } +#if !defined(STARBOARD) TEST_F(FileUtilTest, ReadStreamToString) { ScopedFILE stream( OpenFile(temp_dir_.GetPath().Append(FPL("hello.txt")), "wb+")); @@ -3979,7 +4036,10 @@ TEST_F(FileUtilTest, ReadStreamToStringNullStream) { std::string contents; EXPECT_FALSE(ReadStreamToString(nullptr, &contents)); } +#endif // !defined(STARBOARD) +// Starboard does not support |base::File::SetTimes()|. +#if !defined(STARBOARD) TEST_F(FileUtilTest, TouchFile) { FilePath data_dir = temp_dir_.GetPath().Append(FILE_PATH_LITERAL("FilePathTest")); @@ -4017,6 +4077,7 @@ TEST_F(FileUtilTest, TouchFile) { EXPECT_EQ(modification_time.ToInternalValue(), file_info.last_modified.ToInternalValue()); } +#endif // !defined(STARBOARD) TEST_F(FileUtilTest, WriteFileSpanVariant) { FilePath empty_file = @@ -4080,6 +4141,7 @@ TEST_F(FileUtilTest, IsDirectoryEmpty) { EXPECT_FALSE(IsDirectoryEmpty(empty_dir)); } +#if !defined(STARBOARD) #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) TEST_F(FileUtilTest, SetNonBlocking) { @@ -4106,7 +4168,8 @@ TEST_F(FileUtilTest, SetCloseOnExec) { EXPECT_TRUE(SetCloseOnExec(fd.get())); } -#endif +#endif // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#endif // !defined(STARBOARD) #if BUILDFLAG(IS_MAC) @@ -4487,6 +4550,7 @@ TEST_F(FileUtilTest, GetUniquePathNumberTooManyFiles) { EXPECT_EQ(GetUniquePathNumber(some_file), -1); } +#if !defined(STARBOARD) TEST_F(FileUtilTest, PreReadFile_ExistingFile_NoSize) { FilePath text_file = temp_dir_.GetPath().Append(FPL("text_file")); CreateTextFile(text_file, bogus_content); @@ -4557,7 +4621,10 @@ TEST_F(FileUtilTest, PreReadFile_Executable) { const FilePath test_exe = exe_data_dir.Append(FPL("signed.exe")); EXPECT_TRUE(PreReadFile(test_exe, /*is_executable=*/true)); } +#endif // !defined(STARBOARD) +// Starboard does not support |OpenFile()|. +#if !defined(STARBOARD) // Test that temp files obtained racily are all unique (no interference between // threads). Mimics file operations in DoLaunchChildTestProcess() to rule out // thread-safety issues @ https://crbug.com/826408#c17. @@ -4620,7 +4687,9 @@ TEST(FileUtilMultiThreadedTest, MultiThreadedTempFiles) { for (auto& thread : threads) thread->Stop(); } +#endif // !defined(STARBOARD) +#if !defined(STARBOARD) #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) TEST(ScopedFD, ScopedFDDoesClose) { @@ -4661,7 +4730,9 @@ TEST(ScopedFD, ScopedFDCrashesOnCloseFailure) { } #endif // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#endif // !defined(STARBOARD) +#if !defined(STARBOARD) #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) TEST_F(FileUtilTest, CopyFileContentsWithSendfile) { // This test validates that sendfile(2) can be used to copy a file contents @@ -4825,6 +4896,7 @@ TEST_F(FileUtilTest, CopyFileContentsWithSendfileSeqFile) { #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || // BUILDFLAG(IS_ANDROID) +#endif // !defined(STARBOARD) } // namespace diff --git a/base/files/important_file_writer.cc b/base/files/important_file_writer.cc index ef7da13e3302..e8f50ffbd1c1 100644 --- a/base/files/important_file_writer.cc +++ b/base/files/important_file_writer.cc @@ -35,6 +35,10 @@ #include "base/time/time.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" +#if defined(STARBOARD) +#include "starboard/file.h" +#include "starboard/types.h" +#endif // defined(STARBOARD) namespace base { @@ -142,6 +146,15 @@ void ImportantFileWriter::ProduceAndWriteStringToFileAtomically( std::move(after_write_callback).Run(result); } +#if defined(STARBOARD) +// static +bool ImportantFileWriter::WriteFileAtomicallyImpl(const FilePath& path, + StringPiece data, + StringPiece histogram_suffix, + bool from_instance) { + return SbFileAtomicReplace(path.value().c_str(), data.data(), data.size()); +} +#else // static bool ImportantFileWriter::WriteFileAtomicallyImpl(const FilePath& path, StringPiece data, @@ -267,6 +280,7 @@ bool ImportantFileWriter::WriteFileAtomicallyImpl(const FilePath& path, return result; } +#endif // defined(STARBOARD) ImportantFileWriter::ImportantFileWriter( const FilePath& path, diff --git a/base/files/important_file_writer_cleaner_unittest.cc b/base/files/important_file_writer_cleaner_unittest.cc index 11c0bfa2889d..1aa48344f3d8 100644 --- a/base/files/important_file_writer_cleaner_unittest.cc +++ b/base/files/important_file_writer_cleaner_unittest.cc @@ -22,6 +22,9 @@ using ::testing::ElementsAre; namespace base { +#if defined(STARBOARD) +// base::File::SetTimes is not implemented for starboard. +#else class ImportantFileWriterCleanerTest : public ::testing::Test { public: ImportantFileWriterCleanerTest() @@ -306,5 +309,6 @@ TEST_F(ImportantFileWriterCleanerTest, StopWhileRunning) { StopCleaner(); task_environment_.RunUntilIdle(); } +#endif } // namespace base diff --git a/base/files/important_file_writer_unittest.cc b/base/files/important_file_writer_unittest.cc index 36cb21f66148..72f8f95fe6a4 100644 --- a/base/files/important_file_writer_unittest.cc +++ b/base/files/important_file_writer_unittest.cc @@ -208,6 +208,9 @@ TEST_F(ImportantFileWriterTest, WriteWithObserver) { EXPECT_EQ("baz", GetFileContent(writer.path())); } +// Disable the test as win32 SbFileOpen doesn't fail on relative path +// like bad/../path.tmp +#if !defined(COMPILER_MSVC) TEST_F(ImportantFileWriterTest, FailedWriteWithObserver) { // Use an invalid file path (relative paths are invalid) to get a // FILE_ERROR_ACCESS_DENIED error when trying to write the file. @@ -225,6 +228,7 @@ TEST_F(ImportantFileWriterTest, FailedWriteWithObserver) { write_callback_observer_.GetAndResetObservationState()); EXPECT_FALSE(PathExists(writer.path())); } +#endif TEST_F(ImportantFileWriterTest, CallbackRunsOnWriterThread) { base::Thread file_writer_thread("ImportantFileWriter test thread"); @@ -367,6 +371,7 @@ TEST_F(ImportantFileWriterTest, DoScheduledWrite_FailToSerialize) { histogram_tester.ExpectTotalCount("ImportantFile.WriteDuration", 0); } +#if !defined(STARBOARD) TEST_F(ImportantFileWriterTest, ScheduleWriteWithBackgroundDataSerializer) { base::HistogramTester histogram_tester; base::Thread file_writer_thread("ImportantFileWriter test thread"); @@ -401,6 +406,7 @@ TEST_F(ImportantFileWriterTest, ScheduleWriteWithBackgroundDataSerializer) { histogram_tester.ExpectTotalCount("ImportantFile.SerializationDuration", 1); histogram_tester.ExpectTotalCount("ImportantFile.WriteDuration", 1); } +#endif // !defined(STARBOARD) TEST_F(ImportantFileWriterTest, ScheduleWriteWithBackgroundDataSerializer_FailToSerialize) { @@ -449,6 +455,7 @@ TEST_F(ImportantFileWriterTest, WriteLargeFile) { EXPECT_EQ(large_data, actual); } +#if !defined(STARBOARD) // Verify that a UMA metric for the serialization duration is recorded. TEST_F(ImportantFileWriterTest, SerializationDuration) { base::HistogramTester histogram_tester; @@ -476,5 +483,6 @@ TEST_F(ImportantFileWriterTest, SerializationDurationWithCustomSuffix) { 1); histogram_tester.ExpectTotalCount("ImportantFile.WriteDuration.Foo", 1); } +#endif // !defined(STARBOARD) } // namespace base diff --git a/base/files/memory_mapped_file_starboard.cc b/base/files/memory_mapped_file_starboard.cc new file mode 100644 index 000000000000..0bdf66fc4d13 --- /dev/null +++ b/base/files/memory_mapped_file_starboard.cc @@ -0,0 +1,122 @@ +// Copyright 2023 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "base/files/memory_mapped_file.h" + +#include +#include +#include +#include +#include +#include + +#include "base/files/file_util.h" +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "base/threading/scoped_blocking_call.h" +#include "build/build_config.h" +#include "starboard/extension/memory_mapped_file.h" +#include "starboard/memory.h" + +namespace base { + +MemoryMappedFile::MemoryMappedFile() = default; + +bool MemoryMappedFile::MapFileRegionToMemory( + const MemoryMappedFile::Region& region, + Access access) { + ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK); + + off_t map_start = 0; + size_t map_size = 0; + int32_t data_offset = 0; + + if (region == MemoryMappedFile::Region::kWholeFile) { + int64_t file_len = file_.GetLength(); + if (file_len < 0) { + DPLOG(ERROR) << "fstat " << file_.GetPlatformFile(); + return false; + } + if (!IsValueInRangeForNumericType(file_len)) + return false; + map_size = static_cast(file_len); + length_ = map_size; + } else { + // The region can be arbitrarily aligned. mmap, instead, requires both the + // start and size to be page-aligned. Hence, we map here the page-aligned + // outer region [|aligned_start|, |aligned_start| + |size|] which contains + // |region| and then add up the |data_offset| displacement. + int64_t aligned_start = 0; + size_t aligned_size = 0; + CalculateVMAlignedBoundaries(region.offset, region.size, &aligned_start, + &aligned_size, &data_offset); + + // Ensure that the casts in the mmap call below are sane. + if (aligned_start < 0 || + !IsValueInRangeForNumericType(aligned_start)) { + DLOG(ERROR) << "Region bounds are not valid for mmap"; + return false; + } + + map_start = static_cast(aligned_start); + map_size = aligned_size; + length_ = region.size; + } + + SbMemoryMapFlags flags = kSbMemoryMapProtectRead; + switch (access) { + case READ_ONLY: + flags = kSbMemoryMapProtectRead; + break; + + case READ_WRITE: + flags = kSbMemoryMapProtectReadWrite; + break; + + case READ_WRITE_EXTEND: + flags = kSbMemoryMapProtectReadWrite; + + break; + } + const auto* memory_mapped_file_extension = + reinterpret_cast( + SbSystemGetExtension(kCobaltExtensionMemoryMappedFileName)); + if (memory_mapped_file_extension && + strcmp(memory_mapped_file_extension->name, + kCobaltExtensionMemoryMappedFileName) == 0 && + memory_mapped_file_extension->version >= 1) { + data_ = static_cast(memory_mapped_file_extension->MemoryMapFile( + nullptr, file_.GetFileName().c_str(), flags, + map_start, map_size)); + } + if (data_ == MAP_FAILED) { + DPLOG(ERROR) << "mmap " << file_.GetFileName().c_str(); + return false; + } + + data_ += data_offset; + return true; +} + +void MemoryMappedFile::CloseHandles() { + ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK); + + if (data_ != nullptr) { + munmap(data_.ExtractAsDangling(), length_); + } + file_.Close(); + length_ = 0; +} + +} // namespace base diff --git a/base/files/memory_mapped_file_unittest.cc b/base/files/memory_mapped_file_unittest.cc index dc9b9b6dc4db..6e1682d76966 100644 --- a/base/files/memory_mapped_file_unittest.cc +++ b/base/files/memory_mapped_file_unittest.cc @@ -172,6 +172,8 @@ TEST_F(MemoryMappedFileTest, MapLargePartialRegionInTheMiddle) { ASSERT_TRUE(CheckBufferContents(map.data(), kPartialSize, kOffset)); } +// ReadFileToStringWithMaxSize() is not implemented in Starboard +#if !defined(STARBOARD) TEST_F(MemoryMappedFileTest, WriteableFile) { const size_t kFileSize = 127; CreateTemporaryTestFile(kFileSize); @@ -239,6 +241,7 @@ TEST_F(MemoryMappedFileTest, ExtendableFile) { ASSERT_TRUE(ReadFileToString(temp_file_path(), &contents)); EXPECT_EQ("BAZ", contents.substr(kFileSize, 3)); } +#endif } // namespace diff --git a/base/files/platform_file.h b/base/files/platform_file.h index 186930bfb5bd..c3e9c511ef7e 100644 --- a/base/files/platform_file.h +++ b/base/files/platform_file.h @@ -8,6 +8,10 @@ #include "base/files/scoped_file.h" #include "build/build_config.h" +#if defined(STARBOARD) +#include "starboard/file.h" +#endif + #if BUILDFLAG(IS_WIN) #include "base/win/scoped_handle.h" #include "base/win/windows_types.h" @@ -19,8 +23,12 @@ namespace base { -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +using PlatformFile = int; +using ScopedPlatformFile = ::base::ScopedFD; +constexpr PlatformFile kInvalidPlatformFile = -1; +#elif BUILDFLAG(IS_WIN) using PlatformFile = HANDLE; using ScopedPlatformFile = ::base::win::ScopedHandle; diff --git a/base/files/scoped_file.cc b/base/files/scoped_file.cc index 7ff0c17b90b8..67148c5e0381 100644 --- a/base/files/scoped_file.cc +++ b/base/files/scoped_file.cc @@ -7,7 +7,10 @@ #include "base/check.h" #include "build/build_config.h" -#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#if defined(STARBOARD) +#include +#include +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) #include #include @@ -17,7 +20,8 @@ namespace base { namespace internal { -#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#if defined(STARBOARD) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) // static void ScopedFDCloseTraits::Free(int fd) { diff --git a/base/files/scoped_file.h b/base/files/scoped_file.h index 23180de58a39..f0a57f283a44 100644 --- a/base/files/scoped_file.h +++ b/base/files/scoped_file.h @@ -8,16 +8,28 @@ #include #include +#include #include "base/base_export.h" #include "base/scoped_generic.h" #include "build/build_config.h" +#if defined(STARBOARD) +#include "starboard/file.h" +#include "starboard/types.h" +#include "starboard/common/file.h" +#endif + namespace base { namespace internal { -#if BUILDFLAG(IS_ANDROID) +#if defined(STARBOARD) +struct BASE_EXPORT ScopedFDCloseTraits { + static int InvalidValue() { return -1; } + static void Free(int file) { if (file >= 0) {close(file);} } +}; +#elif BUILDFLAG(IS_ANDROID) // Use fdsan on android. struct BASE_EXPORT ScopedFDCloseTraits : public ScopedGenericOwnershipTracking { static int InvalidValue() { return -1; } @@ -44,6 +56,18 @@ struct BASE_EXPORT ScopedFDCloseTraits { }; #endif +#if defined(STARBOARD) +// Functor for |ScopedFILE| (below). +struct ScopedFILECloser { + inline void operator()(int* x) const { + if (x) { + if (*x >= 0) { + close(*x); + } + } + } +}; +#else // Functor for |ScopedFILE| (below). struct ScopedFILECloser { inline void operator()(FILE* x) const { @@ -51,6 +75,7 @@ struct ScopedFILECloser { fclose(x); } }; +#endif } // namespace internal @@ -86,7 +111,7 @@ void BASE_EXPORT ResetFDOwnership(); // ----------------------------------------------------------------------------- -#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) // A low-level Posix file descriptor closer class. Use this when writing // platform-specific code, especially that does non-file-like things with the // FD (like sockets). @@ -102,7 +127,11 @@ typedef ScopedGeneric ScopedFD; #endif // Automatically closes |FILE*|s. +#if defined(STARBOARD) +typedef std::unique_ptr ScopedFILE; +#else typedef std::unique_ptr ScopedFILE; +#endif #if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX) // Queries the ownership status of an FD, i.e. whether it is currently owned by diff --git a/base/format_macros.h b/base/format_macros.h index b8c3b45e13ed..eb001c6ee897 100644 --- a/base/format_macros.h +++ b/base/format_macros.h @@ -26,6 +26,10 @@ #include "build/build_config.h" +#if defined(STARBOARD) +#include "starboard/types.h" +#endif + #if (BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)) && \ (defined(_INTTYPES_H) || defined(_INTTYPES_H_)) && !defined(PRId64) #error "inttypes.h has already been included before this header file, but " @@ -53,7 +57,7 @@ #define PRIuS "Iu" #endif -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) // GCC will concatenate wide and narrow strings correctly, so nothing needs to // be done here. diff --git a/base/functional/bind_unittest.cc b/base/functional/bind_unittest.cc index 0c5305587db8..297a95561b9d 100644 --- a/base/functional/bind_unittest.cc +++ b/base/functional/bind_unittest.cc @@ -9,11 +9,13 @@ #include #include +#if !defined(COBALT_PENDING_CLEAN_UP) #include "base/allocator/partition_alloc_features.h" #include "base/allocator/partition_alloc_support.h" #include "base/allocator/partition_allocator/dangling_raw_ptr_checks.h" #include "base/allocator/partition_allocator/partition_alloc_buildflags.h" #include "base/allocator/partition_allocator/partition_alloc_for_testing.h" // nogncheck +#endif #include "base/functional/callback.h" #include "base/functional/disallow_unretained.h" #include "base/memory/ptr_util.h" diff --git a/base/functional/callback_helpers.h b/base/functional/callback_helpers.h index 97d181af4cef..410761ecf261 100644 --- a/base/functional/callback_helpers.h +++ b/base/functional/callback_helpers.h @@ -18,6 +18,7 @@ #include "base/atomicops.h" #include "base/base_export.h" +#include "base/callback.h" #include "base/check.h" #include "base/functional/bind.h" #include "base/functional/callback.h" @@ -25,6 +26,31 @@ namespace base { +#ifdef COBALT_PENDING_CLEAN_UP +template +bool ResetAndRunIfNotNull(CallbackType* cb) { + if (cb->is_null()) { + return false; + } + CallbackType ret(std::move(*cb)); + DCHECK(!*cb); + ret.Run(); + return true; +} + +template +bool ResetAndRunIfNotNull(base::Callback* cb, + const ParamTypes&... params) { + if (cb->is_null()) { + return false; + } + base::Callback ret(std::move(*cb)); + DCHECK(!*cb); + ret.Run(params...); + return true; +} +#endif + namespace internal { template diff --git a/base/functional/function_ref_unittest.cc b/base/functional/function_ref_unittest.cc index 89eab6717198..c703f5530764 100644 --- a/base/functional/function_ref_unittest.cc +++ b/base/functional/function_ref_unittest.cc @@ -27,7 +27,11 @@ TEST(FunctionRefTest, FreeFunction) { TEST(FunctionRefTest, Method) { [](FunctionRef ref) { +#if defined(STARBOARD) + C c = {25L}; +#else C c = {.value = 25L}; +#endif EXPECT_EQ(25L, ref(&c)); }(&C::Method); } diff --git a/base/functional/unretained_traits.h b/base/functional/unretained_traits.h index 6933b0e5f16e..68b8a0f97eb1 100644 --- a/base/functional/unretained_traits.h +++ b/base/functional/unretained_traits.h @@ -32,6 +32,12 @@ struct hb_set_t; struct wl_gpu; struct wl_shm; struct wl_surface; +#ifdef COBALT_PENDING_CLEAN_UP +struct SbPlayerPrivate; +struct SbWindowPrivate; +struct SbUiNavItemPrivate; +struct SbDrmSystemPrivate; +#endif namespace base::internal { @@ -114,6 +120,17 @@ inline constexpr bool IsIncompleteTypeSafeForUnretained = true; template <> inline constexpr bool IsIncompleteTypeSafeForUnretained = true; +#ifdef COBALT_PENDING_CLEAN_UP +template <> +inline constexpr bool IsIncompleteTypeSafeForUnretained = true; +template <> +inline constexpr bool IsIncompleteTypeSafeForUnretained = true; +template <> +inline constexpr bool IsIncompleteTypeSafeForUnretained = true; +template <> +inline constexpr bool IsIncompleteTypeSafeForUnretained = true; +#endif + template struct TypeSupportsUnretained { // Incrementally enforce the requirement to be completely defined. For now, diff --git a/base/i18n/break_iterator_unittest.cc b/base/i18n/break_iterator_unittest.cc index 432afd34d8ce..09f45aca47c1 100644 --- a/base/i18n/break_iterator_unittest.cc +++ b/base/i18n/break_iterator_unittest.cc @@ -140,9 +140,11 @@ TEST(BreakIteratorTest, BreakWordThai) { // dictionary to detect word boundaries in Thai, Chinese, Japanese, Burmese, // and Khmer. Due to the size of such a table, the part for Chinese and // Japanese is not shipped on mobile. +// Cobalt does not support Chinese/Japanese word breaking yet. This feature +// requires a big dictionary(cjdict.txt) to support. #if !(BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID)) -TEST(BreakIteratorTest, BreakWordChinese) { +TEST(BreakIteratorTest, DISABLED_BreakWordChinese) { // Terms in Traditional Chinese, without spaces in between. const char16_t term1[] = u"瀏覽"; const char16_t term2[] = u"速度"; @@ -164,7 +166,7 @@ TEST(BreakIteratorTest, BreakWordChinese) { EXPECT_FALSE(iter.IsWord()); } -TEST(BreakIteratorTest, BreakWordJapanese) { +TEST(BreakIteratorTest, DISABLED_BreakWordJapanese) { // Terms in Japanese, without spaces in between. const char16_t term1[] = u"モバイル"; const char16_t term2[] = u"でも"; @@ -182,7 +184,7 @@ TEST(BreakIteratorTest, BreakWordJapanese) { EXPECT_FALSE(iter.IsWord()); } -TEST(BreakIteratorTest, BreakWordChineseEnglish) { +TEST(BreakIteratorTest, DISABLED_BreakWordChineseEnglish) { // Terms in Simplified Chinese mixed with English and wide punctuations. std::u16string space(u" "); const char16_t token1[] = u"下载"; diff --git a/base/i18n/file_util_icu.cc b/base/i18n/file_util_icu.cc index fa9316357653..64a012ea72b5 100644 --- a/base/i18n/file_util_icu.cc +++ b/base/i18n/file_util_icu.cc @@ -7,12 +7,14 @@ #include "base/i18n/file_util_icu.h" #include +#include #include "base/check.h" #include "base/files/file_path.h" #include "base/i18n/icu_string_conversions.h" #include "base/i18n/string_compare.h" #include "base/memory/singleton.h" +#include "base/notreached.h" #include "base/numerics/safe_conversions.h" #include "base/strings/string_util.h" #include "base/strings/sys_string_conversions.h" @@ -170,7 +172,7 @@ UChar32 GetNextCodePoint(const FilePath::StringType* const file_name, // Windows uses UTF-16 encoding for filenames. U16_NEXT(file_name->data(), cursor, static_cast(file_name->length()), code_point); -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) // Mac and Chrome OS use UTF-8 encoding for filenames. // Linux doesn't actually define file system encoding. Try to parse as // UTF-8. @@ -282,6 +284,7 @@ void ReplaceIllegalCharactersInPath(FilePath::StringType* file_name, } } +#if !defined(UCONFIG_NO_COLLATION) bool LocaleAwareCompareFilenames(const FilePath& a, const FilePath& b) { UErrorCode error_code = U_ZERO_ERROR; // Use the default collator. The default locale should have been properly @@ -304,6 +307,12 @@ bool LocaleAwareCompareFilenames(const FilePath& a, const FilePath& b) { WideToUTF16(SysNativeMBToWide(b.value()))) == UCOL_LESS; #endif } +#else +bool LocaleAwareCompareFilenames(const FilePath& a, const FilePath& b) { + NOTIMPLEMENTED(); + return false; +} +#endif void NormalizeFileNameEncoding(FilePath* file_name) { #if BUILDFLAG(IS_CHROMEOS_ASH) diff --git a/base/i18n/icu_string_conversions_unittest.cc b/base/i18n/icu_string_conversions_unittest.cc index c1a0458cf943..9ecacc84c118 100644 --- a/base/i18n/icu_string_conversions_unittest.cc +++ b/base/i18n/icu_string_conversions_unittest.cc @@ -162,6 +162,7 @@ static const struct { nullptr}, }; +#if !defined(UCONFIG_NO_LEGACY_CONVERSION) TEST(ICUStringConversionsTest, ConvertBetweenCodepageAndUTF16) { for (size_t i = 0; i < std::size(kConvertCodepageCases); ++i) { SCOPED_TRACE(base::StringPrintf( @@ -231,5 +232,6 @@ TEST(ICUStringConversionsTest, ConvertToUtf8AndNormalize) { EXPECT_EQ(kConvertAndNormalizeCases[i].expected_value, result); } } +#endif // !defined(UCONFIG_NO_LEGACY_CONVERSION) } // namespace base diff --git a/base/i18n/icu_util.cc b/base/i18n/icu_util.cc index c63a1c39c73b..55551ccd03a2 100644 --- a/base/i18n/icu_util.cc +++ b/base/i18n/icu_util.cc @@ -56,6 +56,11 @@ #include "third_party/icu/source/i18n/unicode/timezone.h" #endif +#if defined(STARBOARD) +#include "starboard/client_porting/icu_init/icu_init.h" +#include "starboard/types.h" +#endif + namespace base::i18n { #if !BUILDFLAG(IS_NACL) @@ -418,6 +423,10 @@ void SetIcuTimeZoneDataDirForTesting(const char* dir) { #endif // (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE) bool InitializeICU() { +#if defined(STARBOARD) + IcuInit(); + return true; +#else #if DCHECK_IS_ON() DCHECK(!g_check_called_once || !g_called_once); g_called_once = true; @@ -433,6 +442,7 @@ bool InitializeICU() { #endif // (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC) return DoCommonInitialization(); +#endif // defined(STARBOARD) } void AllowMultipleInitializeCallsForTesting() { diff --git a/base/i18n/number_formatting_unittest.cc b/base/i18n/number_formatting_unittest.cc index da699c1a7c31..ce487653ffb3 100644 --- a/base/i18n/number_formatting_unittest.cc +++ b/base/i18n/number_formatting_unittest.cc @@ -91,7 +91,8 @@ TEST(NumberFormattingTest, FormatDouble) { } } -TEST(NumberFormattingTest, FormatPercent) { +// TODO: b/316198056 - Re-enable this test once base/net have been updated. +TEST(NumberFormattingTest, DISABLED_FormatPercent) { static const struct { int64_t number; const char* expected_english; diff --git a/base/i18n/rtl.cc b/base/i18n/rtl.cc index f9008c11f515..d9969f5982a6 100644 --- a/base/i18n/rtl.cc +++ b/base/i18n/rtl.cc @@ -8,6 +8,7 @@ #include #include +#include #include "base/check_op.h" #include "base/command_line.h" diff --git a/base/i18n/rtl_unittest.cc b/base/i18n/rtl_unittest.cc index 00935af26bb3..78242c90c44f 100644 --- a/base/i18n/rtl_unittest.cc +++ b/base/i18n/rtl_unittest.cc @@ -553,7 +553,11 @@ TEST_F(SetICULocaleTest, OverlongLocaleId) { lid.append("zzz"); SetICUDefaultLocale(id); // ICU-21639 fix the long locale issue now. + // Todo (b/316198056): Out ICU library is outdated. Enable this + // after ICU is updated. +#if !defined(STARBOARD) EXPECT_STREQ(lid.c_str(), icu::Locale::getDefault().getName()); +#endif } } // namespace i18n diff --git a/base/i18n/string_compare.cc b/base/i18n/string_compare.cc index e85caf23f16b..2efc9922f961 100644 --- a/base/i18n/string_compare.cc +++ b/base/i18n/string_compare.cc @@ -11,6 +11,8 @@ namespace base { namespace i18n { +#if !defined(UCONFIG_NO_COLLATION) + // Compares the character data stored in two different std::u16string strings by // specified Collator instance. UCollationResult CompareString16WithCollator(const icu::Collator& collator, @@ -25,5 +27,7 @@ UCollationResult CompareString16WithCollator(const icu::Collator& collator, return result; } +#endif + } // namespace i18n } // namespace base diff --git a/base/i18n/string_compare.h b/base/i18n/string_compare.h index 3dbe54bfdf08..907bd9e45fbc 100644 --- a/base/i18n/string_compare.h +++ b/base/i18n/string_compare.h @@ -12,12 +12,16 @@ namespace base { namespace i18n { +#if !defined(UCONFIG_NO_COLLATION) + // Compares the two strings using the specified collator. BASE_I18N_EXPORT UCollationResult CompareString16WithCollator(const icu::Collator& collator, const StringPiece16 lhs, const StringPiece16 rhs); +#endif + } // namespace i18n } // namespace base diff --git a/base/i18n/string_search_unittest.cc b/base/i18n/string_search_unittest.cc index a2c493391c6b..0c5a1acf219e 100644 --- a/base/i18n/string_search_unittest.cc +++ b/base/i18n/string_search_unittest.cc @@ -17,6 +17,8 @@ namespace base { namespace i18n { +#if !defined(UCONFIG_NO_COLLATION) + #define EXPECT_MATCH_IGNORE_CASE(find_this, in_this, ex_start, ex_len) \ { \ size_t index = 0; \ @@ -392,5 +394,7 @@ TEST(StringSearchTest, RepeatingStringSearch) { SetICUDefaultLocale(default_locale.data()); } +#endif + } // namespace i18n } // namespace base diff --git a/base/i18n/time_formatting.cc b/base/i18n/time_formatting.cc index cfba1e019dd3..376012e189e4 100644 --- a/base/i18n/time_formatting.cc +++ b/base/i18n/time_formatting.cc @@ -260,7 +260,11 @@ std::u16string DateIntervalFormat(const Time& begin_time, UErrorCode status = U_ZERO_ERROR; std::unique_ptr formatter( +#if defined(USE_HACKY_COBALT_CHANGES) + icu::DateIntervalFormat::createInstance(icu::UnicodeString(DateFormatToString(format)), +#else icu::DateIntervalFormat::createInstance(DateFormatToString(format), +#endif status)); icu::FieldPosition pos = 0; diff --git a/base/i18n/time_formatting_unittest.cc b/base/i18n/time_formatting_unittest.cc index 6967df898b7b..e64b0126d4f0 100644 --- a/base/i18n/time_formatting_unittest.cc +++ b/base/i18n/time_formatting_unittest.cc @@ -76,7 +76,11 @@ TEST(TimeFormattingTest, TimeFormatTimeOfDayDefault12h) { Time time; EXPECT_TRUE(Time::FromUTCExploded(kTestDateTimeExploded, &time)); std::u16string clock24h(u"15:42"); +#if defined(COBALT_PENDING_CLEAN_UP) + std::u16string clock12h_pm(u"3:42 PM"); +#else std::u16string clock12h_pm(u"3:42\u202fPM"); +#endif std::u16string clock12h(u"3:42"); std::u16string clock24h_millis(u"15:42:07.000"); @@ -114,7 +118,11 @@ TEST(TimeFormattingTest, TimeFormatTimeOfDayDefault24h) { Time time; EXPECT_TRUE(Time::FromUTCExploded(kTestDateTimeExploded, &time)); std::u16string clock24h(u"15:42"); +#if defined(COBALT_PENDING_CLEAN_UP) + std::u16string clock12h_pm(u"3:42 pm"); +#else std::u16string clock12h_pm(u"3:42\u202fpm"); +#endif std::u16string clock12h(u"3:42"); std::u16string clock24h_millis(u"15:42:07.000"); @@ -179,7 +187,11 @@ TEST(TimeFormattingTest, TimeFormatTimeOfDayDE) { Time time; EXPECT_TRUE(Time::FromUTCExploded(kTestDateTimeExploded, &time)); std::u16string clock24h(u"15:42"); +#if defined(COBALT_PENDING_CLEAN_UP) + std::u16string clock12h_pm(u"3:42 PM"); +#else std::u16string clock12h_pm(u"3:42\u202fPM"); +#endif std::u16string clock12h(u"3:42"); // The default is 24h clock. @@ -244,14 +256,25 @@ TEST(TimeFormattingTest, TimeFormatDateUS) { EXPECT_EQ(u"Apr 30, 2011", TimeFormatShortDate(time)); EXPECT_EQ(u"4/30/11", TimeFormatShortDateNumeric(time)); +#if defined(COBALT_PENDING_CLEAN_UP) + EXPECT_EQ(u"4/30/11, 3:42:07 PM", TimeFormatShortDateAndTime(time)); + EXPECT_EQ(u"4/30/11, 3:42:07 PM " + GetShortTimeZone(time), + TimeFormatShortDateAndTimeWithTimeZone(time)); +#else EXPECT_EQ(u"4/30/11, 3:42:07\u202fPM", TimeFormatShortDateAndTime(time)); EXPECT_EQ(u"4/30/11, 3:42:07\u202fPM " + GetShortTimeZone(time), TimeFormatShortDateAndTimeWithTimeZone(time)); +#endif EXPECT_EQ(u"April 2011", TimeFormatMonthAndYear(time)); +#if defined(COBALT_PENDING_CLEAN_UP) + EXPECT_EQ(u"Saturday, April 30, 2011 at 3:42:07 PM", + TimeFormatFriendlyDateAndTime(time)); +#else EXPECT_EQ(u"Saturday, April 30, 2011 at 3:42:07\u202fPM", TimeFormatFriendlyDateAndTime(time)); +#endif EXPECT_EQ(u"Saturday, April 30, 2011", TimeFormatFriendlyDate(time)); } @@ -286,12 +309,21 @@ TEST(TimeFormattingTest, TimeFormatWithPattern) { i18n::SetICUDefaultLocale("en_US"); EXPECT_EQ(u"Apr 30, 2011", TimeFormatWithPattern(time, "yMMMd")); +#if defined(COBALT_PENDING_CLEAN_UP) + EXPECT_EQ(u"April 30, 3:42:07 PM", + TimeFormatWithPattern(time, "MMMMdjmmss")); +#else EXPECT_EQ(u"April 30 at 3:42:07\u202fPM", TimeFormatWithPattern(time, "MMMMdjmmss")); +#endif i18n::SetICUDefaultLocale("en_GB"); EXPECT_EQ(u"30 Apr 2011", TimeFormatWithPattern(time, "yMMMd")); +#if defined(COBALT_PENDING_CLEAN_UP) + EXPECT_EQ(u"30 April, 15:42:07", TimeFormatWithPattern(time, "MMMMdjmmss")); +#else EXPECT_EQ(u"30 April at 15:42:07", TimeFormatWithPattern(time, "MMMMdjmmss")); +#endif i18n::SetICUDefaultLocale("ja_JP"); EXPECT_EQ(u"2011年4月30日", TimeFormatWithPattern(time, "yMMMd")); @@ -404,9 +436,15 @@ TEST(TimeFormattingTest, TimeIntervalFormat) { Time end_time; EXPECT_TRUE(Time::FromUTCExploded(kTestIntervalEndTimeExploded, &end_time)); +#if defined(COBALT_PENDING_CLEAN_UP) + EXPECT_EQ( + u"Saturday, April 30 – Saturday, May 28", + DateIntervalFormat(begin_time, end_time, DATE_FORMAT_MONTH_WEEKDAY_DAY)); +#else EXPECT_EQ( u"Saturday, April 30\u2009–\u2009Saturday, May 28", DateIntervalFormat(begin_time, end_time, DATE_FORMAT_MONTH_WEEKDAY_DAY)); +#endif const Time::Exploded kTestIntervalBeginTimeExploded = { 2011, 5, 1, 16, // Mon, May 16, 2012 @@ -414,14 +452,26 @@ TEST(TimeFormattingTest, TimeIntervalFormat) { }; EXPECT_TRUE( Time::FromUTCExploded(kTestIntervalBeginTimeExploded, &begin_time)); +#if defined(COBALT_PENDING_CLEAN_UP) + EXPECT_EQ( + u"Monday, May 16 – Saturday, May 28", + DateIntervalFormat(begin_time, end_time, DATE_FORMAT_MONTH_WEEKDAY_DAY)); +#else EXPECT_EQ( u"Monday, May 16\u2009–\u2009Saturday, May 28", DateIntervalFormat(begin_time, end_time, DATE_FORMAT_MONTH_WEEKDAY_DAY)); +#endif i18n::SetICUDefaultLocale("en_GB"); +#if defined(COBALT_PENDING_CLEAN_UP) + EXPECT_EQ( + u"Monday 16 May – Saturday 28 May", + DateIntervalFormat(begin_time, end_time, DATE_FORMAT_MONTH_WEEKDAY_DAY)); +#else EXPECT_EQ( u"Monday 16\u2009–\u2009Saturday 28 May", DateIntervalFormat(begin_time, end_time, DATE_FORMAT_MONTH_WEEKDAY_DAY)); +#endif i18n::SetICUDefaultLocale("ja"); EXPECT_EQ( diff --git a/base/i18n/transliterator_unittest.cc b/base/i18n/transliterator_unittest.cc index 042a6b9ed583..1628dd9f87a7 100644 --- a/base/i18n/transliterator_unittest.cc +++ b/base/i18n/transliterator_unittest.cc @@ -11,6 +11,7 @@ namespace base { namespace i18n { +#if !UCONFIG_NO_TRANSLITERATION TEST(TransliteratorTest, LowerCorrect) { UParseError parseErr; UErrorCode err = U_ZERO_ERROR; @@ -46,6 +47,7 @@ TEST(TransliteratorTest, LowerLatinASCIICorrect) { transliterator->transliterate(text); EXPECT_EQ(base::i18n::UnicodeStringToString16(text), u"internationalization"); } +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ } // namespace i18n } // namespace base diff --git a/base/immediate_crash.h b/base/immediate_crash.h index 049c9eef225d..4aea7085275e 100644 --- a/base/immediate_crash.h +++ b/base/immediate_crash.h @@ -6,6 +6,9 @@ #define BASE_IMMEDIATE_CRASH_H_ #include "build/build_config.h" +#if defined(STARBOARD) +#include "starboard/common/log.h" +#endif // Crashes in the fastest possible way with no attempt at logging. // There are several constraints; see http://crbug.com/664209 for more context. @@ -41,7 +44,11 @@ // be removed in followups, so splitting it up like this now makes it easy to // land the followups. -#if defined(COMPILER_GCC) +#if defined(STARBOARD) +#define IMMEDIATE_CRASH() SB_CHECK(false) +#define TRAP_SEQUENCE1_() SB_CHECK(false) +#define TRAP_SEQUENCE2_() +#elif defined(COMPILER_GCC) #if BUILDFLAG(IS_NACL) diff --git a/base/json/json_reader_unittest.cc b/base/json/json_reader_unittest.cc index f7d35895de1a..9d71caa2993e 100644 --- a/base/json/json_reader_unittest.cc +++ b/base/json/json_reader_unittest.cc @@ -687,6 +687,11 @@ TEST(JSONReaderTest, LiteralRoots) { TEST(JSONReaderTest, ReadFromFile) { FilePath path; ASSERT_TRUE(PathService::Get(base::DIR_TEST_DATA, &path)); +#if defined(STARBOARD) + path = path.Append(FILE_PATH_LITERAL("base")); + path = path.Append(FILE_PATH_LITERAL("test")); + path = path.Append(FILE_PATH_LITERAL("data")); +#endif path = path.AppendASCII("json"); ASSERT_TRUE(base::PathExists(path)); diff --git a/base/json/json_value_serializer_unittest.cc b/base/json/json_value_serializer_unittest.cc index f9fc20486f0a..16bf99e3fbff 100644 --- a/base/json/json_value_serializer_unittest.cc +++ b/base/json/json_value_serializer_unittest.cc @@ -396,6 +396,11 @@ class JSONFileValueSerializerTest : public testing::Test { TEST_F(JSONFileValueSerializerTest, Roundtrip) { FilePath original_file_path; ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &original_file_path)); +#if defined(STARBOARD) + original_file_path = original_file_path.Append(FILE_PATH_LITERAL("base")); + original_file_path = original_file_path.Append(FILE_PATH_LITERAL("test")); + original_file_path = original_file_path.Append(FILE_PATH_LITERAL("data")); +#endif original_file_path = original_file_path.AppendASCII("serializer_test.json"); ASSERT_TRUE(PathExists(original_file_path)); @@ -427,13 +432,20 @@ TEST_F(JSONFileValueSerializerTest, Roundtrip) { ASSERT_TRUE(PathExists(written_file_path)); // Now compare file contents. +#if !defined(STARBOARD) EXPECT_TRUE(TextContentsEqual(original_file_path, written_file_path)); +#endif // !defined(STARBOARD) EXPECT_TRUE(DeleteFile(written_file_path)); } TEST_F(JSONFileValueSerializerTest, RoundtripNested) { FilePath original_file_path; ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &original_file_path)); +#if defined(STARBOARD) + original_file_path = original_file_path.Append(FILE_PATH_LITERAL("base")); + original_file_path = original_file_path.Append(FILE_PATH_LITERAL("test")); + original_file_path = original_file_path.Append(FILE_PATH_LITERAL("data")); +#endif original_file_path = original_file_path.AppendASCII("serializer_nested_test.json"); @@ -453,13 +465,20 @@ TEST_F(JSONFileValueSerializerTest, RoundtripNested) { ASSERT_TRUE(PathExists(written_file_path)); // Now compare file contents. +#if !defined(STARBOARD) EXPECT_TRUE(TextContentsEqual(original_file_path, written_file_path)); +#endif // !defined(STARBOARD) EXPECT_TRUE(DeleteFile(written_file_path)); } TEST_F(JSONFileValueSerializerTest, NoWhitespace) { FilePath source_file_path; ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &source_file_path)); +#if defined(STARBOARD) + source_file_path = source_file_path.Append(FILE_PATH_LITERAL("base")); + source_file_path = source_file_path.Append(FILE_PATH_LITERAL("test")); + source_file_path = source_file_path.Append(FILE_PATH_LITERAL("data")); +#endif source_file_path = source_file_path.AppendASCII("serializer_test_nowhitespace.json"); ASSERT_TRUE(PathExists(source_file_path)); diff --git a/base/logging.cc b/base/logging.cc index b1b0af0710f2..5900a58e9e7f 100644 --- a/base/logging.cc +++ b/base/logging.cc @@ -4,9 +4,11 @@ #include "base/logging.h" +#ifndef COBALT_PENDING_CLEAN_UP #ifdef BASE_CHECK_H_ #error "logging.h should not include check.h" #endif +#endif #include #include @@ -31,10 +33,26 @@ #include "base/debug/crash_logging.h" #endif // !BUILDFLAG(IS_NACL) -#if defined(LEAK_SANITIZER) && !BUILDFLAG(IS_NACL) +#if (defined(LEAK_SANITIZER) && !BUILDFLAG(IS_NACL)) || \ + (defined(STARBOARD) && defined(ADDRESS_SANITIZER)) #include "base/debug/leak_annotations.h" #endif // defined(LEAK_SANITIZER) && !BUILDFLAG(IS_NACL) +#if defined(STARBOARD) +#include + +#include "base/files/file_starboard.h" +#include "starboard/client_porting/eztime/eztime.h" +#include "starboard/common/log.h" +#include "starboard/common/mutex.h" +#include "starboard/common/time.h" +#include "starboard/configuration.h" +#include "starboard/configuration_constants.h" +#include "starboard/file.h" +#include "starboard/system.h" +typedef int* FileHandle; +typedef pthread_mutex_t MutexHandle; +#else #if BUILDFLAG(IS_WIN) #include #include @@ -76,6 +94,7 @@ typedef HANDLE FileHandle; #define MAX_PATH PATH_MAX typedef FILE* FileHandle; #endif +#endif #include #include @@ -274,7 +293,9 @@ base::stack& GetLogAssertHandlerStack() { LogMessageHandlerFunction g_log_message_handler = nullptr; uint64_t TickCount() { -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) + return starboard::CurrentMonotonicTime(); +#elif BUILDFLAG(IS_WIN) return GetTickCount(); #elif BUILDFLAG(IS_FUCHSIA) return static_cast( @@ -298,7 +319,9 @@ uint64_t TickCount() { } void DeleteFilePath(const PathString& log_name) { -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) + unlink(log_name.c_str()); +#elif BUILDFLAG(IS_WIN) DeleteFile(log_name.c_str()); #elif BUILDFLAG(IS_NACL) // Do nothing; unlink() isn't supported on NaCl. @@ -310,7 +333,15 @@ void DeleteFilePath(const PathString& log_name) { } PathString GetDefaultLogFile() { -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) + // On Starboard, we politely ask for the log directory, like a civilized + // platform. + std::vector path(kSbFileMaxPath + 1); + SbSystemGetPath(kSbSystemPathDebugOutputDirectory, path.data(), path.size()); + PathString log_file = path.data(); + log_file += std::string(kSbFileSepString) + "debug.log"; + return log_file; +#elif BUILDFLAG(IS_WIN) // On Windows we use the same path as the exe. wchar_t module_name[MAX_PATH]; GetModuleFileName(nullptr, module_name, MAX_PATH); @@ -362,10 +393,26 @@ bool InitializeLogFileHandle() { g_log_file_name = new PathString(GetDefaultLogFile()); } +#if defined(STARBOARD) + // This seems to get called a lot with an empty filename, at least in + // base_unittests. + if (g_log_file_name->empty()) { + return false; + } +#endif + if ((g_logging_destination & LOG_TO_FILE) == 0) return true; -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) + int g_log_file_descriptor = + open(g_log_file_name->c_str(), O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR); + g_log_file = &g_log_file_descriptor; + if (g_log_file_descriptor < 0) + return false; + + lseek(g_log_file_descriptor, 0, SEEK_END); +#elif BUILDFLAG(IS_WIN) // The FILE_APPEND_DATA access mask ensures that the file is atomically // appended to across accesses from multiple threads. // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364399(v=vs.85).aspx @@ -412,7 +459,11 @@ bool InitializeLogFileHandle() { } void CloseFile(FileHandle log) { -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) + if (*log >= 0) { + close(*log); + } +#elif BUILDFLAG(IS_WIN) CloseHandle(log); #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) fclose(log); @@ -434,6 +485,31 @@ void CloseLogFileUnlocked() { g_logging_destination &= ~LOG_TO_FILE; } +#if defined(STARBOARD) +SbLogPriority LogLevelToStarboardLogPriority(int level) { + switch (level) { + case LOG_INFO: + return kSbLogPriorityInfo; + case LOG_WARNING: + return kSbLogPriorityWarning; + case LOG_ERROR: + return kSbLogPriorityError; + case LOG_FATAL: + return kSbLogPriorityFatal; + case LOG_VERBOSE: + default: + if (level <= LOG_VERBOSE) { + // Verbose level can be any negative integer, sanity check its range to + // filter out potential errors. + DCHECK_GE(level, -256); + return kSbLogPriorityInfo; + } + NOTREACHED() << "Unrecognized log level."; + return kSbLogPriorityInfo; + } +} +#endif // defined(STARBOARD) + #if BUILDFLAG(IS_FUCHSIA) inline FuchsiaLogSeverity LogSeverityToFuchsiaLogSeverity( LogSeverity severity) { @@ -458,6 +534,14 @@ inline FuchsiaLogSeverity LogSeverityToFuchsiaLogSeverity( #endif // BUILDFLAG(IS_FUCHSIA) void WriteToFd(int fd, const char* data, size_t length) { +#if defined(STARBOARD) + if (length > 0) { + SbLogRaw(data); + if (data[length - 1] != '\n') { + SbLogRaw("\n"); + } + } +#else size_t bytes_written = 0; long rv; while (bytes_written < length) { @@ -468,6 +552,7 @@ void WriteToFd(int fd, const char* data, size_t length) { } bytes_written += static_cast(rv); } +#endif // defined(STARBOARD) } void SetLogFatalCrashKey(LogMessage* log_message) { @@ -615,6 +700,12 @@ bool ShouldCreateLogMessage(int severity) { // set, or only LOG_TO_FILE is set, since that is useful for local development // and debugging. bool ShouldLogToStderr(int severity) { +#if defined(STARBOARD) + if ((g_logging_destination & LOG_TO_SYSTEM_DEBUG_LOG) != 0) { + // Don't SbLog to stderr if already logging to system debug log. + return false; + } +#endif if (g_logging_destination & LOG_TO_STDERR) return true; @@ -763,7 +854,9 @@ LogMessage::~LogMessage() { } if ((g_logging_destination & LOG_TO_SYSTEM_DEBUG_LOG) != 0) { -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) + SbLog(LogLevelToStarboardLogPriority(severity_), str_newline.c_str()); +#elif BUILDFLAG(IS_WIN) OutputDebugStringA(str_newline.c_str()); #elif BUILDFLAG(IS_APPLE) // In LOG_TO_SYSTEM_DEBUG_LOG mode, log messages are always written to @@ -901,7 +994,11 @@ LogMessage::~LogMessage() { // LOG(ERROR) << "Something went wrong"; // free_something(); // } +#if defined(STARBOARD) + SbLog(LogLevelToStarboardLogPriority(severity_), str_newline.c_str()); +#else WriteToFd(STDERR_FILENO, str_newline.data(), str_newline.size()); +#endif } if ((g_logging_destination & LOG_TO_FILE) != 0) { @@ -914,7 +1011,20 @@ LogMessage::~LogMessage() { base::AutoLock guard(GetLoggingLock()); #endif if (InitializeLogFileHandle()) { -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) + lseek(*g_log_file, 0, SEEK_END); + int written = 0; + while (written < str_newline.length()) { + int result = + HANDLE_EINTR(write(*g_log_file, &(str_newline.c_str()[written]), + str_newline.length() - written)); + base::RecordFileWriteStat(result); + if (result < 0) { + break; + } + written += result; + } +#elif BUILDFLAG(IS_WIN) DWORD num_written; WriteFile(g_log_file, static_cast(str_newline.c_str()), @@ -994,9 +1104,30 @@ void LogMessage::Init(const char* file, int line) { if (g_log_process_id) stream_ << base::GetUniqueIdForProcess() << ':'; if (g_log_thread_id) +#if defined(STARBOARD) + // Logging the thread name is added for Starboard logs. + stream_ << base::PlatformThread::GetName() << '/' + << base::PlatformThread::CurrentId() << ":"; +#else stream_ << base::PlatformThread::CurrentId() << ':'; +#endif if (g_log_timestamp) { -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) + EzTimeValue time_value; + EzTimeValueGetNow(&time_value, NULL); + struct EzTimeExploded local_time = {0}; + EzTimeTExplodeLocal(&(time_value.tv_sec), &local_time); + struct EzTimeExploded* tm_time = &local_time; + stream_ << std::setfill('0') + << std::setw(2) << 1 + tm_time->tm_mon + << std::setw(2) << tm_time->tm_mday + << '/' + << std::setw(2) << tm_time->tm_hour + << std::setw(2) << tm_time->tm_min + << std::setw(2) << tm_time->tm_sec + << '.' << std::setw(6) << time_value.tv_usec + << ':'; +#elif BUILDFLAG(IS_WIN) SYSTEMTIME local_time; GetLocalTime(&local_time); stream_ << std::setfill('0') @@ -1050,7 +1181,9 @@ typedef DWORD SystemErrorCode; #endif SystemErrorCode GetLastSystemErrorCode() { -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) + return SbSystemGetLastError(); +#elif BUILDFLAG(IS_WIN) return ::GetLastError(); #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) return errno; @@ -1058,7 +1191,19 @@ SystemErrorCode GetLastSystemErrorCode() { } BASE_EXPORT std::string SystemErrorCodeToString(SystemErrorCode error_code) { -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) + const int kErrorMessageBufferSize = 256; + char msgbuf[kErrorMessageBufferSize]; + + if (SbSystemGetErrorString(error_code, msgbuf, kErrorMessageBufferSize) > 0) { + // Messages returned by system end with line breaks. + return base::CollapseWhitespaceASCII(msgbuf, true) + + base::StringPrintf(" (%d)", error_code); + } else { + return base::StringPrintf("Error (%d) while retrieving error. (%d)", + GetLastSystemErrorCode(), error_code); + } +#elif BUILDFLAG(IS_WIN) const int kErrorMessageBufferSize = 256; char msgbuf[kErrorMessageBufferSize]; DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; @@ -1077,7 +1222,21 @@ BASE_EXPORT std::string SystemErrorCodeToString(SystemErrorCode error_code) { #endif // BUILDFLAG(IS_WIN) } -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +StarboardErrorLogMessage::StarboardErrorLogMessage(const char* file, + int line, + LogSeverity severity, + SystemErrorCode err) + : LogMessage(file, line, severity), err_(err) {} + +StarboardErrorLogMessage::~StarboardErrorLogMessage() { + stream() << ": " << SystemErrorCodeToString(err_); + // We're about to crash (CHECK). Put |err_| on the stack (by placing it in a + // field) and use Alias in hopes that it makes it into crash dumps. + SystemErrorCode last_error = err_; + base::debug::Alias(&last_error); +} +#elif BUILDFLAG(IS_WIN) Win32ErrorLogMessage::Win32ErrorLogMessage(const char* file, int line, LogSeverity severity, @@ -1160,6 +1319,12 @@ ScopedLoggingSettings::ScopedLoggingSettings() ScopedLoggingSettings::~ScopedLoggingSettings() { // Re-initialize logging via the normal path. This will clean up old file // name and handle state, including re-initializing the VLOG internal state. +#if defined(STARBOARD) + CHECK(InitLogging({ + logging_destination_, + log_file_name_ ? log_file_name_->data() : nullptr, + })) << "~ScopedLoggingSettings() failed to restore settings."; +#else CHECK(InitLogging({ .logging_dest = logging_destination_, .log_file_path = log_file_name_ ? log_file_name_->data() : nullptr, @@ -1167,6 +1332,7 @@ ScopedLoggingSettings::~ScopedLoggingSettings() { .log_format = log_format_ #endif })) << "~ScopedLoggingSettings() failed to restore settings."; +#endif // Restore plain data settings. SetMinLogLevel(min_log_level_); @@ -1184,6 +1350,13 @@ void ScopedLoggingSettings::SetLogFormat(LogFormat log_format) const { void RawLog(int level, const char* message) { if (level >= g_min_log_level && message) { +#if defined(STARBOARD) + SbLogRaw(message); + const size_t message_len = strlen(message); + if (message_len > 0 && message[message_len - 1] != '\n') { + SbLogRaw("\n"); + } +#else const size_t message_len = strlen(message); WriteToFd(STDERR_FILENO, message, message_len); @@ -1197,6 +1370,7 @@ void RawLog(int level, const char* message) { } } while (rv != 1); } +#endif } if (level == LOGGING_FATAL) @@ -1249,7 +1423,8 @@ void ScopedVmoduleSwitches::InitWithSwitches( // Make sure we are only initialized once. CHECK(!scoped_vlog_info_); { -#if defined(LEAK_SANITIZER) && !BUILDFLAG(IS_NACL) +#if (defined(LEAK_SANITIZER) && !BUILDFLAG(IS_NACL)) || \ + (defined(STARBOARD) && defined(ADDRESS_SANITIZER)) // See comments on |g_vlog_info|. ScopedLeakSanitizerDisabler lsan_disabler; #endif // defined(LEAK_SANITIZER) diff --git a/base/logging.h b/base/logging.h index 1ae198e30b0b..aeddb4c1de24 100644 --- a/base/logging.h +++ b/base/logging.h @@ -22,6 +22,16 @@ #include "build/build_config.h" #include "build/chromeos_buildflags.h" +#if defined(STARBOARD) +#include "starboard/common/log.h" +#include "starboard/system.h" +#include "starboard/types.h" +#ifdef COBALT_PENDING_CLEAN_UP +#include "base/check_op.h" +#include "base/notreached.h" +#endif +#endif + #if BUILDFLAG(IS_CHROMEOS) #include #endif @@ -185,7 +195,7 @@ namespace logging { // TODO(avi): do we want to do a unification of character types here? #if BUILDFLAG(IS_WIN) typedef wchar_t PathChar; -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) typedef char PathChar; #endif @@ -207,7 +217,7 @@ enum : uint32_t { // On POSIX platforms, where it may not even be possible to locate the // executable on disk, use stderr. // On Fuchsia, use the Fuchsia logging service. -#if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_NACL) +#if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_NACL) || defined(STARBOARD) LOG_DEFAULT = LOG_TO_SYSTEM_DEBUG_LOG, #elif BUILDFLAG(IS_WIN) LOG_DEFAULT = LOG_TO_FILE, @@ -421,7 +431,7 @@ constexpr LogSeverity LOG_DFATAL = LOGGING_DFATAL; #define COMPACT_GOOGLE_LOG_DFATAL COMPACT_GOOGLE_LOG_EX_DFATAL(LogMessage) #define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_EX_DCHECK(LogMessage) -#if BUILDFLAG(IS_WIN) +#if BUILDFLAG(IS_WIN) || defined(COMPILER_MSVC) // wingdi.h defines ERROR to be 0. When we call LOG(ERROR), it gets // substituted with 0, and it expands to COMPACT_GOOGLE_LOG_0. To allow us // to keep using this syntax, we define this macro to do the same thing @@ -520,7 +530,11 @@ BASE_EXPORT int GetDisableAllVLogLevel(); LAZY_STREAM(VLOG_STREAM(verbose_level), \ VLOG_IS_ON(verbose_level) && (condition)) -#if BUILDFLAG(IS_WIN) +#if defined (STARBOARD) +#define VPLOG_STREAM(verbose_level) \ + ::logging::StarboardErrorLogMessage(__FILE__, __LINE__, -verbose_level, \ + ::logging::GetLastSystemErrorCode()).stream() +#elif BUILDFLAG(IS_WIN) #define VPLOG_STREAM(verbose_level) \ ::logging::Win32ErrorLogMessage(__FILE__, __LINE__, -(verbose_level), \ ::logging::GetLastSystemErrorCode()).stream() @@ -543,7 +557,11 @@ BASE_EXPORT int GetDisableAllVLogLevel(); LOG_IF(FATAL, !(ANALYZER_ASSUME_TRUE(condition))) \ << "Assert failed: " #condition ". " -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +#define PLOG_STREAM(severity) \ + COMPACT_GOOGLE_LOG_EX_ ## severity(StarboardErrorLogMessage, \ + ::logging::GetLastSystemErrorCode()).stream() +#elif BUILDFLAG(IS_WIN) #define PLOG_STREAM(severity) \ COMPACT_GOOGLE_LOG_EX_ ## severity(Win32ErrorLogMessage, \ ::logging::GetLastSystemErrorCode()).stream() @@ -694,7 +712,9 @@ class LogMessageVoidify { void operator&(std::ostream&) { } }; -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +typedef SbSystemError SystemErrorCode; +#elif BUILDFLAG(IS_WIN) typedef unsigned long SystemErrorCode; #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) typedef int SystemErrorCode; @@ -705,7 +725,29 @@ typedef int SystemErrorCode; BASE_EXPORT SystemErrorCode GetLastSystemErrorCode(); BASE_EXPORT std::string SystemErrorCodeToString(SystemErrorCode error_code); -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +// Appends a formatted system message of the GetLastError() type. +class BASE_EXPORT StarboardErrorLogMessage : public LogMessage { + public: + StarboardErrorLogMessage(const char* file, + int line, + LogSeverity severity, + SystemErrorCode err); + + StarboardErrorLogMessage(const StarboardErrorLogMessage&) = delete; + StarboardErrorLogMessage& operator=(const StarboardErrorLogMessage&) = delete; + // Appends the error message before destructing the encapsulated class. + ~StarboardErrorLogMessage() override; + + // std::ostream& stream() { return log_message_.stream(); } + + private: + SystemErrorCode err_; + // StarboardErrorLogMessage log_message_; + + // DISALLOW_COPY_AND_ASSIGN(StarboardErrorLogMessage); +}; +#elif BUILDFLAG(IS_WIN) // Appends a formatted system message of the GetLastError() type. class BASE_EXPORT Win32ErrorLogMessage : public LogMessage { public: diff --git a/base/macros.h b/base/macros.h new file mode 100644 index 000000000000..f6d713f133ec --- /dev/null +++ b/base/macros.h @@ -0,0 +1,24 @@ +#ifndef BASE_MACROS_H_ +#define BASE_MACROS_H_ + +#ifndef COBALT_PENDING_CLEAN_UP +#error "remove these" +#endif + +// Put this in the declarations for a class to be uncopyable. +#define DISALLOW_COPY(TypeName) \ + TypeName(const TypeName&) = delete + +// Put this in the declarations for a class to be unassignable. +#define DISALLOW_ASSIGN(TypeName) TypeName& operator=(const TypeName&) = delete + +// Put this in the declarations for a class to be uncopyable and unassignable. +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + DISALLOW_COPY(TypeName); \ + DISALLOW_ASSIGN(TypeName) + +#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ + TypeName() = delete; \ + DISALLOW_COPY_AND_ASSIGN(TypeName) + +#endif // BASE_MACROS_H_ diff --git a/base/memory/aligned_memory.h b/base/memory/aligned_memory.h index cc4b3d9a2efd..d71a91f8e7bd 100644 --- a/base/memory/aligned_memory.h +++ b/base/memory/aligned_memory.h @@ -11,6 +11,7 @@ #include #include "base/base_export.h" +#include "base/basictypes.h" #include "base/bits.h" #include "base/check.h" #include "build/build_config.h" @@ -35,6 +36,83 @@ namespace base { +#if defined(STARBOARD) +// AlignedMemory is a POD type that gives you a portable way to specify static +// or local stack data of a given alignment and size. For example, if you need +// static storage for a class, but you want manual control over when the object +// is constructed and destructed (you don't want static initialization and +// destruction), use AlignedMemory: +// +// static AlignedMemory my_class; +// +// // ... at runtime: +// new(my_class.void_data()) MyClass(); +// +// // ... use it: +// MyClass* mc = my_class.data_as(); +// +// // ... later, to destruct my_class: +// my_class.data_as()->MyClass::~MyClass(); +// +// Alternatively, a runtime sized aligned allocation can be created: +// +// float* my_array = static_cast(AlignedAlloc(size, alignment)); +// +// // ... later, to release the memory: +// AlignedFree(my_array); +// +// Or using scoped_ptr_malloc: +// +// scoped_ptr_malloc my_array( +// static_cast(AlignedAlloc(size, alignment))); + +// AlignedMemory is specialized for all supported alignments. +// Make sure we get a compiler error if someone uses an unsupported alignment. +template +struct AlignedMemory {}; + +#define BASE_DECL_ALIGNED_MEMORY(byte_alignment) \ + template \ + class AlignedMemory { \ + public: \ + ALIGNAS(byte_alignment) uint8 data_[Size]; \ + void* void_data() { return static_cast(data_); } \ + const void* void_data() const { return static_cast(data_); } \ + template \ + Type* data_as() { \ + return static_cast(void_data()); \ + } \ + template \ + const Type* data_as() const { \ + return static_cast(void_data()); \ + } \ + \ + private: \ + void* operator new(size_t); \ + void operator delete(void*); \ + } + +// Specialization for all alignments is required because MSVC (as of VS 2008) +// does not understand ALIGNAS(ALIGNOF(Type)) or ALIGNAS(template_param). +// Greater than 4096 alignment is not supported by some compilers, so 4096 is +// the maximum specified here. +BASE_DECL_ALIGNED_MEMORY(1); +BASE_DECL_ALIGNED_MEMORY(2); +BASE_DECL_ALIGNED_MEMORY(4); +BASE_DECL_ALIGNED_MEMORY(8); +BASE_DECL_ALIGNED_MEMORY(16); +BASE_DECL_ALIGNED_MEMORY(32); +BASE_DECL_ALIGNED_MEMORY(64); +BASE_DECL_ALIGNED_MEMORY(128); +BASE_DECL_ALIGNED_MEMORY(256); +BASE_DECL_ALIGNED_MEMORY(512); +BASE_DECL_ALIGNED_MEMORY(1024); +BASE_DECL_ALIGNED_MEMORY(2048); +BASE_DECL_ALIGNED_MEMORY(4096); + +#undef BASE_DECL_ALIGNED_MEMORY +#endif // defined(STARBOARD) + // This can be replaced with std::aligned_alloc when we have C++17. // Caveat: std::aligned_alloc requires the size parameter be an integral // multiple of alignment. diff --git a/base/memory/discardable_memory.cc b/base/memory/discardable_memory.cc index c3167a64bb7e..55db6f661043 100644 --- a/base/memory/discardable_memory.cc +++ b/base/memory/discardable_memory.cc @@ -18,6 +18,7 @@ namespace base { namespace features { +#if !defined(STARBOARD) #if BUILDFLAG(IS_POSIX) // Feature flag allowing the use of MADV_FREE discardable memory when there are // multiple supported discardable memory backings. @@ -47,12 +48,14 @@ const base::FeatureParam #endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || // BUILDFLAG(IS_CHROMEOS) +#endif } // namespace features namespace { -#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) +#if defined(STARBOARD) +#elif BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) DiscardableMemoryBacking GetBackingForFieldTrial() { DiscardableMemoryTrialGroup trial_group = @@ -71,7 +74,8 @@ DiscardableMemoryBacking GetBackingForFieldTrial() { } // namespace -#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) +#if defined(STARBOARD) +#elif BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) // Probe capabilities of this device to determine whether we should participate // in the discardable memory backing trial. @@ -101,6 +105,7 @@ DiscardableMemory::DiscardableMemory() = default; DiscardableMemory::~DiscardableMemory() = default; DiscardableMemoryBacking GetDiscardableMemoryBacking() { +#if !defined(STARBOARD) #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) if (DiscardableMemoryBackingFieldTrialIsEnabled()) { return GetBackingForFieldTrial(); @@ -120,6 +125,7 @@ DiscardableMemoryBacking GetDiscardableMemoryBacking() { return DiscardableMemoryBacking::kMadvFree; } #endif // BUILDFLAG(IS_POSIX) +#endif return DiscardableMemoryBacking::kSharedMemory; } diff --git a/base/memory/discardable_memory_allocator.cc b/base/memory/discardable_memory_allocator.cc index 9105ad03f76c..f8d4e49bafa7 100644 --- a/base/memory/discardable_memory_allocator.cc +++ b/base/memory/discardable_memory_allocator.cc @@ -45,8 +45,10 @@ DiscardableMemoryAllocator::AllocateLockedDiscardableMemoryWithRetryOrDie( ReleaseFreeMemory(); memory = allocator->AllocateLockedDiscardableMemory(size); +#if !defined(COBALT_PENDING_CLEAN_UP) if (!memory) TerminateBecauseOutOfMemory(size); +#endif return memory; } diff --git a/base/memory/page_size_starboard.cc b/base/memory/page_size_starboard.cc new file mode 100644 index 000000000000..65d6ed8dd57f --- /dev/null +++ b/base/memory/page_size_starboard.cc @@ -0,0 +1,26 @@ +// Copyright 2023 The Cobalt Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "base/memory/page_size.h" + +#include "base/notreached.h" + +namespace base { + +size_t GetPageSize() { + NOTREACHED(); + return 0; +} + +} \ No newline at end of file diff --git a/base/memory/platform_shared_memory_handle.cc b/base/memory/platform_shared_memory_handle.cc index 3c1180d74226..eacedd55a511 100644 --- a/base/memory/platform_shared_memory_handle.cc +++ b/base/memory/platform_shared_memory_handle.cc @@ -6,7 +6,8 @@ namespace base::subtle { -#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID) +#if defined(STARBOARD) +#elif BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID) ScopedFDPair::ScopedFDPair() = default; ScopedFDPair::ScopedFDPair(ScopedFDPair&&) = default; diff --git a/base/memory/platform_shared_memory_handle.h b/base/memory/platform_shared_memory_handle.h index 9487788463ad..2869f0313a41 100644 --- a/base/memory/platform_shared_memory_handle.h +++ b/base/memory/platform_shared_memory_handle.h @@ -18,11 +18,15 @@ #elif BUILDFLAG(IS_POSIX) #include #include "base/files/scoped_file.h" +#elif defined(STARBOARD) +#include "base/files/scoped_file.h" +#include "starboard/file.h" #endif namespace base::subtle { -#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID) +#if defined(STARBOARD) +#elif BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID) // Helper structs to keep two descriptors on POSIX. It's needed to support // ConvertToReadOnly(). struct BASE_EXPORT FDPair { @@ -49,7 +53,10 @@ struct BASE_EXPORT ScopedFDPair { #endif // Platform-specific shared memory type used by the shared memory system. -#if BUILDFLAG(IS_APPLE) +#if defined(STARBOARD) +using PlatformSharedMemoryHandle = int; +using ScopedPlatformSharedMemoryHandle = ScopedFD; +#elif BUILDFLAG(IS_APPLE) using PlatformSharedMemoryHandle = mach_port_t; using ScopedPlatformSharedMemoryHandle = mac::ScopedMachSendRight; #elif BUILDFLAG(IS_FUCHSIA) diff --git a/base/memory/platform_shared_memory_mapper_starboard.cc b/base/memory/platform_shared_memory_mapper_starboard.cc new file mode 100644 index 000000000000..d150ead0165b --- /dev/null +++ b/base/memory/platform_shared_memory_mapper_starboard.cc @@ -0,0 +1,34 @@ +// Copyright 2023 The Cobalt Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "base/memory/platform_shared_memory_mapper.h" + +#include "base/notreached.h" + +namespace base { + +absl::optional> PlatformSharedMemoryMapper::Map( + subtle::PlatformSharedMemoryHandle handle, + bool write_allowed, + uint64_t offset, + size_t size) { + NOTREACHED(); + return absl::optional>(); +} + +void PlatformSharedMemoryMapper::Unmap(span mapping) { + NOTREACHED(); +} + +} \ No newline at end of file diff --git a/base/memory/platform_shared_memory_region.h b/base/memory/platform_shared_memory_region.h index dc26ef554cf6..684779589074 100644 --- a/base/memory/platform_shared_memory_region.h +++ b/base/memory/platform_shared_memory_region.h @@ -124,7 +124,8 @@ class BASE_EXPORT PlatformSharedMemoryRegion { Mode mode, size_t size, const UnguessableToken& guid); -#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_APPLE) +#if defined(STARBOARD) +#elif BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_APPLE) // Specialized version of Take() for POSIX that takes only one file descriptor // instead of pair. Cannot be used with kWritable |mode|. static PlatformSharedMemoryRegion Take(ScopedFD handle, @@ -211,7 +212,8 @@ class BASE_EXPORT PlatformSharedMemoryRegion { CheckPlatformHandlePermissionsCorrespondToMode); static PlatformSharedMemoryRegion Create(Mode mode, size_t size -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) +#if defined(STARBOARD) +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) , bool executable = false #endif diff --git a/base/memory/platform_shared_memory_region_starboard.cc b/base/memory/platform_shared_memory_region_starboard.cc new file mode 100644 index 000000000000..ab81901f27f9 --- /dev/null +++ b/base/memory/platform_shared_memory_region_starboard.cc @@ -0,0 +1,72 @@ +// Copyright 2023 The Cobalt Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "base/memory/platform_shared_memory_region.h" + +#include "base/notreached.h" + +namespace base { +namespace subtle { + +PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Take( + ScopedPlatformSharedMemoryHandle handle, + Mode mode, + size_t size, + const UnguessableToken& guid) { + NOTREACHED(); + return {}; +} + +bool PlatformSharedMemoryRegion::IsValid() const { + NOTREACHED(); + return false; +} + +PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Duplicate() const { + NOTREACHED(); + return {}; +} + +bool PlatformSharedMemoryRegion::ConvertToReadOnly() { + NOTREACHED(); + return false; +} + +bool PlatformSharedMemoryRegion::ConvertToUnsafe() { + NOTREACHED(); + return false; +} + +PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Create(Mode mode, + size_t size) { + NOTREACHED(); + return {}; +} + +bool PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode( + PlatformSharedMemoryHandle handle, + Mode mode, + size_t size) { + NOTREACHED(); + return false; +} + +PlatformSharedMemoryHandle PlatformSharedMemoryRegion::GetPlatformHandle() + const { + NOTREACHED(); + return 0; +} + +} // namespace subtle +} // namespace base \ No newline at end of file diff --git a/base/memory/ref_counted.h b/base/memory/ref_counted.h index 9ef94d84769d..f70e30748a38 100644 --- a/base/memory/ref_counted.h +++ b/base/memory/ref_counted.h @@ -7,6 +7,7 @@ #include +#include #include #include "base/atomic_ref_count.h" diff --git a/base/memory/scoped_refptr.h b/base/memory/scoped_refptr.h index 79979fe7226a..adedf31b1da4 100644 --- a/base/memory/scoped_refptr.h +++ b/base/memory/scoped_refptr.h @@ -234,6 +234,12 @@ class TRIVIAL_ABI scoped_refptr { constexpr scoped_refptr() = default; +#ifdef COBALT_PENDING_CLEAN_UP + constexpr scoped_refptr(T* p) : ptr_(p) { + if (ptr_) + AddRef(ptr_); + } +#else // Allow implicit construction from nullptr. constexpr scoped_refptr(std::nullptr_t) {} @@ -247,6 +253,7 @@ class TRIVIAL_ABI scoped_refptr { if (ptr_) AddRef(ptr_); } +#endif // Copy constructor. This is required in addition to the copy conversion // constructor below. @@ -281,6 +288,11 @@ class TRIVIAL_ABI scoped_refptr { } T* get() const { return ptr_; } +#if defined(STARBOARD) + // TODO[Cobalt]: remove this implicit convertor and replace all occurances of + // necessary implicit conversion with scoped_refptr.get(). + operator T*() const { return ptr_; } +#endif T& operator*() const { DCHECK(ptr_); @@ -297,7 +309,9 @@ class TRIVIAL_ABI scoped_refptr { return *this; } +#if !defined(STARBOARD) scoped_refptr& operator=(T* p) { return *this = scoped_refptr(p); } +#endif // Unified assignment operator. scoped_refptr& operator=(scoped_refptr r) noexcept { @@ -322,6 +336,13 @@ class TRIVIAL_ABI scoped_refptr { return ptr_ == rhs.get(); } +#if defined(STARBOARD) + template + bool operator!=(U* rhs) const { + return ptr_ != rhs; + } +#endif + template bool operator!=(const scoped_refptr& rhs) const { return !operator==(rhs); @@ -382,6 +403,7 @@ void scoped_refptr::Release(T* ptr) { ptr->Release(); } +#if !defined(STARBOARD) template bool operator==(const scoped_refptr& lhs, const U* rhs) { return lhs.get() == rhs; @@ -421,6 +443,7 @@ template bool operator!=(std::nullptr_t null, const scoped_refptr& rhs) { return !operator==(null, rhs); } +#endif template std::ostream& operator<<(std::ostream& out, const scoped_refptr& p) { diff --git a/base/memory/weak_ptr.h b/base/memory/weak_ptr.h index 5e0ef7e0fea4..de383259ac2c 100644 --- a/base/memory/weak_ptr.h +++ b/base/memory/weak_ptr.h @@ -255,6 +255,11 @@ class TRIVIAL_ABI WeakPtr { T* get() const { return ref_.IsValid() ? ptr_ : nullptr; } +#if defined(STARBOARD) + // TODO[Cobalt]: Remove the implicit convertor. + operator T*() const { return get(); } +#endif + // Provide access to the underlying T as a reference. Will CHECK() if the T // pointee is no longer alive. T& operator*() const { @@ -320,6 +325,7 @@ class TRIVIAL_ABI WeakPtr { RAW_PTR_EXCLUSION T* ptr_ = nullptr; }; +#if !defined(STARBOARD) // Allow callers to compare WeakPtrs against nullptr to test validity. template bool operator!=(const WeakPtr& weak_ptr, std::nullptr_t) { @@ -337,6 +343,7 @@ template bool operator==(std::nullptr_t, const WeakPtr& weak_ptr) { return weak_ptr == nullptr; } +#endif namespace internal { class BASE_EXPORT WeakPtrFactoryBase { @@ -430,6 +437,13 @@ class SupportsWeakPtr : public internal::SupportsWeakPtrBase { return WeakPtr(weak_reference_owner_.GetRef(), static_cast(this)); } +#if defined(STARBOARD) + // Call this method to invalidate all existing weak pointers. + // This may be useful to call explicitly in a destructor of a derived class, + // as the SupportsWeakPtr destructor won't run until late in destruction. + void InvalidateWeakPtrs() { weak_reference_owner_.Invalidate(); } +#endif + protected: ~SupportsWeakPtr() = default; diff --git a/base/message_loop/message_pump_for_io.h b/base/message_loop/message_pump_for_io.h index a1080754f6ae..08bf51a39ec3 100644 --- a/base/message_loop/message_pump_for_io.h +++ b/base/message_loop/message_pump_for_io.h @@ -11,7 +11,9 @@ #include "base/message_loop/ios_cronet_buildflags.h" #include "build/build_config.h" -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +#include "base/message_loop/message_pump_io_starboard.h" +#elif BUILDFLAG(IS_WIN) #include "base/message_loop/message_pump_win.h" #elif BUILDFLAG(IS_IOS) && BUILDFLAG(CRONET_BUILD) #include "base/message_loop/message_pump_io_ios.h" @@ -27,7 +29,9 @@ namespace base { -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +using MessagePumpForIO = MessagePumpIOStarboard; +#elif BUILDFLAG(IS_WIN) // Windows defines it as-is. using MessagePumpForIO = MessagePumpForIO; #elif BUILDFLAG(IS_IOS) && BUILDFLAG(CRONET_BUILD) diff --git a/base/message_loop/message_pump_for_ui.h b/base/message_loop/message_pump_for_ui.h index e0abe310cf0c..639a462946a9 100644 --- a/base/message_loop/message_pump_for_ui.h +++ b/base/message_loop/message_pump_for_ui.h @@ -10,7 +10,9 @@ #include "build/build_config.h" -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +#include "base/message_loop/message_pump_ui_starboard.h" +#elif BUILDFLAG(IS_WIN) #include "base/message_loop/message_pump_win.h" #elif BUILDFLAG(IS_ANDROID) #include "base/message_loop/message_pump_android.h" @@ -28,7 +30,9 @@ namespace base { -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +using MessagePumpForUI = MessagePumpUIStarboard; +#elif BUILDFLAG(IS_WIN) // Windows defines it as-is. using MessagePumpForUI = MessagePumpForUI; #elif BUILDFLAG(IS_ANDROID) diff --git a/base/message_loop/message_pump_io_starboard.cc b/base/message_loop/message_pump_io_starboard.cc new file mode 100644 index 000000000000..c0e025a27007 --- /dev/null +++ b/base/message_loop/message_pump_io_starboard.cc @@ -0,0 +1,345 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "base/message_loop/message_pump_io_starboard.h" + +#include "base/auto_reset.h" +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/observer_list.h" +#include "base/posix/eintr_wrapper.h" +#include "base/time/time.h" +#include "starboard/common/socket.h" +#include "starboard/socket_waiter.h" + +namespace base { + +MessagePumpIOStarboard::SocketWatcher::SocketWatcher(const Location& from_here) + : created_from_location_(from_here), + interests_(kSbSocketWaiterInterestNone), + socket_(kSbSocketInvalid), + pump_(nullptr), + watcher_(nullptr), + weak_factory_(this) {} + +MessagePumpIOStarboard::SocketWatcher::~SocketWatcher() { + if (SbSocketIsValid(socket_)) { + StopWatchingSocket(); + } +} + +bool MessagePumpIOStarboard::SocketWatcher::UnregisterInterest(int interests) { + bool result = true; + if (SbSocketIsValid(socket_)) { + // This may get called multiple times from TCPSocketStarboard. + if (pump_) { + result = pump_->UnregisterInterest(socket_, interests, this); + } else + Release(); + } else { + interests_ = 0; + } + if (!interests_) { + DCHECK(!SbSocketIsValid(socket_)); + pump_ = nullptr; + watcher_ = nullptr; + } + return result; +} + +bool MessagePumpIOStarboard::SocketWatcher::StopWatchingSocket() { + return UnregisterInterest(kSbSocketWaiterInterestRead | + kSbSocketWaiterInterestWrite); +} + +void MessagePumpIOStarboard::SocketWatcher::Init(SbSocket socket, + bool persistent) { + DCHECK(socket); + DCHECK(!socket_); + socket_ = socket; + persistent_ = persistent; +} + +SbSocket MessagePumpIOStarboard::SocketWatcher::Release() { + SbSocket socket = socket_; + socket_ = kSbSocketInvalid; + return socket; +} + +void MessagePumpIOStarboard::SocketWatcher::OnSocketReadyToRead( + SbSocket socket, + MessagePumpIOStarboard* pump) { + if (!watcher_) + return; + pump->WillProcessIOEvent(); + watcher_->OnSocketReadyToRead(socket); + pump->DidProcessIOEvent(); +} + +void MessagePumpIOStarboard::SocketWatcher::OnSocketReadyToWrite( + SbSocket socket, + MessagePumpIOStarboard* pump) { + if (!watcher_) + return; + pump->WillProcessIOEvent(); + watcher_->OnSocketReadyToWrite(socket); + pump->DidProcessIOEvent(); +} + +MessagePumpIOStarboard::MessagePumpIOStarboard() + : keep_running_(true), + processed_io_events_(false), + waiter_(SbSocketWaiterCreate()) {} + +MessagePumpIOStarboard::~MessagePumpIOStarboard() { + DCHECK(SbSocketWaiterIsValid(waiter_)); + SbSocketWaiterDestroy(waiter_); +} + +bool MessagePumpIOStarboard::UnregisterInterest(SbSocket socket, + int dropped_interests, + SocketWatcher* controller) { + DCHECK(SbSocketIsValid(socket)); + DCHECK(controller); + DCHECK(dropped_interests == kSbSocketWaiterInterestRead || + dropped_interests == kSbSocketWaiterInterestWrite || + dropped_interests == + (kSbSocketWaiterInterestRead | kSbSocketWaiterInterestWrite)); + DCHECK_CALLED_ON_VALID_THREAD(watch_socket_caller_checker_); + + // Make sure we don't pick up any funky internal masks. + int old_interest_mask = + controller->interests() & + (kSbSocketWaiterInterestRead | kSbSocketWaiterInterestWrite); + int interests = old_interest_mask & (~dropped_interests); + if (interests == old_interest_mask) { + // Interests didn't change, return. + return true; + } + + SbSocket old_socket = controller->Release(); + if (SbSocketIsValid(old_socket)) { + // It's illegal to use this function to listen on 2 separate fds with the + // same |controller|. + if (old_socket != socket) { + NOTREACHED() << "Sockets don't match " << old_socket << "!=" << socket; + return false; + } + + // Must disarm the event before we can reuse it. + SbSocketWaiterRemove(waiter_, old_socket); + } else { + interests = kSbSocketWaiterInterestNone; + } + controller->set_interests(interests); + + if (!SbSocketIsValid(socket)) { + NOTREACHED() << "Invalid socket " << socket; + return false; + } + + if (interests) { + // Set current interest mask and waiter for this event. + if (!SbSocketWaiterAdd(waiter_, socket, controller, + OnSocketWaiterNotification, interests, + controller->persistent())) { + return false; + } + controller->Init(socket, controller->persistent()); + } + return true; +} + +bool MessagePumpIOStarboard::Watch(SbSocket socket, + bool persistent, + int interests, + SocketWatcher* controller, + Watcher* delegate) { + DCHECK(SbSocketIsValid(socket)); + DCHECK(controller); + DCHECK(delegate); + DCHECK(interests == kSbSocketWaiterInterestRead || + interests == kSbSocketWaiterInterestWrite || + interests == + (kSbSocketWaiterInterestRead | kSbSocketWaiterInterestWrite)); + if ((controller->interests() & interests) == interests) { + // Interests didn't change, return. + return true; + } + // Watch should be called on the pump thread. It is not threadsafe, and your + // watcher may never be registered. + DCHECK_CALLED_ON_VALID_THREAD(watch_socket_caller_checker_); + + SbSocket old_socket = controller->Release(); + if (SbSocketIsValid(old_socket)) { + // It's illegal to use this function to listen on 2 separate fds with the + // same |controller|. + if (old_socket != socket) { + NOTREACHED() << "Sockets don't match " << old_socket << "!=" << socket; + return false; + } + + // Make sure we don't pick up any funky internal masks. + int old_interest_mask = + controller->interests() & + (kSbSocketWaiterInterestRead | kSbSocketWaiterInterestWrite); + + // Combine old/new event masks. + interests |= old_interest_mask; + + // Must disarm the event before we can reuse it. + SbSocketWaiterRemove(waiter_, old_socket); + } + + if (!SbSocketIsValid(socket)) { + NOTREACHED() << "Invalid socket " << socket; + return false; + } + + // Set current interest mask and waiter for this event. + if (!SbSocketWaiterAdd(waiter_, socket, controller, + OnSocketWaiterNotification, interests, persistent)) { + return false; + } + + controller->Init(socket, persistent); + controller->set_watcher(delegate); + controller->set_pump(this); + controller->set_interests(interests); + return true; +} + +bool MessagePumpIOStarboard::StopWatching(SbSocket socket) { + return SbSocketWaiterRemove(waiter_, socket); +} + +void MessagePumpIOStarboard::AddIOObserver(IOObserver* obs) { + io_observers_.AddObserver(obs); +} + +void MessagePumpIOStarboard::RemoveIOObserver(IOObserver* obs) { + io_observers_.RemoveObserver(obs); +} + +// Reentrant! +void MessagePumpIOStarboard::Run(Delegate* delegate) { + AutoReset auto_reset_keep_running(&keep_running_, true); + + for (;;) { + Delegate::NextWorkInfo next_work_info = delegate->DoWork(); + bool immediate_work_available = next_work_info.is_immediate(); + + if (should_quit()) + break; + + // NOTE: We need to have a wake-up pending any time there is work queued, + // and the MessageLoop only wakes up the pump when the work queue goes from + // 0 tasks to 1 task. If any work is scheduled on this MessageLoop (from + // another thread) anywhere in between the call to DoWork() above and the + // call to SbSocketWaiterWaitTimed() below, SbSocketWaiterWaitTimed() will + // consume a wake-up, but leave the work queued. This will cause the + // blocking wait further below to hang forever, no matter how many more + // items are added to the queue. To resolve this, if this wait consumes a + // wake-up, we set did_work to true so we will jump back to the top of the + // loop and call delegate->DoWork() before we decide to block. + SbSocketWaiterResult result = SbSocketWaiterWaitTimed(waiter_, 0); + DCHECK_NE(kSbSocketWaiterResultInvalid, result); + + bool attempt_more_work = + (result == kSbSocketWaiterResultWokenUp) || immediate_work_available || processed_io_events_; + processed_io_events_ = false; + + if (should_quit()) + break; + + if (attempt_more_work) + continue; + + attempt_more_work = delegate->DoIdleWork(); + + if (should_quit()) + break; + + if (attempt_more_work) + continue; + + if (next_work_info.delayed_run_time.is_max()) { + SbSocketWaiterWait(waiter_); + } else { + SbSocketWaiterWaitTimed(waiter_, next_work_info.remaining_delay().InMicroseconds()); + } + + if (should_quit()) + break; + } +} + +void MessagePumpIOStarboard::Quit() { + // Tell both the SbObjectWaiter and Run that they should break out of their + // loops. + keep_running_ = false; + ScheduleWork(); +} + +void MessagePumpIOStarboard::ScheduleWork() { + SbSocketWaiterWakeUp(waiter_); +} + +void MessagePumpIOStarboard::ScheduleDelayedWork( + const Delegate::NextWorkInfo& next_work_info) { + // We know that we can't be blocked on Wait right now since this method can + // only be called on the same thread as Run, so we only need to update our + // record of how long to sleep when we do sleep. +} + +void MessagePumpIOStarboard::WillProcessIOEvent() { + for (IOObserver& observer : io_observers_) { + observer.WillProcessIOEvent(); + } +} + +void MessagePumpIOStarboard::DidProcessIOEvent() { + for (IOObserver& observer : io_observers_) { + observer.DidProcessIOEvent(); + } +} + +// static +void MessagePumpIOStarboard::OnSocketWaiterNotification(SbSocketWaiter waiter, + SbSocket socket, + void* context, + int ready_interests) { + base::WeakPtr controller = + static_cast(context)->weak_factory_.GetWeakPtr(); + DCHECK(controller.get()); + + MessagePumpIOStarboard* pump = controller->pump(); + pump->processed_io_events_ = true; + + // If not persistent, the watch has been released at this point. + if (!controller->persistent()) { + controller->Release(); + } + + if (ready_interests & kSbSocketWaiterInterestWrite) { + controller->OnSocketReadyToWrite(socket, pump); + } + + // Check |controller| in case it's been deleted previously. + if (controller.get() && ready_interests & kSbSocketWaiterInterestRead) { + controller->OnSocketReadyToRead(socket, pump); + } +} + +} // namespace base diff --git a/base/message_loop/message_pump_io_starboard.h b/base/message_loop/message_pump_io_starboard.h new file mode 100644 index 000000000000..8b1dc277c4eb --- /dev/null +++ b/base/message_loop/message_pump_io_starboard.h @@ -0,0 +1,177 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef BASE_MESSAGE_PUMP_IO_STARBOARD_H_ +#define BASE_MESSAGE_PUMP_IO_STARBOARD_H_ + +#include "base/compiler_specific.h" +#include "base/memory/weak_ptr.h" +#include "base/message_loop/message_pump.h" +#include "base/observer_list.h" +#include "base/threading/thread_checker.h" +#include "base/time/time.h" +#include "starboard/common/socket.h" +#include "starboard/socket_waiter.h" + +namespace base { + +// Class to monitor sockets and issue callbacks when sockets are ready for I/O. +class BASE_EXPORT MessagePumpIOStarboard : public MessagePump { + public: + class IOObserver : public CheckedObserver { + public: + IOObserver() {} + virtual ~IOObserver() {} + + // An IOObserver is an object that receives IO notifications from the + // MessagePump. + // + // NOTE: An IOObserver should not do much work, it should return extremely + // quickly! + virtual void WillProcessIOEvent() = 0; + virtual void DidProcessIOEvent() = 0; + }; + + // Used with WatchFileDescriptor to asynchronously monitor the I/O readiness + // of a file descriptor. + class Watcher { + public: + // These methods are called from MessageLoop::Run when a socket can be + // interacted with without blocking. + virtual void OnSocketReadyToRead(SbSocket socket) {} + virtual void OnSocketReadyToWrite(SbSocket socket) {} + + protected: + virtual ~Watcher() {} + }; + + // Object returned by WatchSocket to manage further watching. + class SocketWatcher { + public: + SocketWatcher(const Location& from_here); + ~SocketWatcher(); // Implicitly calls StopWatchingSocket. + + SocketWatcher(const SocketWatcher&) = delete; + SocketWatcher& operator=(const SocketWatcher&) = delete; + + // Unregisters the interests for watching the socket, always safe to call. + // No-op if there's nothing to do. + bool UnregisterInterest(int interests); + + // Stops watching the socket, always safe to call. No-op if there's nothing + // to do. + bool StopWatchingSocket(); + + bool persistent() const { return persistent_; } + + private: + friend class MessagePumpIOStarboard; + friend class MessagePumpIOStarboardTest; + + // Called by MessagePumpIOStarboard. + void Init(SbSocket socket, bool persistent); + SbSocket Release(); + + int interests() const { return interests_; } + void set_interests(int interests) { interests_ = interests; } + + void set_pump(MessagePumpIOStarboard* pump) { pump_ = pump; } + MessagePumpIOStarboard* pump() const { return pump_; } + + void set_watcher(Watcher* watcher) { watcher_ = watcher; } + + void OnSocketReadyToRead(SbSocket socket, MessagePumpIOStarboard* pump); + void OnSocketReadyToWrite(SbSocket socket, MessagePumpIOStarboard* pump); + + const Location created_from_location_; + int interests_; + SbSocket socket_; + bool persistent_; + MessagePumpIOStarboard* pump_; + Watcher* watcher_; + base::WeakPtrFactory weak_factory_; + }; + + MessagePumpIOStarboard(); + virtual ~MessagePumpIOStarboard(); + + MessagePumpIOStarboard(const MessagePumpIOStarboard&) = delete; + MessagePumpIOStarboard& operator=(const MessagePumpIOStarboard&) = delete; + + // Have the current thread's message loop watch for a a situation in which + // reading/writing to the socket can be performed without blocking. Callers + // must provide a preallocated SocketWatcher object which can later be used to + // manage the lifetime of this event. If a SocketWatcher is passed in which + // is already attached to a socket, then the effect is cumulative i.e. after + // the call |controller| will watch both the previous event and the new one. + // If an error occurs while calling this method in a cumulative fashion, the + // event previously attached to |controller| is aborted. Returns true on + // success. Must be called on the same thread the message_pump is running on. + bool Watch(SbSocket socket, + bool persistent, + int interests, + SocketWatcher* controller, + Watcher* delegate); + + // Removes an interest from a socket, and stops watching the socket if needed. + bool UnregisterInterest(SbSocket socket, + int dropped_interests, + SocketWatcher* controller); + + // Stops watching the socket. + bool StopWatching(SbSocket socket); + + void AddIOObserver(IOObserver* obs); + void RemoveIOObserver(IOObserver* obs); + + // MessagePump methods: + virtual void Run(Delegate* delegate) override; + virtual void Quit() override; + virtual void ScheduleWork() override; + virtual void ScheduleDelayedWork(const Delegate::NextWorkInfo& next_work_info) override; + + private: + friend class MessagePumpIOStarboardTest; + + void WillProcessIOEvent(); + void DidProcessIOEvent(); + + // Called by SbSocketWaiter to tell us a registered socket can be read and/or + // written to. + static void OnSocketWaiterNotification(SbSocketWaiter waiter, + SbSocket socket, + void* context, + int ready_interests); + + bool should_quit() const { return !keep_running_; } + + // This flag is set to false when Run should return. + bool keep_running_; + + // This flag is set if the Socket Waiter has processed I/O events. + bool processed_io_events_; + + // Starboard socket waiter dispatcher. Waits for all sockets registered with + // it, and sends readiness callbacks when a socket is ready for I/O. + SbSocketWaiter waiter_; + + ObserverList io_observers_; + THREAD_CHECKER(watch_socket_caller_checker_); +}; + +using MessagePumpForIO = MessagePumpIOStarboard; + +} // namespace base + +#endif // BASE_MESSAGE_PUMP_IO_STARBOARD_H_ diff --git a/base/message_loop/message_pump_io_starboard_unittest.cc b/base/message_loop/message_pump_io_starboard_unittest.cc new file mode 100644 index 000000000000..8747ca52d6f4 --- /dev/null +++ b/base/message_loop/message_pump_io_starboard_unittest.cc @@ -0,0 +1,279 @@ +// Copyright 2024 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "base/message_loop/message_pump_io_starboard.h" + +#include + +#include + +#include "base/functional/bind.h" +#include "base/run_loop.h" +#include "base/synchronization/waitable_event.h" +#include "base/synchronization/waitable_event_watcher.h" +#include "base/task/single_thread_task_executor.h" +#include "base/task/single_thread_task_runner.h" +#include "base/test/gtest_util.h" +#include "base/test/task_environment.h" +#include "base/threading/thread.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +class MessagePumpIOStarboardTest : public testing::Test { + protected: + MessagePumpIOStarboardTest() + : task_environment_(std::make_unique( + test::SingleThreadTaskEnvironment::MainThreadType::DEFAULT)), + event_(WaitableEvent::ResetPolicy::AUTOMATIC, + WaitableEvent::InitialState::NOT_SIGNALED), + io_thread_("MessagePumpIOStarboardTestIOThread") {} + ~MessagePumpIOStarboardTest() override = default; + + void SetUp() override { + Thread::Options options(MessagePumpType::IO, 0); + ASSERT_TRUE(io_thread_.StartWithOptions(std::move(options))); + socket_ = SbSocketCreate(SbSocketAddressType::kSbSocketAddressTypeIpv4, SbSocketProtocol::kSbSocketProtocolTcp); + SbSocketIsValid(socket_); + } + + void TearDown() override { + // Some tests watch |pipefds_| from the |io_thread_|. The |io_thread_| must + // thus be joined to ensure those watches are complete before closing the + // pipe. + io_thread_.Stop(); + + SbSocketDestroy(socket_); + } + + std::unique_ptr CreateMessagePump() { + return std::make_unique(); + } + + SbSocket socket() { + return socket_; + } + + scoped_refptr io_runner() const { + return io_thread_.task_runner(); + } + + void SimulateIOEvent(MessagePumpIOStarboard::SocketWatcher* controller) { + MessagePumpIOStarboard::OnSocketWaiterNotification( + nullptr, nullptr, controller, + (kSbSocketWaiterInterestRead | kSbSocketWaiterInterestWrite)); + } + + std::unique_ptr task_environment_; + + WaitableEvent event_; + + private: + Thread io_thread_; + SbSocket socket_; +}; + +namespace { + +// Concrete implementation of MessagePumpIOStarboard::Watcher that does +// nothing useful. +class StupidWatcher : public MessagePumpIOStarboard::Watcher { + public: + ~StupidWatcher() override = default; + + // MessagePumpIOStarboard::Watcher interface + void OnSocketReadyToRead(SbSocket socket) override {} + void OnSocketReadyToWrite(SbSocket socket) override {} +}; + +// Death tests not supported. +TEST_F(MessagePumpIOStarboardTest, DISABLED_QuitOutsideOfRun) { + std::unique_ptr pump = CreateMessagePump(); + ASSERT_DCHECK_DEATH(pump->Quit()); +} + +class BaseWatcher : public MessagePumpIOStarboard::Watcher { + public: + BaseWatcher() = default; + ~BaseWatcher() override = default; + + // MessagePumpIOStarboard::Watcher interface + void OnSocketReadyToRead(SbSocket socket) override { NOTREACHED(); } + void OnSocketReadyToWrite(SbSocket socket) override { NOTREACHED(); } +}; + +class DeleteWatcher : public BaseWatcher { + public: + explicit DeleteWatcher( + std::unique_ptr controller) + : controller_(std::move(controller)) {} + + ~DeleteWatcher() override { DCHECK(!controller_); } + + MessagePumpIOStarboard::SocketWatcher* controller() { + return controller_.get(); + } + + void OnSocketReadyToWrite(SbSocket socket) override { + DCHECK(controller_); + controller_.reset(); + } + + private: + std::unique_ptr controller_; +}; + +// Fails on some platforms. +TEST_F(MessagePumpIOStarboardTest, DISABLED_DeleteWatcher) { + DeleteWatcher delegate( + std::make_unique(FROM_HERE)); + std::unique_ptr pump = CreateMessagePump(); + pump->Watch(socket(), + /*persistent=*/false, + (kSbSocketWaiterInterestRead | kSbSocketWaiterInterestWrite), + delegate.controller(), &delegate); + SimulateIOEvent(delegate.controller()); +} + +class StopWatcher : public BaseWatcher { + public: + explicit StopWatcher(MessagePumpIOStarboard::SocketWatcher* controller) + : controller_(controller) {} + + ~StopWatcher() override = default; + + void OnSocketReadyToWrite(SbSocket socket) override { + controller_->StopWatchingSocket(); + } + + private: + raw_ptr controller_ = nullptr; +}; + +// Fails on some platforms. +TEST_F(MessagePumpIOStarboardTest, DISABLED_StopWatcher) { + std::unique_ptr pump = CreateMessagePump(); + MessagePumpIOStarboard::SocketWatcher controller(FROM_HERE); + StopWatcher delegate(&controller); + pump->Watch(socket(), + /*persistent=*/false, + (kSbSocketWaiterInterestRead | kSbSocketWaiterInterestWrite), + &controller, &delegate); + SimulateIOEvent(&controller); +} + +void QuitMessageLoopAndStart(OnceClosure quit_closure) { + std::move(quit_closure).Run(); + + RunLoop runloop(RunLoop::Type::kNestableTasksAllowed); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE, + runloop.QuitClosure()); + runloop.Run(); +} + +class NestedPumpWatcher : public MessagePumpIOStarboard::Watcher { + public: + NestedPumpWatcher() = default; + ~NestedPumpWatcher() override = default; + + void OnSocketReadyToRead(SbSocket socket) override { + RunLoop runloop; + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&QuitMessageLoopAndStart, runloop.QuitClosure())); + runloop.Run(); + } + + void OnSocketReadyToWrite(SbSocket socket) override {} +}; + +// Fails on some platforms. +TEST_F(MessagePumpIOStarboardTest, DISABLED_NestedPumpWatcher) { + NestedPumpWatcher delegate; + std::unique_ptr pump = CreateMessagePump(); + MessagePumpIOStarboard::SocketWatcher controller(FROM_HERE); + pump->Watch(socket(), + /*persistent=*/false, kSbSocketWaiterInterestRead, &controller, + &delegate); + SimulateIOEvent(&controller); +} + +void FatalClosure() { + FAIL() << "Reached fatal closure."; +} + +class QuitWatcher : public BaseWatcher { + public: + QuitWatcher(base::OnceClosure quit_closure) + : quit_closure_(std::move(quit_closure)) {} + + void OnSocketReadyToRead(SbSocket socket) override { + // Post a fatal closure to the MessageLoop before we quit it. + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&FatalClosure)); + + if (quit_closure_) + std::move(quit_closure_).Run(); + } + + private: + base::OnceClosure quit_closure_; +}; + +void WriteSocketWrapper(MessagePumpIOStarboard* pump, + WaitableEvent* event) { + pump->ScheduleWork(); +} + +// Fails on some platforms. +TEST_F(MessagePumpIOStarboardTest, DISABLED_QuitWatcher) { + // Delete the old TaskEnvironment so that we can manage our own one here. + task_environment_.reset(); + + std::unique_ptr executor_pump = CreateMessagePump(); + MessagePumpIOStarboard* pump = executor_pump.get(); + SingleThreadTaskExecutor executor(std::move(executor_pump)); + RunLoop run_loop; + QuitWatcher delegate(run_loop.QuitClosure()); + MessagePumpIOStarboard::SocketWatcher controller(FROM_HERE); + std::unique_ptr watcher(new WaitableEventWatcher); + + // Tell the pump to watch the pipe. + pump->Watch(socket(), + /*persistent=*/false, kSbSocketWaiterInterestRead, &controller, + &delegate); + + // Make the IO thread wait for |event_| before writing to pipefds[1]. + const char buf = 0; + WaitableEventWatcher::EventCallback write_socket_task = + BindOnce(&WriteSocketWrapper, base::Unretained(pump)); + io_runner()->PostTask( + FROM_HERE, BindOnce(IgnoreResult(&WaitableEventWatcher::StartWatching), + Unretained(watcher.get()), &event_, + std::move(write_socket_task), io_runner())); + + // Queue task to signal |event_|. + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&WaitableEvent::Signal, Unretained(&event_))); + + // Now run the MessageLoop. + run_loop.Run(); + + // StartWatching can move |watcher| to IO thread. Release on IO thread. + io_runner()->PostTask(FROM_HERE, BindOnce(&WaitableEventWatcher::StopWatching, + Owned(watcher.release()))); +} + +} // namespace + +} // namespace base diff --git a/base/message_loop/message_pump_ui_starboard.cc b/base/message_loop/message_pump_ui_starboard.cc new file mode 100644 index 000000000000..142be66d5364 --- /dev/null +++ b/base/message_loop/message_pump_ui_starboard.cc @@ -0,0 +1,173 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "base/message_loop/message_pump_ui_starboard.h" + +#include "base/logging.h" +#include "base/time/time.h" +#include "starboard/event.h" +#include "starboard/system.h" + +namespace base { + +namespace { + +void CallMessagePumpImmediate(void* context) { + DCHECK(context); + MessagePumpUIStarboard* pump = + reinterpret_cast(context); + pump->CancelImmediate(); + pump->RunUntilIdle(); +} + +void CallMessagePumpDelayed(void* context) { + DCHECK(context); + MessagePumpUIStarboard* pump = + reinterpret_cast(context); + pump->CancelDelayed(); + pump->RunUntilIdle(); +} + +} // namespace + +MessagePumpUIStarboard::MessagePumpUIStarboard() : delegate_(nullptr) {} + +void MessagePumpUIStarboard::CancelDelayed() { + base::AutoLock auto_lock(outstanding_events_lock_); + CancelDelayedLocked(); +} + +void MessagePumpUIStarboard::CancelImmediate() { + base::AutoLock auto_lock(outstanding_events_lock_); + CancelImmediateLocked(); +} + +void MessagePumpUIStarboard::RunUntilIdle() { + DCHECK(delegate_); +#if !defined(COBALT_BUILD_TYPE_GOLD) + // Abort if this is a QA build to signal that this is unexpected. + CHECK(delegate_); +#endif + + if (should_quit()) + return; + + for (;;) { + // Do some work and see if the next task is ready right away. + Delegate::NextWorkInfo next_work_info = delegate_->DoWork(); + bool attempt_more_work = next_work_info.is_immediate(); + + if (should_quit()) + break; + + if (attempt_more_work) + continue; + + attempt_more_work = delegate_->DoIdleWork(); + + if (should_quit()) + break; + + if (attempt_more_work) + continue; + + // If there is delayed work. + if (!next_work_info.delayed_run_time.is_max()) { + ScheduleDelayedWork(next_work_info); + } + + // Idle. + break; + } +} + +void MessagePumpUIStarboard::Run(Delegate* delegate) { + // This should never be called because we are not like a normal message pump + // where we loop until told to quit. We are providing a MessagePump interface + // on top of an externally-owned message pump. We want to exist and be able to + // schedule work, but the actual for(;;) loop is owned by Starboard. + NOTREACHED(); +} + +void MessagePumpUIStarboard::Attach(Delegate* delegate) { + // Since the Looper is controlled by the UI thread or JavaHandlerThread, we + // can't use Run() like we do on other platforms or we would prevent Java + // tasks from running. Instead we create and initialize a run loop here, then + // return control back to the Looper. + + SetDelegate(delegate); +} + +void MessagePumpUIStarboard::Quit() { + delegate_ = nullptr; + CancelAll(); +} + +void MessagePumpUIStarboard::ScheduleWork() { + // Check if outstanding event already exists. + if (outstanding_event_) + return; + + base::AutoLock auto_lock(outstanding_events_lock_); + outstanding_event_ = + SbEventSchedule(&CallMessagePumpImmediate, this, 0); +} + +void MessagePumpUIStarboard::ScheduleDelayedWork( + const Delegate::NextWorkInfo& next_work_info) { + if (next_work_info.is_immediate() || next_work_info.delayed_run_time.is_max()) { + return; + } + + TimeDelta delay = next_work_info.remaining_delay(); + if (delay.is_negative()) { + delay = base::TimeDelta(); + } + + base::AutoLock auto_lock(outstanding_events_lock_); + // Make sure any outstanding delayed event is canceled. + CancelDelayedLocked(); + outstanding_delayed_event_ = + SbEventSchedule(&CallMessagePumpDelayed, this, delay.InMicroseconds()); +} + +void MessagePumpUIStarboard::CancelAll() { + base::AutoLock auto_lock(outstanding_events_lock_); + CancelImmediateLocked(); + CancelDelayedLocked(); +} + +void MessagePumpUIStarboard::CancelImmediateLocked() { + outstanding_events_lock_.AssertAcquired(); + if (!outstanding_event_) + return; + + SbEventCancel(*outstanding_event_); + outstanding_event_.reset(); +} + +void MessagePumpUIStarboard::CancelDelayedLocked() { + outstanding_events_lock_.AssertAcquired(); + if (!outstanding_delayed_event_) + return; + + SbEventCancel(*outstanding_delayed_event_); + outstanding_delayed_event_.reset(); +} + +MessagePump::Delegate* MessagePumpForUI::SetDelegate(Delegate* delegate) { + return std::exchange(delegate_, delegate); +} + +} // namespace base diff --git a/base/message_loop/message_pump_ui_starboard.h b/base/message_loop/message_pump_ui_starboard.h new file mode 100644 index 000000000000..a60b11c05477 --- /dev/null +++ b/base/message_loop/message_pump_ui_starboard.h @@ -0,0 +1,103 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef BASE_MESSAGE_PUMP_UI_STARBOARD_H_ +#define BASE_MESSAGE_PUMP_UI_STARBOARD_H_ + +#include + +#include "base/base_export.h" +#include "base/message_loop/message_pump.h" +#include "base/synchronization/lock.h" +#include "base/time/time.h" +#include "starboard/event.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +namespace base { + +// MessagePump that integrates with the Starboard message pump. Starboard has +// its own main loop, so the MessagePumpUIStarboard just piggybacks on that +// rather than implementing its own pump. +// +// To adopt Starboard's message pump, one simply has to create a MessageLoop of +// TYPE_UI from the Starboard message pump thread. A traditional place to do +// this would be in the kSbEventStart event handler. One has to be sure to keep +// the MessageLoop alive for as long as the application wants to use the +// Starboard message pump as a MessageLoop. That would typically be for the +// entire lifetime of the application, and in that case, the MessageLoop would +// traditionally be deleted in the kSbEventStop handler. +class BASE_EXPORT MessagePumpUIStarboard : public MessagePump { + public: + MessagePumpUIStarboard(); + virtual ~MessagePumpUIStarboard() { Quit(); } + + MessagePumpUIStarboard(const MessagePumpUIStarboard&) = delete; + MessagePumpUIStarboard& operator=(const MessagePumpUIStarboard&) = delete; + + // Cancels delayed schedule callback events. + void CancelDelayed(); + + // Cancels immediate schedule callback events. + void CancelImmediate(); + + // Runs one iteration of the run loop, and schedules another iteration if + // necessary. + void RunUntilIdle(); + + // --- MessagePump Implementation --- + + virtual void Run(Delegate* delegate) override; + virtual void Quit() override; + virtual void ScheduleWork() override; + virtual void ScheduleDelayedWork( + const Delegate::NextWorkInfo& next_work_info) override; + + // Attaches |delegate| to this native MessagePump. |delegate| will from then + // on be invoked by the native loop to process application tasks. + virtual void Attach(Delegate* delegate); + + protected: + Delegate* SetDelegate(Delegate* delegate); + + private: + // Cancels all outstanding scheduled callback events, if any. + void CancelAll(); + + // Cancel workhorse that assumes |outstanding_events_lock_| is locked. + void CancelImmediateLocked(); + + // Cancel delayed workhorse that assumes |outstanding_events_lock_| is locked. + void CancelDelayedLocked(); + + // If the delegate has been removed, Quit() has been called. + bool should_quit() const { return delegate_ == nullptr; } + + // The MessagePump::Delegate configured in Start(). + Delegate* delegate_; + + // Lock protecting outstanding scheduled callback events. + base::Lock outstanding_events_lock_; + + // The set of outstanding scheduled callback events for immediate work. + absl::optional outstanding_event_; + + // The set of outstanding scheduled callback events for delayed work. + absl::optional outstanding_delayed_event_; +}; + +using MessagePumpForUI = MessagePumpUIStarboard; + +} // namespace base + +#endif // BASE_MESSAGE_PUMP_UI_STARBOARD_H_ diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc index fa6a8cc6c06e..d1853f853f18 100644 --- a/base/metrics/field_trial.cc +++ b/base/metrics/field_trial.cc @@ -189,14 +189,14 @@ void AddFeatureAndFieldTrialFlags(CommandLine* cmd_line) { #endif // !BUILDFLAG(IS_IOS) void OnOutOfMemory(size_t size) { -#if BUILDFLAG(IS_NACL) +#if BUILDFLAG(IS_NACL) || defined(STARBOARD) NOTREACHED(); #else TerminateBecauseOutOfMemory(size); #endif } -#if !BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_IOS) +#if !BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_IOS) && !defined(STARBOARD) // Returns whether the operation succeeded. bool DeserializeGUIDFromStringPieces(StringPiece first, StringPiece second, @@ -215,7 +215,7 @@ bool DeserializeGUIDFromStringPieces(StringPiece first, *guid = token.value(); return true; } -#endif // !BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_IOS) +#endif // !BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_IOS) && !defined(STARBOARD) } // namespace @@ -694,7 +694,7 @@ void FieldTrialList::CreateTrialsFromCommandLine(const CommandLine& cmd_line, uint32_t fd_key) { global_->create_trials_from_command_line_called_ = true; -#if !BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_IOS) +#if !BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_IOS) && !defined(STARBOARD) if (cmd_line.HasSwitch(switches::kFieldTrialHandle)) { std::string switch_value = cmd_line.GetSwitchValueASCII(switches::kFieldTrialHandle); @@ -748,7 +748,7 @@ void FieldTrialList::PopulateLaunchOptionsWithFieldTrialState( return; } -#if !BUILDFLAG(IS_NACL) +#if !BUILDFLAG(IS_NACL) && !defined(STARBOARD) global_->field_trial_allocator_->UpdateTrackingHistograms(); std::string switch_value = SerializeSharedMemoryRegionMetadata( global_->readonly_allocator_region_, launch_options); @@ -782,7 +782,7 @@ int FieldTrialList::GetFieldTrialDescriptor() { if (!global_ || !global_->readonly_allocator_region_.IsValid()) return -1; -#if BUILDFLAG(IS_ANDROID) +#if BUILDFLAG(IS_ANDROID) || defined(STARBOARD) return global_->readonly_allocator_region_.GetPlatformHandle(); #else return global_->readonly_allocator_region_.GetPlatformHandle().fd; @@ -1047,7 +1047,9 @@ void FieldTrialList::RestoreInstanceForTesting(FieldTrialList* instance) { global_ = instance; } -#if !BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_IOS) +#ifdef COBALT_PENDING_CLEAN_UP +// TODO(b/298237462): Try to enable the below code. +#elif !BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_IOS) // static std::string FieldTrialList::SerializeSharedMemoryRegionMetadata( @@ -1217,6 +1219,9 @@ bool FieldTrialList::CreateTrialsFromSharedMemoryRegion( // static bool FieldTrialList::CreateTrialsFromSharedMemoryMapping( ReadOnlySharedMemoryMapping shm_mapping) { +#if defined(STARBOARD) + return false; +#else global_->field_trial_allocator_ = std::make_unique( std::move(shm_mapping), 0, kAllocatorName); @@ -1241,10 +1246,14 @@ bool FieldTrialList::CreateTrialsFromSharedMemoryMapping( } } return true; +#endif // defined(STARBOARD) } // static void FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded() { +#if defined(STARBOARD) + return; +#else if (!global_) return; @@ -1277,6 +1286,7 @@ void FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded() { #if !BUILDFLAG(IS_NACL) global_->readonly_allocator_region_ = std::move(shm.region); #endif +#endif // !defined(STARBOARD) } // static diff --git a/base/metrics/field_trial.h b/base/metrics/field_trial.h index bc805254b3b8..2209ac8d1cf5 100644 --- a/base/metrics/field_trial.h +++ b/base/metrics/field_trial.h @@ -230,6 +230,9 @@ class BASE_EXPORT FieldTrial : public RefCounted { // If the group's name is empty, a string version containing the group number // is used as the group name. This causes a winner to be chosen if none was. const std::string& group_name(); +#ifdef COBALT_PENDING_CLEAN_UP + int group () { return 0; } +#endif // Finalizes the group choice and returns the chosen group, but does not mark // the trial as active - so its state will not be reported until group_name() diff --git a/base/metrics/field_trial_unittest.cc b/base/metrics/field_trial_unittest.cc index a2a8ef31b9d1..49c2d8bece24 100644 --- a/base/metrics/field_trial_unittest.cc +++ b/base/metrics/field_trial_unittest.cc @@ -923,6 +923,9 @@ class FieldTrialListTest : public ::testing::Test { test::ScopedFeatureList scoped_feature_list_; }; +// TODO(b/298237462): Try to enable |FieldTrialList|. +// TODO(b/316198056): Determine if tests should be enabled. +#if !defined(COBALT_PENDING_CLEAN_UP) #if !BUILDFLAG(IS_IOS) // LaunchOptions is not available on iOS. TEST_F(FieldTrialListTest, TestCopyFieldTrialStateToFlags) { @@ -1291,6 +1294,7 @@ TEST_F(FieldTrialListTest, TestGetRandomizedFieldTrialCount) { // Note: FieldTrialList should delete the objects at shutdown. } +#endif // !defined(COBALT_PENDING_CLEAN_UP) TEST_F(FieldTrialTest, TestAllParamsToString) { std::string exptected_output = "t1.g1:p1/v1/p2/v2"; diff --git a/base/metrics/histogram_macros.h b/base/metrics/histogram_macros.h index d777ae068484..0a3436e71120 100644 --- a/base/metrics/histogram_macros.h +++ b/base/metrics/histogram_macros.h @@ -76,11 +76,12 @@ // example). For scoped enums, this is awkward since it requires casting the // enum to an arithmetic type and adding one. Instead, prefer the two argument // version of the macro which automatically deduces the boundary from kMaxValue. +#define CR_EXPAND_ARG(arg) arg #define UMA_HISTOGRAM_ENUMERATION(name, ...) \ - INTERNAL_UMA_HISTOGRAM_ENUMERATION_GET_MACRO( \ + CR_EXPAND_ARG(INTERNAL_UMA_HISTOGRAM_ENUMERATION_GET_MACRO( \ __VA_ARGS__, INTERNAL_UMA_HISTOGRAM_ENUMERATION_SPECIFY_BOUNDARY, \ INTERNAL_UMA_HISTOGRAM_ENUMERATION_DEDUCE_BOUNDARY) \ - (name, __VA_ARGS__, base::HistogramBase::kUmaTargetedHistogramFlag) + (name, __VA_ARGS__, base::HistogramBase::kUmaTargetedHistogramFlag)) // As above but "scaled" count to avoid overflows caused by increments of // large amounts. See UMA_HISTOGRAM_SCALED_EXACT_LINEAR for more information. @@ -363,11 +364,12 @@ enum class ScopedHistogramTiming { name, sample, min, max, bucket_count, \ base::HistogramBase::kUmaStabilityHistogramFlag) +#define CR_EXPAND_ARG(arg) arg #define UMA_STABILITY_HISTOGRAM_ENUMERATION(name, ...) \ - INTERNAL_UMA_HISTOGRAM_ENUMERATION_GET_MACRO( \ + CR_EXPAND_ARG(INTERNAL_UMA_HISTOGRAM_ENUMERATION_GET_MACRO( \ __VA_ARGS__, INTERNAL_UMA_HISTOGRAM_ENUMERATION_SPECIFY_BOUNDARY, \ INTERNAL_UMA_HISTOGRAM_ENUMERATION_DEDUCE_BOUNDARY) \ - (name, __VA_ARGS__, base::HistogramBase::kUmaStabilityHistogramFlag) + (name, __VA_ARGS__, base::HistogramBase::kUmaStabilityHistogramFlag)) #define UMA_STABILITY_HISTOGRAM_LONG_TIMES(name, sample) \ STATIC_HISTOGRAM_POINTER_BLOCK( \ diff --git a/base/metrics/histogram_macros_local.h b/base/metrics/histogram_macros_local.h index c1235fb0f8c7..2a2f29f9c290 100644 --- a/base/metrics/histogram_macros_local.h +++ b/base/metrics/histogram_macros_local.h @@ -17,11 +17,12 @@ // // For usage details, see the equivalents in histogram_macros.h. +#define CR_GET_ARG(arg) arg #define LOCAL_HISTOGRAM_ENUMERATION(name, ...) \ - INTERNAL_UMA_HISTOGRAM_ENUMERATION_GET_MACRO( \ + CR_GET_ARG(INTERNAL_UMA_HISTOGRAM_ENUMERATION_GET_MACRO( \ __VA_ARGS__, INTERNAL_UMA_HISTOGRAM_ENUMERATION_SPECIFY_BOUNDARY, \ INTERNAL_UMA_HISTOGRAM_ENUMERATION_DEDUCE_BOUNDARY) \ - (name, __VA_ARGS__, base::HistogramBase::kNoFlags) + (name, __VA_ARGS__, base::HistogramBase::kNoFlags)) #define LOCAL_HISTOGRAM_BOOLEAN(name, sample) \ STATIC_HISTOGRAM_POINTER_BLOCK(name, AddBoolean(sample), \ diff --git a/base/metrics/histogram_samples.cc b/base/metrics/histogram_samples.cc index 58069b3c63bd..eb9efc057d16 100644 --- a/base/metrics/histogram_samples.cc +++ b/base/metrics/histogram_samples.cc @@ -5,6 +5,7 @@ #include "base/metrics/histogram_samples.h" #include +#include #include "base/compiler_specific.h" #include "base/memory/raw_ptr.h" diff --git a/base/metrics/persistent_histogram_allocator.cc b/base/metrics/persistent_histogram_allocator.cc index c0e4bc478368..c8bbf45e82b7 100644 --- a/base/metrics/persistent_histogram_allocator.cc +++ b/base/metrics/persistent_histogram_allocator.cc @@ -686,7 +686,7 @@ void GlobalHistogramAllocator::CreateWithLocalMemory( std::make_unique(size, id, name)))); } -#if !BUILDFLAG(IS_NACL) +#if !BUILDFLAG(IS_NACL) && !defined(STARBOARD) // static bool GlobalHistogramAllocator::CreateWithFile(const FilePath& file_path, size_t size, @@ -848,8 +848,9 @@ bool GlobalHistogramAllocator::CreateSpareFile(const FilePath& spare_path, return success; } -#endif // !BUILDFLAG(IS_NACL) +#endif // !BUILDFLAG(IS_NACL) && !defined(STARBOARD) +#if !defined(STARBOARD) // static void GlobalHistogramAllocator::CreateWithSharedMemoryRegion( const WritableSharedMemoryRegion& region) { @@ -864,6 +865,7 @@ void GlobalHistogramAllocator::CreateWithSharedMemoryRegion( std::make_unique( std::move(mapping), 0, StringPiece())))); } +#endif // !defined(STARBOARD) // static void GlobalHistogramAllocator::Set( diff --git a/base/metrics/persistent_histogram_allocator.h b/base/metrics/persistent_histogram_allocator.h index 5c30fb5c34ec..eed72ef14129 100644 --- a/base/metrics/persistent_histogram_allocator.h +++ b/base/metrics/persistent_histogram_allocator.h @@ -372,7 +372,7 @@ class BASE_EXPORT GlobalHistogramAllocator // specified |size| taken from the heap. static void CreateWithLocalMemory(size_t size, uint64_t id, StringPiece name); -#if !BUILDFLAG(IS_NACL) +#if !BUILDFLAG(IS_NACL) && !defined(STARBOARD) // Create a global allocator by memory-mapping a |file|. If the file does // not exist, it will be created with the specified |size|. If the file does // exist, the allocator will use and add to its contents, ignoring the passed @@ -434,12 +434,14 @@ class BASE_EXPORT GlobalHistogramAllocator static bool CreateSpareFile(const FilePath& spare_path, size_t size); #endif +#if !defined(STARBOARD) // Create a global allocator using a block of shared memory accessed // through the given |region|. The allocator maps the shared memory into // current process's virtual address space and frees it upon destruction. // The memory will continue to live if other processes have access to it. static void CreateWithSharedMemoryRegion( const WritableSharedMemoryRegion& region); +#endif // !defined(STARBOARD) // Sets a GlobalHistogramAllocator for globally storing histograms in // a space that can be persisted or shared between processes. There is only diff --git a/base/metrics/persistent_histogram_allocator_unittest.cc b/base/metrics/persistent_histogram_allocator_unittest.cc index c57954d8ee91..25ed1da70914 100644 --- a/base/metrics/persistent_histogram_allocator_unittest.cc +++ b/base/metrics/persistent_histogram_allocator_unittest.cc @@ -134,6 +134,7 @@ TEST_F(PersistentHistogramAllocatorTest, CreateAndIterate) { EXPECT_FALSE(recovered); } +#if !defined(STARBOARD) TEST_F(PersistentHistogramAllocatorTest, ConstructPaths) { const FilePath dir_path(FILE_PATH_LITERAL("foo/")); const std::string dir_string = @@ -212,6 +213,7 @@ TEST_F(PersistentHistogramAllocatorTest, CreateSpareFile) { EXPECT_EQ(0, buffer[i]); } } +#endif // !defined(STARBOARD) TEST_F(PersistentHistogramAllocatorTest, StatisticsRecorderMerge) { const char LinearHistogramName[] = "SRTLinearHistogram"; @@ -456,6 +458,7 @@ TEST_F(PersistentHistogramAllocatorTest, RangesDeDuplication) { EXPECT_EQ(ranges_ref, data2[kRangesRefIndex]); } +#if !defined(STARBOARD) TEST_F(PersistentHistogramAllocatorTest, MovePersistentFile) { const char temp_name[] = "MovePersistentFileTest.pma"; ScopedTempDir temp_dir; @@ -514,5 +517,6 @@ TEST_F(PersistentHistogramAllocatorTest, MovePersistentFile) { } EXPECT_TRUE(found_histogram); } +#endif // !defined(STARBOARD) } // namespace base diff --git a/base/metrics/persistent_histogram_storage.cc b/base/metrics/persistent_histogram_storage.cc index b6a61154d7af..0dbab31498ba 100644 --- a/base/metrics/persistent_histogram_storage.cc +++ b/base/metrics/persistent_histogram_storage.cc @@ -30,7 +30,8 @@ constexpr size_t kAllocSize = 1 << 20; // 1 MiB void* AllocateLocalMemory(size_t size) { void* address; -#if BUILDFLAG(IS_WIN) +#if defined(COBALT_PENDING_CLEAN_UP) +#elif BUILDFLAG(IS_WIN) address = ::VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (address) diff --git a/base/metrics/persistent_memory_allocator.cc b/base/metrics/persistent_memory_allocator.cc index ec0e8528733c..c4976fd425e4 100644 --- a/base/metrics/persistent_memory_allocator.cc +++ b/base/metrics/persistent_memory_allocator.cc @@ -29,12 +29,15 @@ #include // Must be after #include -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) #include #if BUILDFLAG(IS_ANDROID) #include #endif #endif +#if defined(STARBOARD) +#include "starboard/memory.h" +#endif namespace { @@ -348,11 +351,17 @@ PersistentMemoryAllocator::PersistentMemoryAllocator(Memory memory, // Ensure that memory segment is of acceptable size. CHECK(IsMemoryAcceptable(memory.base, size, page_size, readonly)); + // The |is_lock_free| function has been found to require additional library + // linkage that we'd like to avoid on Starboard platforms. Additionally we + // don't support multi-process applications on Starboard currently, so this + // code will not be used. +#if !defined(STARBOARD) // These atomics operate inter-process and so must be lock-free. DCHECK(SharedMetadata().freeptr.is_lock_free()); DCHECK(SharedMetadata().flags.is_lock_free()); DCHECK(BlockHeader().next.is_lock_free()); CHECK(corrupt_.is_lock_free()); +#endif // !defined(STARBOARD) if (shared_meta()->cookie != kGlobalCookie) { if (readonly) { @@ -981,7 +990,8 @@ LocalPersistentMemoryAllocator::AllocateLocalMemory(size_t size, base::StringPiece name) { void* address; -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +#elif BUILDFLAG(IS_WIN) address = ::VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (address) @@ -1025,7 +1035,8 @@ void LocalPersistentMemoryAllocator::DeallocateLocalMemory(void* memory, } DCHECK_EQ(MEM_VIRTUAL, type); -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +#elif BUILDFLAG(IS_WIN) BOOL success = ::VirtualFree(memory, 0, MEM_DECOMMIT); DCHECK(success); #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) @@ -1037,6 +1048,7 @@ void LocalPersistentMemoryAllocator::DeallocateLocalMemory(void* memory, } //----- WritableSharedPersistentMemoryAllocator -------------------------------- +#if !defined(STARBOARD) WritableSharedPersistentMemoryAllocator:: WritableSharedPersistentMemoryAllocator( @@ -1059,9 +1071,11 @@ bool WritableSharedPersistentMemoryAllocator::IsSharedMemoryAcceptable( const base::WritableSharedMemoryMapping& memory) { return IsMemoryAcceptable(memory.memory(), memory.size(), 0, false); } +#endif // !defined(STARBOARD) //----- ReadOnlySharedPersistentMemoryAllocator -------------------------------- +#if !defined(STARBOARD) ReadOnlySharedPersistentMemoryAllocator:: ReadOnlySharedPersistentMemoryAllocator( base::ReadOnlySharedMemoryMapping memory, @@ -1084,6 +1098,7 @@ bool ReadOnlySharedPersistentMemoryAllocator::IsSharedMemoryAcceptable( const base::ReadOnlySharedMemoryMapping& memory) { return IsMemoryAcceptable(memory.memory(), memory.size(), 0, true); } +#endif // !defined(STARBOARD) #if !BUILDFLAG(IS_NACL) //----- FilePersistentMemoryAllocator ------------------------------------------ @@ -1158,12 +1173,14 @@ void FilePersistentMemoryAllocator::FlushPartial(size_t length, bool sync) { int result = ::msync(const_cast(data()), length, sync ? MS_SYNC : MS_ASYNC); DCHECK_NE(EINVAL, result); -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) && !defined(COMPILER_MSVC) // On POSIX, "invalidate" forces _other_ processes to recognize what has // been written to disk and so is applicable to "flush". int result = ::msync(const_cast(data()), length, MS_INVALIDATE | (sync ? MS_SYNC : MS_ASYNC)); DCHECK_NE(EINVAL, result); +#elif defined(STARBOARD) && defined(COMPILER_MSVC) + msync(const_cast(data()), length, 0); #else #error Unsupported OS. #endif diff --git a/base/metrics/persistent_memory_allocator.h b/base/metrics/persistent_memory_allocator.h index 806df91c3af0..f3e94cf78789 100644 --- a/base/metrics/persistent_memory_allocator.h +++ b/base/metrics/persistent_memory_allocator.h @@ -744,6 +744,7 @@ class BASE_EXPORT LocalPersistentMemoryAllocator }; +#if !defined(STARBOARD) // This allocator takes a writable shared memory mapping object and performs // allocation from it. The allocator takes ownership of the mapping object. class BASE_EXPORT WritableSharedPersistentMemoryAllocator @@ -799,6 +800,7 @@ class BASE_EXPORT ReadOnlySharedPersistentMemoryAllocator private: base::ReadOnlySharedMemoryMapping shared_memory_; }; +#endif // !defined(STARBOARD) // NACL doesn't support any kind of file access in build. #if !BUILDFLAG(IS_NACL) diff --git a/base/metrics/persistent_memory_allocator_unittest.cc b/base/metrics/persistent_memory_allocator_unittest.cc index 82aaee56b2aa..4d66a1c200af 100644 --- a/base/metrics/persistent_memory_allocator_unittest.cc +++ b/base/metrics/persistent_memory_allocator_unittest.cc @@ -617,6 +617,7 @@ TEST(LocalPersistentMemoryAllocatorTest, CreationTest) { } //----- {Writable,ReadOnly}SharedPersistentMemoryAllocator --------------------- +#if !defined(STARBOARD) TEST(SharedPersistentMemoryAllocatorTest, CreationTest) { base::WritableSharedMemoryRegion rw_region = @@ -722,8 +723,10 @@ TEST(SharedPersistentMemoryAllocatorTest, CreationTest) { EXPECT_EQ(0, data[2]); EXPECT_EQ(0, data[3]); } +#endif // !defined(STARBOARD) -#if !BUILDFLAG(IS_NACL) +// TODO: b/316198056 - Re-enable this test once base/net have been updated. +#if !BUILDFLAG(IS_NACL) && !defined(STARBOARD) //----- FilePersistentMemoryAllocator ------------------------------------------ TEST(FilePersistentMemoryAllocatorTest, CreationTest) { diff --git a/base/native_library.cc b/base/native_library.cc index b0d1125b701b..2865d316cd13 100644 --- a/base/native_library.cc +++ b/base/native_library.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#if !defined(STARBOARD) #include "base/native_library.h" namespace base { @@ -13,3 +14,4 @@ NativeLibrary LoadNativeLibrary(const FilePath& library_path, } } // namespace base +#endif // !defined(STARBOARD) diff --git a/base/native_library.h b/base/native_library.h index 4c9f7882fbb4..7e9f42ca7b76 100644 --- a/base/native_library.h +++ b/base/native_library.h @@ -15,6 +15,8 @@ #include "base/strings/string_piece.h" #include "build/build_config.h" +#if !defined(STARBOARD) + #if BUILDFLAG(IS_WIN) #include #elif BUILDFLAG(IS_APPLE) @@ -129,4 +131,5 @@ BASE_EXPORT std::string GetLoadableModuleName(StringPiece name); } // namespace base +#endif // !defined(STARBOARD) #endif // BASE_NATIVE_LIBRARY_H_ diff --git a/base/notreached.h b/base/notreached.h index 96d4f9be0dae..c7f8c42c0fff 100644 --- a/base/notreached.h +++ b/base/notreached.h @@ -51,6 +51,10 @@ namespace logging { (true) ? ::logging::NotReachedFailure() : EAT_CHECK_STREAM_PARAMS() #endif +#if defined(COMPILER_MSVC) && defined(USE_COBALT_CUSTOMIZATIONS) +#define __PRETTY_FUNCTION__ __FUNCSIG__ +#endif + // The NOTIMPLEMENTED() macro annotates codepaths which have not been // implemented yet. If output spam is a serious concern, // NOTIMPLEMENTED_LOG_ONCE can be used. diff --git a/base/observer_list.h b/base/observer_list.h index ce5e05814e35..05835df1bf3e 100644 --- a/base/observer_list.h +++ b/base/observer_list.h @@ -387,4 +387,16 @@ using ReentrantObserverList = ObserverList; } // namespace base +#if defined(STARBOARD) +#define FOR_EACH_OBSERVER(ObserverType, observer_list, func) \ + do { \ + if (!(observer_list).empty()) { \ + for (base::ObserverList::Iter it(&observer_list); \ + it != base::ObserverList::Iter(); it++) { \ + it->func; \ + } \ + } \ + } while (0) +#endif + #endif // BASE_OBSERVER_LIST_H_ diff --git a/base/observer_list_threadsafe.cc b/base/observer_list_threadsafe.cc index 63f2c6c771b2..9b1a4bf1986a 100644 --- a/base/observer_list_threadsafe.cc +++ b/base/observer_list_threadsafe.cc @@ -6,15 +6,50 @@ #include "base/compiler_specific.h" #include "third_party/abseil-cpp/absl/base/attributes.h" +#if defined(STARBOARD) +#include +#endif + namespace base { namespace internal { +#if defined(STARBOARD) + +namespace { + +ABSL_CONST_INIT pthread_once_t s_once_flag = PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_local_key = 0; + +void InitThreadLocalKey() { + int res = pthread_key_create(&s_thread_local_key , NULL); + DCHECK(res == 0); +} + +} + +void ObserverListThreadSafeBase::EnsureThreadLocalKeyInited() { + pthread_once(&s_once_flag, InitThreadLocalKey); +} + +const pthread_key_t ObserverListThreadSafeBase::GetThreadLocalKey() { + EnsureThreadLocalKeyInited(); + return s_thread_local_key; +} + +#else ABSL_CONST_INIT thread_local const ObserverListThreadSafeBase:: NotificationDataBase* current_notification = nullptr; +#endif // static const ObserverListThreadSafeBase::NotificationDataBase*& ObserverListThreadSafeBase::GetCurrentNotification() { +#if defined(STARBOARD) + // We can't use a reference to a pointer in Starboard here. + NOTIMPLEMENTED(); + static const ObserverListThreadSafeBase::NotificationDataBase* rv = nullptr; + return rv; +#else // Workaround false-positive MSAN use-of-uninitialized-value on // thread_local storage for loaded libraries: // https://github.com/google/sanitizers/issues/1265 @@ -23,6 +58,7 @@ ObserverListThreadSafeBase::GetCurrentNotification() { sizeof(const ObserverListThreadSafeBase::NotificationDataBase*)); return current_notification; +#endif } } // namespace internal diff --git a/base/observer_list_threadsafe.h b/base/observer_list_threadsafe.h index eda8a177a78c..a6ac666faa60 100644 --- a/base/observer_list_threadsafe.h +++ b/base/observer_list_threadsafe.h @@ -25,6 +25,10 @@ #include "build/build_config.h" #include "third_party/abseil-cpp/absl/base/attributes.h" +#if defined(STARBOARD) +#include +#endif + /////////////////////////////////////////////////////////////////////////////// // // OVERVIEW: @@ -88,6 +92,11 @@ class BASE_EXPORT ObserverListThreadSafeBase static const NotificationDataBase*& GetCurrentNotification(); +#if defined(STARBOARD) + static void EnsureThreadLocalKeyInited(); + static const pthread_key_t GetThreadLocalKey(); +#endif + virtual ~ObserverListThreadSafeBase() = default; private: @@ -146,8 +155,15 @@ class ObserverListThreadSafe : public internal::ObserverListThreadSafeBase { // may not make it to |observer| depending on the outcome of the race to // |lock_|). if (policy_ == ObserverListPolicy::ALL) { +#if defined(STARBOARD) + void* current_notification_void = pthread_getspecific(GetThreadLocalKey()); + if (current_notification_void) { + if (const NotificationDataBase* const current_notification = + static_cast(current_notification_void); +#else if (const NotificationDataBase* const current_notification = GetCurrentNotification(); +#endif current_notification && current_notification->observer_list == this) { const NotificationData* notification_data = static_cast(current_notification); @@ -164,6 +180,9 @@ class ObserverListThreadSafe : public internal::ObserverListThreadSafeBase { current_notification->from_here, notification_data->method))); } +#if defined(STARBOARD) + } +#endif } return was_empty ? AddObserverResult::kBecameNonEmpty @@ -255,11 +274,21 @@ class ObserverListThreadSafe : public internal::ObserverListThreadSafeBase { // Note: GetCurrentNotification() may not return null if this runs in a // nested loop started by a notification callback. In that case, it is // important to save the previous value to restore it later. +#if defined(STARBOARD) + EnsureThreadLocalKeyInited(); + void* scoped_reset_value = pthread_getspecific(GetThreadLocalKey()); + pthread_setspecific(GetThreadLocalKey(), const_cast(¬ification)); +#else const AutoReset resetter_( &GetCurrentNotification(), ¬ification); +#endif // Invoke the callback. notification.method.Run(observer); + +#if defined(STARBOARD) + pthread_setspecific(GetThreadLocalKey(), scoped_reset_value); +#endif } const ObserverListPolicy policy_ = ObserverListPolicy::ALL; diff --git a/base/optional.h b/base/optional.h new file mode 100644 index 000000000000..c53a904a0fd2 --- /dev/null +++ b/base/optional.h @@ -0,0 +1,17 @@ +#ifndef BASE_OPTIONAL_H_ +#define BASE_OPTIONAL_H_ + +#ifndef COBALT_PENDING_CLEAN_UP +#error "Remove stubs" +#endif + +#include "third_party/abseil-cpp/absl/types/optional.h" + +namespace base { +template +using Optional = absl::optional; + +constexpr auto nullopt = absl::nullopt; +} + +#endif // BASE_OPTIONAL_H_ diff --git a/base/parameter_pack.h b/base/parameter_pack.h index 3eef16e6117b..2b9170930f2a 100644 --- a/base/parameter_pack.h +++ b/base/parameter_pack.h @@ -38,6 +38,72 @@ inline constexpr size_t count(std::initializer_list ilist, T value) { constexpr size_t pack_npos = static_cast(-1); +#if defined(COBALT_PENDING_CLEAN_UP) +template +struct ParameterPack; + +template +struct ParameterPack { + // Checks if |Type| occurs in the parameter pack. + template + using HasType = + std::bool_constant::value || any_of({std::is_same::value...})>; + + + // Checks if the parameter pack only contains |Type|. + template + using OnlyHasType = + std::bool_constant::value && all_of({std::is_same::value...})>; + + + // Checks if |Type| occurs only once in the parameter pack. + template + using IsUniqueInPack = + std::bool_constant::value + count({std::is_same::value...}, true) == 1>; + + // Returns the zero-based index of |Type| within |Pack...| or |pack_npos| if + // it's not within the pack. + template + static constexpr size_t IndexInPack() { + size_t index = 0; + if (std::is_same::value) { + return index; + } + index++; + for (bool value : {std::is_same::value...}) { + if (value) + return index; + index++; + } + return pack_npos; + } + + // Helper for extracting the Nth type from a parameter pack. + template + using NthType = std::tuple_element_t>; + + // Checks if every type in the parameter pack is the same. + using IsAllSameType = + std::bool_constant::value...})>; +}; + +template <> +struct ParameterPack<> { + template + using HasType = std::bool_constant; + + template + using OnlyHasType = std::bool_constant; + + template + using IsUniqueInPack = std::bool_constant; + + template + static constexpr size_t IndexInPack() { return pack_npos; } + + static constexpr bool IsAllSameType = true; +}; +#else template struct ParameterPack { // Checks if |Type| occurs in the parameter pack. @@ -76,6 +142,7 @@ struct ParameterPack { using IsAllSameType = std::bool_constant, Ts>::value...})>; }; +#endif } // namespace base diff --git a/base/path_service.cc b/base/path_service.cc index ea626f026303..050f8caf0125 100644 --- a/base/path_service.cc +++ b/base/path_service.cc @@ -26,7 +26,9 @@ namespace base { bool PathProvider(int key, FilePath* result); -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +bool PathProviderStarboard(int key, FilePath* result); +#elif BUILDFLAG(IS_WIN) bool PathProviderWin(int key, FilePath* result); #elif BUILDFLAG(IS_APPLE) bool PathProviderMac(int key, FilePath* result); @@ -64,6 +66,18 @@ Provider base_provider = {PathProvider, nullptr, #endif true}; +#if defined(STARBOARD) +Provider base_provider_starboard = { + base::PathProviderStarboard, + &base_provider, +#ifndef NDEBUG + base::PATH_STARBOARD_START, + base::PATH_STARBOARD_END, +#endif + true +}; +#else + #if BUILDFLAG(IS_WIN) Provider base_provider_win = { PathProviderWin, @@ -119,6 +133,7 @@ Provider base_provider_posix = { true }; #endif +#endif struct PathData { @@ -129,7 +144,9 @@ struct PathData { bool cache_disabled; // Don't use cache if true; PathData() : cache_disabled(false) { -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) + providers = &base_provider_starboard; +#elif BUILDFLAG(IS_WIN) providers = &base_provider_win; #elif BUILDFLAG(IS_APPLE) providers = &base_provider_mac; @@ -190,7 +207,14 @@ bool PathService::Get(int key, FilePath* result) { // Special case the current directory because it can never be cached. if (key == DIR_CURRENT) +#if defined(STARBOARD) + { + NOTREACHED() << "DIR_CURRENT not supported in Starboard."; + return false; + } +#else return GetCurrentDirectory(result); +#endif Provider* provider = nullptr; { @@ -269,11 +293,13 @@ bool PathService::OverrideAndCreateIfNeeded(int key, } // We need to have an absolute path. +#if !defined(STARBOARD) if (!is_absolute) { file_path = MakeAbsoluteFilePath(file_path); if (file_path.empty()) return false; } +#endif DCHECK(file_path.IsAbsolute()); AutoLock scoped_lock(path_data->lock); diff --git a/base/path_service_unittest.cc b/base/path_service_unittest.cc index 4cd5ecb9a07a..cdedb2ce9b12 100644 --- a/base/path_service_unittest.cc +++ b/base/path_service_unittest.cc @@ -110,7 +110,10 @@ typedef PlatformTest PathServiceTest; // failure for the value(s) on that platform in this test. TEST_F(PathServiceTest, Get) { // Contains keys that are defined but not supported on the platform. -#if BUILDFLAG(IS_ANDROID) +#if defined(STARBOARD) + constexpr std::array kUnsupportedKeys = {DIR_CURRENT, DIR_USER_DESKTOP, + DIR_SOURCE_ROOT}; +#elif BUILDFLAG(IS_ANDROID) // The following keys are not intended to be implemented on Android (see // crbug.com/1257402). Current implementation is described before each key. // TODO(crbug.com/1257402): Remove the definition of these keys on Android @@ -136,10 +139,22 @@ TEST_F(PathServiceTest, Get) { constexpr std::array kUnsupportedKeys = {}; #endif // BUILDFLAG(IS_ANDROID) for (int key = PATH_START + 1; key < PATH_END; ++key) { +#if defined(STARBOARD) + if (key == DIR_CURRENT || key == DIR_USER_DESKTOP || + key == DIR_SOURCE_ROOT) { + continue; + } +#endif EXPECT_PRED1(Contains(kUnsupportedKeys, key) ? &ReturnsInvalidPath : &ReturnsValidPath, key); } +#if defined(STARBOARD) + // In the three Starboard custom directories, DIR_CACHE should always be + // valid while DIR_SYSTEM_FONTS and DIR_SYSTEM_FONTS_CONFIGURATION + // can be invalid on some platforms. + EXPECT_PRED1(ReturnsValidPath, DIR_CACHE); +#else // STARBOARD #if BUILDFLAG(IS_WIN) for (int key = PATH_WIN_START + 1; key < PATH_WIN_END; ++key) { EXPECT_PRED1(ReturnsValidPath, key); @@ -159,11 +174,16 @@ TEST_F(PathServiceTest, Get) { EXPECT_PRED1(ReturnsValidPath, key); } #endif // BUILDFLAG(IS_WIN) +#endif // defined(STARBOARD) } // Tests that CheckedGet returns the same path as Get. TEST_F(PathServiceTest, CheckedGet) { +#if defined(STARBOARD) + constexpr int kKey = DIR_CACHE; +#else constexpr int kKey = DIR_CURRENT; +#endif // defined(STARBOARD) FilePath path; ASSERT_TRUE(PathService::Get(kKey, &path)); EXPECT_EQ(path, PathService::CheckedGet(kKey)); @@ -211,7 +231,7 @@ TEST_F(PathServiceTest, Override) { MakeAbsoluteFilePath(temp_dir.GetPath()).AppendASCII("non_existent")); EXPECT_TRUE(non_existent.IsAbsolute()); EXPECT_FALSE(PathExists(non_existent)); -#if !BUILDFLAG(IS_ANDROID) +#if !BUILDFLAG(IS_ANDROID) && !defined(STARBOARD) // This fails because MakeAbsoluteFilePath fails for non-existent files. // Earlier versions of Bionic libc don't fail for non-existent files, so // skip this check on Android. @@ -219,7 +239,7 @@ TEST_F(PathServiceTest, Override) { non_existent, false, false)); -#endif // !BUILDFLAG(IS_ANDROID) +#endif // !BUILDFLAG(IS_ANDROID) && !defined(STARBOARD) // This works because indicating that |non_existent| is absolute skips the // internal MakeAbsoluteFilePath call. EXPECT_TRUE(PathService::OverrideAndCreateIfNeeded(my_special_key, diff --git a/base/pickle.cc b/base/pickle.cc index 0776e11761bf..7b0f01520efc 100644 --- a/base/pickle.cc +++ b/base/pickle.cc @@ -8,6 +8,7 @@ #include #include #include +#include #include "base/bits.h" #include "base/numerics/safe_conversions.h" diff --git a/base/pickle_unittest.cc b/base/pickle_unittest.cc index 41a642d49394..f12f3ddc6f75 100644 --- a/base/pickle_unittest.cc +++ b/base/pickle_unittest.cc @@ -190,7 +190,11 @@ TEST(PickleTest, CopyWithInvalidHeader) { // 1. Actual header size (calculated based on the input buffer) > passed in // buffer size. Which results in Pickle's internal |header_| = null. { +#if defined(STARBOARD) + Pickle::Header header = {100}; +#else Pickle::Header header = {.payload_size = 100}; +#endif const char* data = reinterpret_cast(&header); const Pickle pickle(data, sizeof(header)); diff --git a/base/power_monitor/battery_level_provider_unittest.cc b/base/power_monitor/battery_level_provider_unittest.cc index 9906631ab369..bda257e1a82b 100644 --- a/base/power_monitor/battery_level_provider_unittest.cc +++ b/base/power_monitor/battery_level_provider_unittest.cc @@ -36,9 +36,15 @@ TEST(BatteryLevelProviderTest, NoBattery) { TEST(BatteryLevelProviderTest, SingleBatteryWithExternalPower) { auto state = FakeBatteryLevelProvider::MakeBatteryState( +#if defined(STARBOARD) + {BatteryDetails({true, + 42, + 100})}); +#else {BatteryDetails({.is_external_power_connected = true, .current_capacity = 42, .full_charged_capacity = 100})}); +#endif EXPECT_EQ(1, state.battery_count); EXPECT_TRUE(state.is_external_power_connected); EXPECT_EQ(42U, state.current_capacity); @@ -48,9 +54,15 @@ TEST(BatteryLevelProviderTest, SingleBatteryWithExternalPower) { TEST(BatteryLevelProviderTest, SingleBatteryDischarging) { auto state = FakeBatteryLevelProvider::MakeBatteryState( +#if defined(STARBOARD) + {BatteryDetails({false, + 42, + 100})}); +#else {BatteryDetails({.is_external_power_connected = false, .current_capacity = 42, .full_charged_capacity = 100})}); +#endif EXPECT_EQ(1, state.battery_count); EXPECT_FALSE(state.is_external_power_connected); EXPECT_EQ(42U, state.current_capacity); @@ -60,12 +72,21 @@ TEST(BatteryLevelProviderTest, SingleBatteryDischarging) { TEST(BatteryLevelProviderTest, MultipleBatteriesWithExternalPower) { auto state = FakeBatteryLevelProvider::MakeBatteryState( +#if defined(STARBOARD) + {BatteryDetails({false, + 42, + 100}), + BatteryDetails({true, + 10, + 100})}); +#else {BatteryDetails({.is_external_power_connected = false, .current_capacity = 42, .full_charged_capacity = 100}), BatteryDetails({.is_external_power_connected = true, .current_capacity = 10, .full_charged_capacity = 100})}); +#endif EXPECT_EQ(2, state.battery_count); EXPECT_TRUE(state.is_external_power_connected); EXPECT_EQ(absl::nullopt, state.current_capacity); @@ -75,12 +96,21 @@ TEST(BatteryLevelProviderTest, MultipleBatteriesWithExternalPower) { TEST(BatteryLevelProviderTest, MultipleBatteriesDischarging) { auto state = FakeBatteryLevelProvider::MakeBatteryState( +#if defined(STARBOARD) + {BatteryDetails({false, + 42, + 100}), + BatteryDetails({false, + 10, + 100})}); +#else {BatteryDetails({.is_external_power_connected = false, .current_capacity = 42, .full_charged_capacity = 100}), BatteryDetails({.is_external_power_connected = false, .current_capacity = 10, .full_charged_capacity = 100})}); +#endif EXPECT_EQ(2, state.battery_count); EXPECT_FALSE(state.is_external_power_connected); EXPECT_EQ(absl::nullopt, state.current_capacity); @@ -90,11 +120,19 @@ TEST(BatteryLevelProviderTest, MultipleBatteriesDischarging) { TEST(BatteryLevelProviderTest, SingleBatteryMAh) { auto state = FakeBatteryLevelProvider::MakeBatteryState({BatteryDetails( +#if defined(STARBOARD) + {false, + 42, + 100, + 12, + BatteryLevelProvider::BatteryLevelUnit::kMAh})}); +#else {.is_external_power_connected = false, .current_capacity = 42, .full_charged_capacity = 100, .voltage_mv = 12, .charge_unit = BatteryLevelProvider::BatteryLevelUnit::kMAh})}); +#endif EXPECT_EQ(1, state.battery_count); EXPECT_FALSE(state.is_external_power_connected); EXPECT_EQ(42U, state.current_capacity); diff --git a/base/process/environment_internal.cc b/base/process/environment_internal.cc index ad3ab4ab26f0..d9532dafc9be 100644 --- a/base/process/environment_internal.cc +++ b/base/process/environment_internal.cc @@ -4,6 +4,8 @@ #include "base/process/environment_internal.h" +#if !defined(STARBOARD) + #include #include "build/build_config.h" @@ -132,3 +134,5 @@ NativeEnvironmentString AlterEnvironment(const wchar_t* env, } // namespace internal } // namespace base + +#endif // !defined(STARBOARD) diff --git a/base/process/environment_internal.h b/base/process/environment_internal.h index 9cf164124574..a3513b632547 100644 --- a/base/process/environment_internal.h +++ b/base/process/environment_internal.h @@ -8,6 +8,8 @@ #ifndef BASE_PROCESS_ENVIRONMENT_INTERNAL_H_ #define BASE_PROCESS_ENVIRONMENT_INTERNAL_H_ +#if !defined(STARBOARD) + #include #include "base/base_export.h" @@ -50,4 +52,6 @@ AlterEnvironment(const wchar_t* env, const EnvironmentMap& changes); } // namespace internal } // namespace base +#endif // !defined(STARBOARD) + #endif // BASE_PROCESS_ENVIRONMENT_INTERNAL_H_ diff --git a/base/process/environment_internal_unittest.cc b/base/process/environment_internal_unittest.cc index 2bc5437a734a..28fbc085e315 100644 --- a/base/process/environment_internal_unittest.cc +++ b/base/process/environment_internal_unittest.cc @@ -11,6 +11,8 @@ #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" +#if !defined(STARBOARD) + using EnvironmentInternalTest = PlatformTest; namespace base { @@ -159,3 +161,5 @@ TEST_F(EnvironmentInternalTest, AlterEnvironment) { } // namespace internal } // namespace base + +#endif // !defined(STARBOARD) diff --git a/base/process/internal_linux.cc b/base/process/internal_linux.cc index ed2c4416224f..f0fe1e34f76d 100644 --- a/base/process/internal_linux.cc +++ b/base/process/internal_linux.cc @@ -33,6 +33,7 @@ const char kProcDir[] = "/proc"; const char kStatFile[] = "stat"; +#if !defined(STARBOARD) FilePath GetProcPidDir(pid_t pid) { return FilePath(kProcDir).Append(NumberToString(pid)); } @@ -56,6 +57,7 @@ pid_t ProcDirSlotToPid(const char* d_name) { } return pid; } +#endif // !defined(STARBOARD) bool ReadProcFile(const FilePath& file, std::string* buffer) { DCHECK(FilePath(kProcDir).IsParent(file)); @@ -70,10 +72,12 @@ bool ReadProcFile(const FilePath& file, std::string* buffer) { return !buffer->empty(); } +#if !defined(STARBOARD) bool ReadProcStats(pid_t pid, std::string* buffer) { FilePath stat_file = internal::GetProcPidDir(pid).Append(kStatFile); return ReadProcFile(stat_file, buffer); } +#endif // !defined(STARBOARD) bool ParseProcStats(const std::string& stats_data, std::vector* proc_stats) { @@ -99,7 +103,11 @@ bool ParseProcStats(const std::string& stats_data, proc_stats->clear(); // PID. +#if defined(STARBOARD) + proc_stats->push_back(stats_data.substr(0, open_parens_idx - 1)); +#else proc_stats->push_back(stats_data.substr(0, open_parens_idx)); +#endif // Process name without parentheses. proc_stats->push_back( stats_data.substr(open_parens_idx + 1, @@ -152,16 +160,19 @@ int64_t ReadStatFileAndGetFieldAsInt64(const FilePath& stat_file, return GetProcStatsFieldAsInt64(proc_stats, field_num); } +#if !defined(STARBOARD) int64_t ReadProcStatsAndGetFieldAsInt64(pid_t pid, ProcStatsFields field_num) { FilePath stat_file = internal::GetProcPidDir(pid).Append(kStatFile); return ReadStatFileAndGetFieldAsInt64(stat_file, field_num); } +#endif // !defined(STARBOARD) int64_t ReadProcSelfStatsAndGetFieldAsInt64(ProcStatsFields field_num) { FilePath stat_file = FilePath(kProcDir).Append("self").Append(kStatFile); return ReadStatFileAndGetFieldAsInt64(stat_file, field_num); } +#if !defined(STARBOARD) size_t ReadProcStatsAndGetFieldAsSizeT(pid_t pid, ProcStatsFields field_num) { std::string stats_data; @@ -172,6 +183,7 @@ size_t ReadProcStatsAndGetFieldAsSizeT(pid_t pid, return 0; return GetProcStatsFieldAsSizeT(proc_stats, field_num); } +#endif // !defined(STARBOARD) Time GetBootTime() { FilePath path("/proc/stat"); @@ -189,6 +201,7 @@ Time GetBootTime() { return Time::FromTimeT(btime); } +#if !defined(STARBOARD) TimeDelta GetUserCpuTimeSinceBoot() { FilePath path("/proc/stat"); std::string contents; @@ -228,6 +241,7 @@ TimeDelta ClockTicksToTimeDelta(int64_t clock_ticks) { return Microseconds(Time::kMicrosecondsPerSecond * clock_ticks / kHertz); } +#endif // !defined(STARBOARD) } // namespace internal } // namespace base diff --git a/base/process/internal_linux.h b/base/process/internal_linux.h index d7cc2f121fe7..f59643f55289 100644 --- a/base/process/internal_linux.h +++ b/base/process/internal_linux.h @@ -14,7 +14,9 @@ #include #include +#if !defined(STARBOARD) #include "base/files/dir_reader_posix.h" +#endif // !defined(STARBOARD) #include "base/files/file_path.h" #include "base/process/process_handle.h" #include "base/strings/string_number_conversions.h" @@ -33,14 +35,17 @@ extern const char kProcDir[]; // "stat" extern const char kStatFile[]; +#if !defined(STARBOARD) // Returns a FilePath to "/proc/pid". base::FilePath GetProcPidDir(pid_t pid); +#endif // !defined(STARBOARD) // Reads a file from /proc into a string. This is allowed on any thread as // reading from /proc does not hit the disk. Returns true if the file can be // read and is non-empty. bool ReadProcFile(const FilePath& file, std::string* buffer); +#if !defined(STARBOARD) // Take a /proc directory entry named |d_name|, and if it is the directory for // a process, convert it to a pid_t. // Returns 0 on failure. @@ -50,6 +55,7 @@ pid_t ProcDirSlotToPid(const char* d_name); // Reads /proc//stat into |buffer|. Returns true if the file can be read // and is non-empty. bool ReadProcStats(pid_t pid, std::string* buffer); +#endif // !defined(STARBOARD) // Takes |stats_data| and populates |proc_stats| with the values split by // spaces. Taking into account the 2nd field may, in itself, contain spaces. @@ -89,16 +95,21 @@ size_t GetProcStatsFieldAsSizeT(const std::vector& proc_stats, // ReadProcStats(). See GetProcStatsFieldAsInt64() for details. int64_t ReadStatsFilendGetFieldAsInt64(const FilePath& stat_file, ProcStatsFields field_num); +#if !defined(STARBOARD) int64_t ReadProcStatsAndGetFieldAsInt64(pid_t pid, ProcStatsFields field_num); +#endif // !defined(STARBOARD) int64_t ReadProcSelfStatsAndGetFieldAsInt64(ProcStatsFields field_num); +#if !defined(STARBOARD) // Same as ReadProcStatsAndGetFieldAsInt64() but for size_t values. size_t ReadProcStatsAndGetFieldAsSizeT(pid_t pid, ProcStatsFields field_num); +#endif // !defined(STARBOARD) // Returns the time that the OS started. Clock ticks are relative to this. Time GetBootTime(); +#if !defined(STARBOARD) // Returns the amount of time spent in user space since boot across all CPUs. TimeDelta GetUserCpuTimeSinceBoot(); @@ -130,6 +141,7 @@ void ForEachProcessTask(base::ProcessHandle process, Lambda&& lambda) { lambda(tid, task_path); } } +#endif // !defined(STARBOARD) } // namespace internal } // namespace base diff --git a/base/process/launch_starboard.cc b/base/process/launch_starboard.cc new file mode 100644 index 000000000000..b2212909ca0b --- /dev/null +++ b/base/process/launch_starboard.cc @@ -0,0 +1,31 @@ +// Copyright 2024 The Cobalt Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/process/launch.h" + +#include "base/notreached.h" + +namespace base { + +void RaiseProcessToHighPriority() { + // Impossible on iOS. Do nothing. +} + +bool GetAppOutput(const CommandLine& cl, std::string* output) { + NOTIMPLEMENTED(); + return false; +} + +bool GetAppOutputAndError(const CommandLine& cl, std::string* output) { + NOTIMPLEMENTED(); + return false; +} + +Process LaunchProcess(const CommandLine& cmdline, + const LaunchOptions& options) { + NOTIMPLEMENTED(); + return Process(); +} + +} // namespace base diff --git a/base/process/memory_starboard.cc b/base/process/memory_starboard.cc new file mode 100644 index 000000000000..728afa27cf5a --- /dev/null +++ b/base/process/memory_starboard.cc @@ -0,0 +1,40 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "base/process/memory.h" + +#include + +#include "starboard/memory.h" + +namespace base { + +void EnableTerminationOnOutOfMemory() { + // Nothing to be done here. +} + +void EnableTerminationOnHeapCorruption() { + // Nothing to be done here. +} + +bool UncheckedMalloc(size_t size, void** result) { + *result = malloc(size); + return *result != nullptr; +} + +void UncheckedFree(void* ptr) { + free(ptr); +} + +} // namespace base diff --git a/base/process/process_handle.h b/base/process/process_handle.h index aefe9a8d06d6..f240f5bb6e0c 100644 --- a/base/process/process_handle.h +++ b/base/process/process_handle.h @@ -28,7 +28,13 @@ class FilePath; // ProcessHandle is a platform specific type which represents the underlying OS // handle to a process. // ProcessId is a number which identifies the process in the OS. -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +typedef uint32_t ProcessHandle; +typedef uint32_t ProcessId; +const ProcessHandle kNullProcessHandle = 0; +const ProcessId kNullProcessId = 0; +#define CrPRIdPid "d" +#elif BUILDFLAG(IS_WIN) typedef HANDLE ProcessHandle; typedef DWORD ProcessId; typedef HANDLE UserTokenHandle; diff --git a/base/process/process_metrics.h b/base/process/process_metrics.h index 4e2541958828..28add2036c86 100644 --- a/base/process/process_metrics.h +++ b/base/process/process_metrics.h @@ -45,6 +45,8 @@ #include "base/threading/platform_thread.h" #endif +#if !defined(STARBOARD) + namespace base { // Full declaration is in process_metrics_iocounters.h. @@ -645,4 +647,5 @@ BASE_EXPORT MachVMRegionResult GetTopInfo(mach_port_t task, } // namespace base +#endif // !defined(STARBOARD) #endif // BASE_PROCESS_PROCESS_METRICS_H_ diff --git a/base/process/process_starboard.cc b/base/process/process_starboard.cc new file mode 100644 index 000000000000..709ad047474b --- /dev/null +++ b/base/process/process_starboard.cc @@ -0,0 +1,68 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "base/process/process.h" +#include "base/process/process_handle.h" +#include "starboard/common/process.h" + +namespace base { + +ProcessId GetProcId(ProcessHandle) { + return starboard::kStarboardFakeProcessId; +} + +ProcessId GetParentProcessId(ProcessHandle) { + return starboard::kStarboardFakeProcessId; +} + +ProcessId GetCurrentProcId() { + return starboard::kStarboardFakeProcessId; +} + +ProcessHandle GetCurrentProcessHandle() { + return GetCurrentProcId(); +} + +bool Process::IsProcessBackgrounded() const { + return false; +} + +Time Process::CreationTime() const { + return Time(); +} + +#ifndef STARBOARD +// static +void Process::TerminateCurrentProcessImmediately(int exit_code) { + std::_Exit(exit_code); +} +#endif // !STARBOARD + +Process Process::Current() { + return Process(); +} + +Process::Process(ProcessHandle handle) {} +Process::~Process() {} + +void Process::TerminateCurrentProcessImmediately(int) {} + +bool Process::IsValid() const { return false; } +bool Process::WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) const { return false; } +bool Process::Terminate(int exit_code, bool wait) const { return false; } +void Process::Close() {} + +} // namespace base diff --git a/base/profiler/module_cache_starboard.cc b/base/profiler/module_cache_starboard.cc new file mode 100644 index 000000000000..e1e1e10bd534 --- /dev/null +++ b/base/profiler/module_cache_starboard.cc @@ -0,0 +1,39 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "base/profiler/module_cache.h" + +#include "base/notreached.h" + +namespace base { + +class TModule : public ModuleCache::Module { + public: + TModule(); + uintptr_t GetBaseAddress() const override { return 0; } + std::string GetId() const override { return ""; } + FilePath GetDebugBasename() const override { return FilePath(); } + size_t GetSize() const override { return 0; } + bool IsNative() const override { return false; } +}; + +TModule::TModule() {} + +// static +std::unique_ptr ModuleCache::CreateModuleForAddress(uintptr_t address) { + NOTIMPLEMENTED(); + return std::make_unique(); +} + +} // namespace base diff --git a/base/profiler/sampling_profiler_thread_token.cc b/base/profiler/sampling_profiler_thread_token.cc index 8186e673d308..9e9cb728dc8e 100644 --- a/base/profiler/sampling_profiler_thread_token.cc +++ b/base/profiler/sampling_profiler_thread_token.cc @@ -16,7 +16,9 @@ namespace base { SamplingProfilerThreadToken GetSamplingProfilerCurrentThreadToken() { PlatformThreadId id = PlatformThread::CurrentId(); -#if BUILDFLAG(IS_ANDROID) +#if defined(STARBOARD) + return {id}; +#elif BUILDFLAG(IS_ANDROID) return {id, pthread_self()}; #elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) absl::optional maybe_stack_base = diff --git a/base/profiler/stack_copier_suspend_unittest.cc b/base/profiler/stack_copier_suspend_unittest.cc index 46de91d548c9..e51e57dffc54 100644 --- a/base/profiler/stack_copier_suspend_unittest.cc +++ b/base/profiler/stack_copier_suspend_unittest.cc @@ -64,7 +64,10 @@ class TestSuspendableThreadDelegate : public SuspendableThreadDelegate { RegisterContextStackPointer(thread_context) = reinterpret_cast(&(*fake_stack_)[0]); RegisterContextInstructionPointer(thread_context) = +/* Cobalt reinterpret_cast((*fake_stack_)[0]); +Cobalt */ + static_cast((*fake_stack_)[0]); return true; } diff --git a/base/profiler/stack_sampler_starboard.cc b/base/profiler/stack_sampler_starboard.cc new file mode 100644 index 000000000000..b8de56729473 --- /dev/null +++ b/base/profiler/stack_sampler_starboard.cc @@ -0,0 +1,24 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This is a dummy implementation. + +#include "base/profiler/stack_sampler.h" + +namespace base { + +std::unique_ptr StackSampler::Create( + SamplingProfilerThreadToken thread_token, + ModuleCache* module_cache, + UnwindersFactory core_unwinders_factory, + RepeatingClosure record_sample_callback, + StackSamplerTestDelegate* test_delegate) { + return nullptr; +} + +size_t StackSampler::GetStackBufferSize() { + return 0; +} + +} // namespace base diff --git a/base/profiler/stack_sampling_profiler_test_util.cc b/base/profiler/stack_sampling_profiler_test_util.cc index 2aab6684de13..940347c32873 100644 --- a/base/profiler/stack_sampling_profiler_test_util.cc +++ b/base/profiler/stack_sampling_profiler_test_util.cc @@ -31,7 +31,7 @@ #include "base/profiler/native_unwinder_android.h" #endif -#if BUILDFLAG(IS_WIN) +#if BUILDFLAG(IS_WIN) || defined(COMPILER_MSVC) // Windows doesn't provide an alloca function like Linux does. // Fortunately, it provides _alloca, which functions identically. #include @@ -276,6 +276,7 @@ NOINLINE FunctionAddressRange CallWithAlloca(OnceClosure wait_for_sample) { return {start_program_counter, end_program_counter}; } +#if !defined(STARBOARD) // Disable inlining for this function so that it gets its own stack frame. NOINLINE FunctionAddressRange CallThroughOtherLibrary(NativeLibrary library, OnceClosure wait_for_sample) { @@ -295,6 +296,7 @@ CallThroughOtherLibrary(NativeLibrary library, OnceClosure wait_for_sample) { const void* volatile end_program_counter = GetProgramCounter(); return {start_program_counter, end_program_counter}; } +#endif void WithTargetThread(UnwindScenario* scenario, ProfileCallback profile_callback) { @@ -424,6 +426,7 @@ void ExpectStackDoesNotContain( } } +#if !defined(STARBOARD) NativeLibrary LoadTestLibrary(StringPiece library_name) { // The lambda gymnastics works around the fact that we can't use ASSERT_* // macros in a function returning non-null. @@ -460,6 +463,7 @@ uintptr_t GetAddressInOtherLibrary(NativeLibrary library) { EXPECT_NE(address, 0u); return address; } +#endif StackSamplingProfiler::UnwindersFactory CreateCoreUnwindersFactoryForTesting( ModuleCache* module_cache) { diff --git a/base/profiler/stack_sampling_profiler_test_util.h b/base/profiler/stack_sampling_profiler_test_util.h index 8bf6db4605c7..d881ab7c78a2 100644 --- a/base/profiler/stack_sampling_profiler_test_util.h +++ b/base/profiler/stack_sampling_profiler_test_util.h @@ -139,11 +139,13 @@ FunctionAddressRange CallWithPlainFunction(OnceClosure wait_for_sample); // frame pointer. FunctionAddressRange CallWithAlloca(OnceClosure wait_for_sample); +#if !defined(STARBOARD) // Calls into |wait_for_sample| through a function within another library, to // test unwinding through multiple modules and scenarios involving unloaded // modules. FunctionAddressRange CallThroughOtherLibrary(NativeLibrary library, OnceClosure wait_for_sample); +#endif // The callback to perform profiling on the provided thread. using ProfileCallback = OnceCallback; @@ -180,6 +182,7 @@ void ExpectStackDoesNotContain( const std::vector& stack, const std::vector& functions); +#if !defined(STARBOARD) // Load test library with given name. NativeLibrary LoadTestLibrary(StringPiece library_name); @@ -188,6 +191,7 @@ NativeLibrary LoadTestLibrary(StringPiece library_name); NativeLibrary LoadOtherLibrary(); uintptr_t GetAddressInOtherLibrary(NativeLibrary library); +#endif // Creates a list of core unwinders required for StackSamplingProfilerTest. // This is useful notably on Android, which requires ChromeUnwinderAndroid in diff --git a/base/profiler/stack_sampling_profiler_unittest.cc b/base/profiler/stack_sampling_profiler_unittest.cc index 10556682a68f..800c895e5f07 100644 --- a/base/profiler/stack_sampling_profiler_unittest.cc +++ b/base/profiler/stack_sampling_profiler_unittest.cc @@ -189,6 +189,7 @@ void TestProfileBuilder::OnProfileCompleted(TimeDelta profile_duration, profile_duration, sampling_period}); } +#if !defined(STARBOARD) // Unloads |library| and returns when it has completed unloading. Unloading a // library is asynchronous on Windows, so simply calling UnloadNativeLibrary() // is insufficient to ensure it's been unloaded. @@ -213,6 +214,7 @@ void SynchronousUnloadNativeLibrary(NativeLibrary library) { NOTIMPLEMENTED(); #endif } +#endif void WithTargetThread(ProfileCallback profile_callback) { UnwindScenario scenario(BindRepeating(&CallWithPlainFunction)); @@ -287,6 +289,7 @@ TimeDelta AVeryLongTimeDelta() { return Days(1); } +#if !defined(STARBOARD) // Tests the scenario where the library is unloaded after copying the stack, but // before walking it. If |wait_until_unloaded| is true, ensures that the // asynchronous library loading has completed before walking the stack. If @@ -416,6 +419,7 @@ void TestLibraryUnload(bool wait_until_unloaded, ModuleCache* module_cache) { scenario.GetOuterFunctionAddressRange()}); } } +#endif // Provide a suitable (and clean) environment for the tests below. All tests // must use this class to ensure that proper clean-up is done and thus be @@ -530,6 +534,7 @@ PROFILER_TEST_F(StackSamplingProfilerTest, MAYBE_Alloca) { scenario.GetOuterFunctionAddressRange()}); } +#if !defined(STARBOARD) // Checks that a stack that runs through another library produces a stack with // the expected functions. // macOS ASAN is not yet supported - crbug.com/718628. @@ -601,6 +606,7 @@ PROFILER_TEST_F(StackSamplingProfilerTest, MAYBE_UnloadingLibrary) { PROFILER_TEST_F(StackSamplingProfilerTest, MAYBE_UnloadedLibrary) { TestLibraryUnload(true, module_cache()); } +#endif // Checks that a profiler can stop/destruct without ever having started. PROFILER_TEST_F(StackSamplingProfilerTest, StopWithoutStarting) { diff --git a/base/rand_util_starboard.cc b/base/rand_util_starboard.cc new file mode 100644 index 000000000000..86e8a1765f18 --- /dev/null +++ b/base/rand_util_starboard.cc @@ -0,0 +1,40 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "base/rand_util.h" + +#include "base/notreached.h" +#include "starboard/system.h" + +namespace base { + +namespace internal { + +void ConfigureBoringSSLBackedRandBytesFieldTrial() {} + +double RandDoubleAvoidAllocation() { + NOTREACHED(); + return 0.0; +} + +} + +// NOTE: This function must be cryptographically secure. http://crbug.com/140076 +void RandBytes(void* output, size_t output_length) { + if (output_length != 0) { + SbSystemGetRandomData(output, output_length); + } +} + +} // namespace base diff --git a/base/run_loop.cc b/base/run_loop.cc index e6b0b0483ca1..79e9b91430ca 100644 --- a/base/run_loop.cc +++ b/base/run_loop.cc @@ -15,13 +15,57 @@ #include "build/build_config.h" #include "third_party/abseil-cpp/absl/base/attributes.h" +#if defined(STARBOARD) +#include + +#include "starboard/thread.h" +#endif + namespace base { namespace { +#if defined(STARBOARD) +ABSL_CONST_INIT pthread_once_t s_once_delegate_flag = PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_local_delegate_key = 0; + +void InitThreadLocalDelegateKey() { + int res = pthread_key_create(&s_thread_local_delegate_key , NULL); + DCHECK(res == 0); +} + +void EnsureThreadLocalDelegateKeyInited() { + pthread_once(&s_once_delegate_flag, InitThreadLocalDelegateKey); +} + +RunLoop::Delegate* GetDelegate() { + EnsureThreadLocalDelegateKeyInited(); + return static_cast( + pthread_getspecific(s_thread_local_delegate_key)); +} + +ABSL_CONST_INIT pthread_once_t s_once_timeout_flag = PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_local_timeout_key = 0; + +void InitThreadLocalTimeoutKey() { + int res = pthread_key_create(&s_thread_local_timeout_key, NULL); + DCHECK(res == 0); +} + +void EnsureThreadLocalTimeoutKeyInited() { + pthread_once(&s_once_timeout_flag, InitThreadLocalTimeoutKey); +} + +const RunLoop::RunLoopTimeout* GetRunLoopTimeout() { + EnsureThreadLocalTimeoutKeyInited(); + return static_cast( + pthread_getspecific(s_thread_local_timeout_key)); +} +#else ABSL_CONST_INIT thread_local RunLoop::Delegate* delegate = nullptr; ABSL_CONST_INIT thread_local const RunLoop::RunLoopTimeout* run_loop_timeout = nullptr; +#endif // Runs |closure| immediately if this is called on |task_runner|, otherwise // forwards |closure| to it. @@ -56,8 +100,14 @@ RunLoop::Delegate::~Delegate() { // be on its creation thread (e.g. a Thread that fails to start) and // shouldn't disrupt that thread's state. if (bound_) { +#if defined(STARBOARD) + DCHECK_EQ(this, GetDelegate()); + EnsureThreadLocalDelegateKeyInited(); + pthread_setspecific(s_thread_local_delegate_key, nullptr); +#else DCHECK_EQ(this, delegate); delegate = nullptr; +#endif } } @@ -78,16 +128,30 @@ void RunLoop::RegisterDelegateForCurrentThread(Delegate* new_delegate) { DCHECK_CALLED_ON_VALID_THREAD(new_delegate->bound_thread_checker_); // There can only be one RunLoop::Delegate per thread. +#if defined(STARBOARD) + DCHECK(!GetDelegate()) +#else DCHECK(!delegate) +#endif << "Error: Multiple RunLoop::Delegates registered on the same thread.\n\n" "Hint: You perhaps instantiated a second " "MessageLoop/TaskEnvironment on a thread that already had one?"; +#if defined(STARBOARD) + EnsureThreadLocalDelegateKeyInited(); + pthread_setspecific(s_thread_local_delegate_key, new_delegate); + new_delegate->bound_ = true; +#else delegate = new_delegate; delegate->bound_ = true; +#endif } RunLoop::RunLoop(Type type) +#if defined(STARBOARD) + : delegate_(GetDelegate()), +#else : delegate_(delegate), +#endif type_(type), origin_task_runner_(SingleThreadTaskRunner::GetCurrentDefault()) { DCHECK(delegate_) << "A RunLoop::Delegate must be bound to this thread prior " @@ -226,22 +290,34 @@ bool RunLoop::AnyQuitCalled() { // static bool RunLoop::IsRunningOnCurrentThread() { +#if defined(STARBOARD) + auto delegate = GetDelegate(); +#endif return delegate && !delegate->active_run_loops_.empty(); } // static bool RunLoop::IsNestedOnCurrentThread() { +#if defined(STARBOARD) + auto delegate = GetDelegate(); +#endif return delegate && delegate->active_run_loops_.size() > 1; } // static void RunLoop::AddNestingObserverOnCurrentThread(NestingObserver* observer) { +#if defined(STARBOARD) + auto delegate = GetDelegate(); +#endif DCHECK(delegate); delegate->nesting_observers_.AddObserver(observer); } // static void RunLoop::RemoveNestingObserverOnCurrentThread(NestingObserver* observer) { +#if defined(STARBOARD) + auto delegate = GetDelegate(); +#endif DCHECK(delegate); delegate->nesting_observers_.RemoveObserver(observer); } @@ -249,6 +325,9 @@ void RunLoop::RemoveNestingObserverOnCurrentThread(NestingObserver* observer) { // static void RunLoop::QuitCurrentDeprecated() { DCHECK(IsRunningOnCurrentThread()); +#if defined(STARBOARD) + auto delegate = GetDelegate(); +#endif DCHECK(delegate->active_run_loops_.top()->allow_quit_current_deprecated_) << "Please migrate off QuitCurrentDeprecated(), e.g. to QuitClosure()."; delegate->active_run_loops_.top()->Quit(); @@ -257,6 +336,9 @@ void RunLoop::QuitCurrentDeprecated() { // static void RunLoop::QuitCurrentWhenIdleDeprecated() { DCHECK(IsRunningOnCurrentThread()); +#if defined(STARBOARD) + auto delegate = GetDelegate(); +#endif DCHECK(delegate->active_run_loops_.top()->allow_quit_current_deprecated_) << "Please migrate off QuitCurrentWhenIdleDeprecated(), e.g. to " "QuitWhenIdleClosure()."; @@ -274,7 +356,11 @@ RepeatingClosure RunLoop::QuitCurrentWhenIdleClosureDeprecated() { #if DCHECK_IS_ON() ScopedDisallowRunningRunLoop::ScopedDisallowRunningRunLoop() +#if defined(STARBOARD) + : current_delegate_(GetDelegate()), +#else : current_delegate_(delegate), +#endif previous_run_allowance_(current_delegate_ && current_delegate_->allow_running_for_testing_) { if (current_delegate_) @@ -282,7 +368,11 @@ ScopedDisallowRunningRunLoop::ScopedDisallowRunningRunLoop() } ScopedDisallowRunningRunLoop::~ScopedDisallowRunningRunLoop() { +#if defined(STARBOARD) + DCHECK_EQ(current_delegate_, GetDelegate()); +#else DCHECK_EQ(current_delegate_, delegate); +#endif if (current_delegate_) current_delegate_->allow_running_for_testing_ = previous_run_allowance_; } @@ -300,17 +390,26 @@ RunLoop::RunLoopTimeout::~RunLoopTimeout() = default; // static void RunLoop::SetTimeoutForCurrentThread(const RunLoopTimeout* timeout) { +#if defined(STARBOARD) + EnsureThreadLocalTimeoutKeyInited(); + pthread_setspecific(s_thread_local_timeout_key, const_cast(timeout)); +#else run_loop_timeout = timeout; +#endif } // static const RunLoop::RunLoopTimeout* RunLoop::GetTimeoutForCurrentThread() { +#if defined(STARBOARD) + return GetRunLoopTimeout(); +#else // Workaround false-positive MSAN use-of-uninitialized-value on // thread_local storage for loaded libraries: // https://github.com/google/sanitizers/issues/1265 MSAN_UNPOISON(&run_loop_timeout, sizeof(RunLoopTimeout*)); return run_loop_timeout; +#endif } bool RunLoop::BeforeRun() { diff --git a/base/run_loop.h b/base/run_loop.h index 3da3cc78169c..db326ac66b6b 100644 --- a/base/run_loop.h +++ b/base/run_loop.h @@ -86,6 +86,13 @@ class BASE_EXPORT RunLoop { // (directly or by running the RunLoop::QuitClosure). void Run(const Location& location = Location::Current()); +#if defined(STARBOARD) + // Starboard has its own for loop so it does not call RunLoop::Run and + // therefore requires these two functions public. + bool BeforeRun(); + void AfterRun(); +#endif + // Run the current RunLoop::Delegate until it doesn't find any tasks or // messages in its queue (it goes idle). // WARNING #1: This may run long (flakily timeout) and even never return! Do @@ -293,9 +300,13 @@ class BASE_EXPORT RunLoop { static void SetTimeoutForCurrentThread(const RunLoopTimeout* timeout); static const RunLoopTimeout* GetTimeoutForCurrentThread(); +#if !defined(STARBOARD) + // Starboard has its own for loop so it does not call RunLoop::Run and + // therefore requires these two functions public. // Return false to abort the Run. bool BeforeRun(); void AfterRun(); +#endif // A cached reference of RunLoop::Delegate for the thread driven by this // RunLoop for quick access without using TLS (also allows access to state diff --git a/base/sampling_heap_profiler/poisson_allocation_sampler.cc b/base/sampling_heap_profiler/poisson_allocation_sampler.cc index eda76af7b188..b8c4d6bfb80f 100644 --- a/base/sampling_heap_profiler/poisson_allocation_sampler.cc +++ b/base/sampling_heap_profiler/poisson_allocation_sampler.cc @@ -24,6 +24,10 @@ #include "base/allocator/dispatcher/standard_hooks.h" #endif +#if defined(STARBOARD) +#include "base/notreached.h" +#endif + namespace base { namespace { @@ -91,9 +95,14 @@ ThreadLocalData* GetThreadLocalData() { // https://github.com/gcc-mirror/gcc/blob/master/libgcc/emutls.c // macOS version is based on _tlv_get_addr from dyld: // https://opensource.apple.com/source/dyld/dyld-635.2/src/threadLocalHelpers.s.auto.html +#if defined(STARBOARD) + NOTIMPLEMENTED(); + return nullptr; +#else thread_local ThreadLocalData thread_local_data; return &thread_local_data; #endif +#endif } } // namespace diff --git a/base/sampling_heap_profiler/sampling_heap_profiler.cc b/base/sampling_heap_profiler/sampling_heap_profiler.cc index 0b531aa2c1c1..7f4f1946b418 100644 --- a/base/sampling_heap_profiler/sampling_heap_profiler.cc +++ b/base/sampling_heap_profiler/sampling_heap_profiler.cc @@ -4,12 +4,18 @@ #include "base/sampling_heap_profiler/sampling_heap_profiler.h" +#if defined(COBALT_PENDING_CLEAN_UP) +#include +#endif + #include #include #include +#if !defined(COBALT_PENDING_CLEAN_UP) #include "base/allocator/partition_allocator/partition_alloc.h" #include "base/allocator/partition_allocator/shim/allocator_shim.h" +#endif #include "base/compiler_specific.h" #include "base/debug/stack_trace.h" #include "base/feature_list.h" @@ -32,6 +38,13 @@ #include #endif +#if defined(STARBOARD) +#include + +#include "base/check_op.h" +#include "starboard/thread.h" +#endif + namespace base { constexpr uint32_t kMaxStackEntries = 256; @@ -82,12 +95,33 @@ const char* GetAndLeakThreadName() { } const char* UpdateAndGetThreadName(const char* name) { +#if defined(STARBOARD) + static pthread_once_t s_once_flag = PTHREAD_ONCE_INIT; + static pthread_key_t s_thread_local_key = 0; + + auto InitThreadLocalKey = [](){ + int res = pthread_key_create(&s_thread_local_key , NULL); + DCHECK(res == 0); + }; + + pthread_once(&s_once_flag, InitThreadLocalKey); + + const char* thread_name = + static_cast(pthread_getspecific(s_thread_local_key)); + if (name) + pthread_setspecific(s_thread_local_key, const_cast(name)); + else if (!thread_name) + pthread_setspecific(s_thread_local_key, + const_cast(GetAndLeakThreadName())); + return static_cast(pthread_getspecific(s_thread_local_key)); +#else static thread_local const char* thread_name; if (name) thread_name = name; if (!thread_name) thread_name = GetAndLeakThreadName(); return thread_name; +#endif } // Checks whether unwinding from this function works. diff --git a/base/scoped_clear_last_error.h b/base/scoped_clear_last_error.h index 633e9454ed4f..c3ca62e65814 100644 --- a/base/scoped_clear_last_error.h +++ b/base/scoped_clear_last_error.h @@ -44,7 +44,7 @@ class BASE_EXPORT ScopedClearLastError : public ScopedClearLastErrorBase { const unsigned long last_system_error_; }; -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) using ScopedClearLastError = ScopedClearLastErrorBase; diff --git a/base/scoped_native_library.h b/base/scoped_native_library.h index f2c446e326b0..3d085b32f755 100644 --- a/base/scoped_native_library.h +++ b/base/scoped_native_library.h @@ -9,6 +9,8 @@ #include "base/native_library.h" #include "base/scoped_generic.h" +#if !defined(STARBOARD) + namespace base { class FilePath; @@ -61,4 +63,5 @@ class BASE_EXPORT ScopedNativeLibrary } // namespace base +#endif // !defined(STARBOARD) #endif // BASE_SCOPED_NATIVE_LIBRARY_H_ diff --git a/base/security_unittest.cc b/base/security_unittest.cc index 04ca69f0446e..6faad637362b 100644 --- a/base/security_unittest.cc +++ b/base/security_unittest.cc @@ -66,7 +66,8 @@ void OverflowTestsSoftExpectTrue(bool overflow_detected) { #if BUILDFLAG(IS_APPLE) || defined(ADDRESS_SANITIZER) || \ defined(THREAD_SANITIZER) || defined(MEMORY_SANITIZER) || \ - BUILDFLAG(IS_HWASAN) || BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) + BUILDFLAG(IS_HWASAN) || BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) || \ + SB_IS(EVERGREEN) #define MAYBE_NewOverflow DISABLED_NewOverflow #else #define MAYBE_NewOverflow NewOverflow @@ -97,7 +98,7 @@ TEST(SecurityTest, MAYBE_NewOverflow) { char* volatile p = reinterpret_cast(array_pointer.get()); OverflowTestsSoftExpectTrue(!p); } -#if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_64_BITS) +#if BUILDFLAG(IS_WIN) || defined(COMPILER_MSVC) && defined(ARCH_CPU_64_BITS) // On Windows, the compiler prevents static array sizes of more than // 0x7fffffff (error C2148). #else diff --git a/base/sequence_token.cc b/base/sequence_token.cc index c19cdf4778b2..b0a003f29268 100644 --- a/base/sequence_token.cc +++ b/base/sequence_token.cc @@ -7,6 +7,14 @@ #include "base/atomic_sequence_num.h" #include "third_party/abseil-cpp/absl/base/attributes.h" +#if defined(STARBOARD) +#include + +#include "base/check_op.h" +#include "starboard/thread.h" +#include "base/logging.h" +#endif + namespace base { namespace { @@ -15,8 +23,71 @@ base::AtomicSequenceNumber g_sequence_token_generator; base::AtomicSequenceNumber g_task_token_generator; +#if defined(STARBOARD) +ABSL_CONST_INIT pthread_once_t s_once_sequence_flag = PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_local_sequence_key = 0; + +void InitThreadLocalSequenceKey() { + int res = pthread_key_create(&s_thread_local_sequence_key, NULL); + DCHECK(res == 0); +} + +void EnsureThreadLocalSequenceKeyInited() { + pthread_once(&s_once_sequence_flag, InitThreadLocalSequenceKey); +} + +ABSL_CONST_INIT pthread_once_t s_once_set_sequence_flag = PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_local_sequence_set_for_thread = 0; +void InitThreadLocalSequenceBoolKey() { + int res = pthread_key_create(&s_thread_local_sequence_set_for_thread, NULL); + DCHECK(res == 0); +} + +void EnsureThreadLocalSequenceBoolKeyInited() { + pthread_once(&s_once_set_sequence_flag, InitThreadLocalSequenceBoolKey); +} + +bool IsSequenceSetForThread() { + EnsureThreadLocalSequenceBoolKeyInited(); + void* set_for_thread = + pthread_getspecific(s_thread_local_sequence_set_for_thread); + return !!set_for_thread ? reinterpret_cast(set_for_thread) != 0 + : false; +} + +ABSL_CONST_INIT pthread_once_t s_once_task_flag = PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_local_task_key = 0; + +void InitThreadLocalTaskKey() { + int res = pthread_key_create(&s_thread_local_task_key , NULL); + DCHECK(res == 0); +} + +void EnsureThreadLocalTaskKeyInited() { + pthread_once(&s_once_task_flag, InitThreadLocalTaskKey); +} + +ABSL_CONST_INIT pthread_once_t s_once_set_task_flag = PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_local_task_set_for_thread = 0; +void InitThreadLocalTaskBoolKey() { + int res = pthread_key_create(&s_thread_local_task_set_for_thread, NULL); + DCHECK(res == 0); +} + +void EnsureThreadLocalTaskBoolKeyInited() { + pthread_once(&s_once_set_task_flag, InitThreadLocalTaskBoolKey); +} + +bool IsTaskSetForThread() { + EnsureThreadLocalTaskBoolKeyInited(); + void* set_for_thread = pthread_getspecific(s_thread_local_task_set_for_thread); + return !!set_for_thread ? reinterpret_cast(set_for_thread) != 0 + : false; +} +#else ABSL_CONST_INIT thread_local SequenceToken current_sequence_token; ABSL_CONST_INIT thread_local TaskToken current_task_token; +#endif } // namespace @@ -41,7 +112,18 @@ SequenceToken SequenceToken::Create() { } SequenceToken SequenceToken::GetForCurrentThread() { +#if defined(STARBOARD) + if (IsSequenceSetForThread()) { + EnsureThreadLocalSequenceKeyInited(); + int token = static_cast(reinterpret_cast( + pthread_getspecific(s_thread_local_sequence_key))); + return SequenceToken(token); + } else { + return SequenceToken(); + } +#else return current_sequence_token; +#endif } bool TaskToken::operator==(const TaskToken& other) const { @@ -61,11 +143,50 @@ TaskToken TaskToken::Create() { } TaskToken TaskToken::GetForCurrentThread() { +#if defined(STARBOARD) + if (IsTaskSetForThread()) { + EnsureThreadLocalTaskKeyInited(); + int token = static_cast(reinterpret_cast( + pthread_getspecific(s_thread_local_task_key))); + return TaskToken(token); + } else { + return TaskToken(); + } +#else return current_task_token; +#endif } ScopedSetSequenceTokenForCurrentThread::ScopedSetSequenceTokenForCurrentThread( const SequenceToken& sequence_token) +#if defined(STARBOARD) +{ + EnsureThreadLocalSequenceKeyInited(); + auto sequence_reset_token = TaskToken::GetForCurrentThread(); + DCHECK(!sequence_reset_token.IsValid()); + scoped_sequence_reset_value_ = reinterpret_cast( + static_cast(sequence_reset_token.GetToken())); + pthread_setspecific(s_thread_local_sequence_key, + reinterpret_cast( + static_cast(sequence_token.GetToken()))); + + EnsureThreadLocalTaskKeyInited(); + auto task_reset_token = TaskToken::GetForCurrentThread(); + DCHECK(!task_reset_token.IsValid()); + scoped_task_reset_value_ = reinterpret_cast( + static_cast(task_reset_token.GetToken())); + pthread_setspecific(s_thread_local_task_key, + reinterpret_cast(static_cast( + TaskToken::Create().GetToken()))); + + EnsureThreadLocalSequenceBoolKeyInited(); + pthread_setspecific(s_thread_local_sequence_set_for_thread, + reinterpret_cast(static_cast(true))); + EnsureThreadLocalTaskBoolKeyInited(); + pthread_setspecific(s_thread_local_task_set_for_thread, + reinterpret_cast(static_cast(true))); +} +#else // The lambdas here exist because invalid tokens don't compare equal, so // passing invalid sequence/task tokens as the third args to AutoReset // constructors doesn't work. @@ -78,8 +199,21 @@ ScopedSetSequenceTokenForCurrentThread::ScopedSetSequenceTokenForCurrentThread( DCHECK(!current_task_token.IsValid()); return TaskToken::Create(); }()) {} +#endif +#if defined(STARBOARD) +ScopedSetSequenceTokenForCurrentThread:: + ~ScopedSetSequenceTokenForCurrentThread() { + EnsureThreadLocalSequenceKeyInited(); + pthread_setspecific(s_thread_local_sequence_key, + scoped_sequence_reset_value_); + + EnsureThreadLocalTaskKeyInited(); + pthread_setspecific(s_thread_local_task_key, scoped_task_reset_value_); +} +#else ScopedSetSequenceTokenForCurrentThread:: ~ScopedSetSequenceTokenForCurrentThread() = default; +#endif } // namespace base diff --git a/base/sequence_token.h b/base/sequence_token.h index e37ef6177a6d..74039e0344f9 100644 --- a/base/sequence_token.h +++ b/base/sequence_token.h @@ -42,6 +42,10 @@ class BASE_EXPORT SequenceToken { // if any. static SequenceToken GetForCurrentThread(); +#if defined(STARBOARD) + int GetToken() const { return token_; } +#endif + private: explicit SequenceToken(int token) : token_(token) {} @@ -78,6 +82,10 @@ class BASE_EXPORT TaskToken { // invalid TaskToken. static TaskToken GetForCurrentThread(); +#if defined(STARBOARD) + int GetToken() { return token_; } +#endif + private: friend class ScopedSetSequenceTokenForCurrentThread; @@ -110,8 +118,13 @@ class BASE_EXPORT ~ScopedSetSequenceTokenForCurrentThread(); private: +#if defined(STARBOARD) + void* scoped_sequence_reset_value_; + void* scoped_task_reset_value_; +#else const AutoReset sequence_token_resetter_; const AutoReset task_token_resetter_; +#endif }; } // namespace base diff --git a/base/single_thread_task_runner.h b/base/single_thread_task_runner.h new file mode 100644 index 000000000000..5f0e5062b70a --- /dev/null +++ b/base/single_thread_task_runner.h @@ -0,0 +1,10 @@ +#ifndef BASE_SINGLE_THREAD_TASK_RUNNER_H_ +#define BASE_SINGLE_THREAD_TASK_RUNNER_H_ + +#ifndef COBALT_PENDING_CLEAN_UP +#error "Remove" +#endif + +#include "base/task/single_thread_task_runner.h" + +#endif diff --git a/base/state_transitions_unittest.cc b/base/state_transitions_unittest.cc index f0d5fb5454cf..11085d6b997f 100644 --- a/base/state_transitions_unittest.cc +++ b/base/state_transitions_unittest.cc @@ -65,7 +65,7 @@ TEST(StateTransitionsTest, DCHECK_STATE_TRANSITION) { #if DCHECK_IS_ON() // EXPECT_DEATH is not defined on IOS. -#if !BUILDFLAG(IS_IOS) +#if !BUILDFLAG(IS_IOS) && !defined(STARBOARD) EXPECT_DEATH( DCHECK_STATE_TRANSITION(&transitions, State::kState1, State::kState4), "Check failed.*Invalid transition: 0 -> 3"); @@ -73,7 +73,7 @@ TEST(StateTransitionsTest, DCHECK_STATE_TRANSITION) { EXPECT_DEATH( DCHECK_STATE_TRANSITION(&transitions, State::kState3, State::kState4), "Check failed.*Invalid transition: 2 -> 3"); -#endif // !BUILDFLAG(IS_IOS) +#endif // !BUILDFLAG(IS_IOS) && !defined(STARBOARD) #endif // DCHECK_IS_ON() } diff --git a/base/stl_util.h b/base/stl_util.h index 69a1aaba0485..78af946762bd 100644 --- a/base/stl_util.h +++ b/base/stl_util.h @@ -14,9 +14,19 @@ #include "base/check.h" #include "base/ranges/algorithm.h" +#include "base/containers/cxx20_erase.h" namespace base { +#ifdef COBALT_PENDING_CLEAN_UP +// Test to see if a set or map contains a particular key. +// Returns true if the key is in the collection. +template +bool ContainsKey(const Collection& collection, const Key& key) { + return collection.find(key) != collection.end(); +} +#endif + namespace internal { template diff --git a/base/strings/safe_sprintf.h b/base/strings/safe_sprintf.h index ecdd8f06d6fc..da62e825c5ee 100644 --- a/base/strings/safe_sprintf.h +++ b/base/strings/safe_sprintf.h @@ -15,6 +15,9 @@ #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) // For ssize_t #include +#elif defined(STARBOARD) +#include "starboard/common/string.h" +#include "starboard/types.h" #endif #include "base/base_export.h" diff --git a/base/strings/safe_sprintf_unittest.cc b/base/strings/safe_sprintf_unittest.cc index 9b94a99973f3..1cfe22200c89 100644 --- a/base/strings/safe_sprintf_unittest.cc +++ b/base/strings/safe_sprintf_unittest.cc @@ -17,6 +17,12 @@ #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" +#if defined(STARBOARD) +#include "starboard/common/string.h" +#include "starboard/memory.h" +#include "starboard/types.h" +#endif + // Death tests on Android are currently very flaky. No need to add more flaky // tests, as they just make it hard to spot real problems. // TODO(markus): See if the restrictions on Android can eventually be lifted. diff --git a/base/strings/string16.h b/base/strings/string16.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/base/strings/string_number_conversions.h b/base/strings/string_number_conversions.h index f8a3bd8862f5..b6c5f47b95f5 100644 --- a/base/strings/string_number_conversions.h +++ b/base/strings/string_number_conversions.h @@ -79,6 +79,11 @@ BASE_EXPORT bool StringToUint(StringPiece16 input, unsigned* output); BASE_EXPORT bool StringToInt64(StringPiece input, int64_t* output); BASE_EXPORT bool StringToInt64(StringPiece16 input, int64_t* output); +#if defined(STARBOARD) +BASE_EXPORT bool StringToInt32(StringPiece input, int32_t* output); +BASE_EXPORT bool StringToUint32(StringPiece input, uint32_t* output); +#endif + BASE_EXPORT bool StringToUint64(StringPiece input, uint64_t* output); BASE_EXPORT bool StringToUint64(StringPiece16 input, uint64_t* output); diff --git a/base/strings/string_number_conversions_unittest.cc b/base/strings/string_number_conversions_unittest.cc index 7834139350f5..3dc8fd8ee9e4 100644 --- a/base/strings/string_number_conversions_unittest.cc +++ b/base/strings/string_number_conversions_unittest.cc @@ -18,6 +18,10 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "testing/gtest/include/gtest/gtest.h" +#if defined(STARBOARD) +#include "starboard/memory.h" +#include "starboard/types.h" +#endif namespace base { diff --git a/base/strings/string_piece.h b/base/strings/string_piece.h index 61273898b1bd..cbe0aeb27c60 100644 --- a/base/strings/string_piece.h +++ b/base/strings/string_piece.h @@ -483,6 +483,17 @@ class GSL_POINTER BasicStringPiece { return find_last_not_of(BasicStringPiece(s), pos); } +#ifdef COBALT_PENDING_CLEAN_UP +// Remove once we're on C++20+ + constexpr bool starts_with(BasicStringPiece v) const noexcept { + return rfind(v, 0) == 0; + } + + constexpr bool ends_with(BasicStringPiece v) const noexcept { + return find(v) == size() - v.size(); + } +#endif + static constexpr size_type npos = size_type(-1); protected: diff --git a/base/strings/string_util.cc b/base/strings/string_util.cc index 74f9e9aa4dda..4dfa9fabfbf8 100644 --- a/base/strings/string_util.cc +++ b/base/strings/string_util.cc @@ -249,6 +249,18 @@ bool IsStringUTF8AllowingNoncharacters(StringPiece str) { return internal::DoIsStringUTF8(str); } +#if defined(STARBOARD) +bool EqualsCaseInsensitiveASCII(const char* a_begin, + const char* a_end, + const char* b) { + for (const char *it = a_begin; it != a_end; ++it, ++b) { + if (!*b || base::ToLowerASCII(*it) != *b) + return false; + } + return *b == 0; +} +#endif + bool EqualsASCII(StringPiece16 str, StringPiece ascii) { return ranges::equal(ascii, str); } diff --git a/base/strings/string_util.h b/base/strings/string_util.h index 0367af552ead..2176abb38412 100644 --- a/base/strings/string_util.h +++ b/base/strings/string_util.h @@ -182,6 +182,9 @@ BASE_EXPORT constexpr int CompareCaseInsensitiveASCII(StringPiece16 a, // To compare all Unicode code points case-insensitively, use // base::i18n::ToLower or base::i18n::FoldCase and then compare with either == // or !=. +#ifdef STARBOARD +BASE_EXPORT bool EqualsCaseInsensitiveASCII(const char* a_begin, const char* a_end, const char* b); +#endif inline bool EqualsCaseInsensitiveASCII(StringPiece a, StringPiece b) { return internal::EqualsCaseInsensitiveASCIIT(a, b); } @@ -571,7 +574,9 @@ BASE_EXPORT std::u16string ReplaceStringPlaceholders( } // namespace base -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +#include "base/strings/string_util_starboard.h" +#elif BUILDFLAG(IS_WIN) #include "base/strings/string_util_win.h" #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) #include "base/strings/string_util_posix.h" diff --git a/base/strings/string_util_starboard.cc b/base/strings/string_util_starboard.cc new file mode 100644 index 000000000000..e0b82616466a --- /dev/null +++ b/base/strings/string_util_starboard.cc @@ -0,0 +1,27 @@ +// Copyright 2024 The Cobalt Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "base/strings/string_util_starboard.h" + +#include "base/strings/string_util_impl_helpers.h" + +namespace base { + +#if !defined(WCHAR_T_IS_UTF32) +bool IsStringASCII(WStringPiece str) { + return internal::DoIsStringASCII(str.data(), str.length()); +} +#endif + +} // namespace base diff --git a/base/strings/string_util_starboard.h b/base/strings/string_util_starboard.h new file mode 100644 index 000000000000..90da5a8ce1ec --- /dev/null +++ b/base/strings/string_util_starboard.h @@ -0,0 +1,88 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef BASE_STRING_UTIL_STARBOARD_H_ +#define BASE_STRING_UTIL_STARBOARD_H_ + +#include +#if SB_API_VERSION >= 16 +#include +#endif + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/strings/string_util.h" +#include "starboard/common/string.h" +#include "starboard/memory.h" +#include "starboard/types.h" + +namespace base { + +inline int vsnprintf(char* buffer, size_t size, + const char* format, va_list arguments) { + return ::vsnprintf(buffer, size, format, arguments); +} + +inline int c16SbMemoryCompare(const char16* s1, const char16* s2, size_t n) { + // We cannot call memcmp because that changes the semantics. + while (n-- > 0) { + if (*s1 != *s2) { + // We cannot use (*s1 - *s2) because char16 is unsigned. + return ((*s1 < *s2) ? -1 : 1); + } + ++s1; + ++s2; + } + return 0; +} + +inline const char16_t* as_u16cstr(const wchar_t* str) { + return reinterpret_cast(str); +} + +inline const char16_t* as_u16cstr(WStringPiece str) { + return reinterpret_cast(str.data()); +} + +BASE_EXPORT bool IsStringASCII(WStringPiece str); + +#if defined(WCHAR_T_IS_UTF16) +inline wchar_t* as_writable_wcstr(char16_t* str) { + return reinterpret_cast(str); +} + +inline wchar_t* as_writable_wcstr(std::u16string& str) { + return reinterpret_cast(data(str)); +} + +inline const wchar_t* as_wcstr(const char16_t* str) { + return reinterpret_cast(str); +} + +inline const wchar_t* as_wcstr(StringPiece16 str) { + return reinterpret_cast(str.data()); +} + +inline char16_t* as_writable_u16cstr(wchar_t* str) { + return reinterpret_cast(str); +} + +inline char16_t* as_writable_u16cstr(std::wstring& str) { + return reinterpret_cast(data(str)); +} +#endif + +} // namespace base + +#endif // BASE_STRING_UTIL_STARBOARD_H_ diff --git a/base/strings/string_util_unittest.cc b/base/strings/string_util_unittest.cc index 4bb22e54c76f..7751370c2dd0 100644 --- a/base/strings/string_util_unittest.cc +++ b/base/strings/string_util_unittest.cc @@ -17,6 +17,9 @@ #include "base/strings/string_piece.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" +#include "starboard/common/string.h" +#include "starboard/memory.h" +#include "starboard/types.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/base/strings/stringprintf.h b/base/strings/stringprintf.h index 7894c49320a5..1e6c2e4d5154 100644 --- a/base/strings/stringprintf.h +++ b/base/strings/stringprintf.h @@ -12,6 +12,9 @@ #include "base/base_export.h" #include "base/compiler_specific.h" #include "build/build_config.h" +#if defined(STARBOARD) +#include "starboard/types.h" +#endif namespace base { diff --git a/base/strings/stringprintf_unittest.cc b/base/strings/stringprintf_unittest.cc index 270afe11f68d..b322c46389f1 100644 --- a/base/strings/stringprintf_unittest.cc +++ b/base/strings/stringprintf_unittest.cc @@ -139,7 +139,9 @@ TEST(StringPrintfTest, Grow) { const int kRefSize = 320000; char* ref = new char[kRefSize]; -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) + snprintf(ref, kRefSize, fmt, src, src, src, src, src, src, src); +#elif defined(OS_WIN) sprintf_s(ref, kRefSize, fmt, src, src, src, src, src, src, src); #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) snprintf(ref, kRefSize, fmt, src, src, src, src, src, src, src); diff --git a/base/strings/sys_string_conversions_posix.cc b/base/strings/sys_string_conversions_posix.cc index 82215c542c79..2d192b47c394 100644 --- a/base/strings/sys_string_conversions_posix.cc +++ b/base/strings/sys_string_conversions_posix.cc @@ -27,7 +27,7 @@ std::wstring SysUTF8ToWide(StringPiece utf8) { return out; } -#if defined(SYSTEM_NATIVE_UTF8) || BUILDFLAG(IS_ANDROID) +#if defined(SYSTEM_NATIVE_UTF8) || BUILDFLAG(IS_ANDROID) || defined(STARBOARD) // TODO(port): Consider reverting the OS_ANDROID when we have wcrtomb() // support and a better understanding of what calls these routines. @@ -154,6 +154,7 @@ std::wstring SysNativeMBToWide(StringPiece native_mb) { return out; } -#endif // defined(SYSTEM_NATIVE_UTF8) || BUILDFLAG(IS_ANDROID) +#endif // defined(SYSTEM_NATIVE_UTF8) || BUILDFLAG(IS_ANDROID) || + // defined(STARBOARD) } // namespace base diff --git a/base/strings/sys_string_conversions_starboard.cc b/base/strings/sys_string_conversions_starboard.cc new file mode 100644 index 000000000000..3e7cfda9eecc --- /dev/null +++ b/base/strings/sys_string_conversions_starboard.cc @@ -0,0 +1,16 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// We use the same code as the POSIX version. +#include "sys_string_conversions_posix.cc" diff --git a/base/substring_set_matcher/substring_set_matcher.h b/base/substring_set_matcher/substring_set_matcher.h index 3dd00dc1d681..03f543d0b0ce 100644 --- a/base/substring_set_matcher/substring_set_matcher.h +++ b/base/substring_set_matcher/substring_set_matcher.h @@ -305,7 +305,11 @@ class BASE_EXPORT SubstringSetMatcher { // If not equal to zero, will be a multiple of 4, so that we can use // SIMD to accelerate looking for edges. uint16_t edges_capacity_ = 0; +#if defined(STARBOARD) && defined(COMPILER_MSVC) + }; +#else } __attribute__((packed)); +#endif using SubstringPatternVector = std::vector; diff --git a/base/sync_socket.cc b/base/sync_socket.cc index 8ed46ae811c0..a019b71a6c2d 100644 --- a/base/sync_socket.cc +++ b/base/sync_socket.cc @@ -4,6 +4,7 @@ #include "base/sync_socket.h" +#if !defined(STARBOARD) namespace base { const SyncSocket::Handle SyncSocket::kInvalidHandle = kInvalidPlatformFile; @@ -29,3 +30,5 @@ CancelableSyncSocket::CancelableSyncSocket(ScopedHandle handle) : SyncSocket(std::move(handle)) {} } // namespace base + +#endif // !defined(STARBOARD) diff --git a/base/sync_socket.h b/base/sync_socket.h index 0d7288384a37..27746a3a9ab9 100644 --- a/base/sync_socket.h +++ b/base/sync_socket.h @@ -22,6 +22,8 @@ #endif #include +#if !defined(STARBOARD) + namespace base { class BASE_EXPORT SyncSocket { @@ -138,4 +140,6 @@ class BASE_EXPORT CancelableSyncSocket : public SyncSocket { } // namespace base +#endif // !defined(STARBOARD) + #endif // BASE_SYNC_SOCKET_H_ diff --git a/base/synchronization/condition_variable.h b/base/synchronization/condition_variable.h index 17144f0a20e1..4c84bc200b25 100644 --- a/base/synchronization/condition_variable.h +++ b/base/synchronization/condition_variable.h @@ -66,16 +66,24 @@ #include "base/memory/raw_ptr.h" #include "build/build_config.h" +#include "base/base_export.h" +#include "base/synchronization/lock.h" + +#if defined(STARBOARD) +#if SB_API_VERSION < 16 +#include "starboard/condition_variable.h" +#else +#include +#endif // SB_API_VERSION < 16 +#else #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) #include #endif -#include "base/base_export.h" -#include "base/synchronization/lock.h" - #if BUILDFLAG(IS_WIN) #include "base/win/windows_types.h" #endif +#endif namespace base { @@ -113,7 +121,15 @@ class BASE_EXPORT ConditionVariable { void declare_only_used_while_idle() { waiting_is_blocking_ = false; } private: -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +#if SB_API_VERSION < 16 + SbConditionVariable condition_; + SbMutex* user_mutex_; +#else + pthread_cond_t condition_; + pthread_mutex_t* user_mutex_; +#endif // SB_API_VERSION < 16 +#elif BUILDFLAG(IS_WIN) CHROME_CONDITION_VARIABLE cv_; const raw_ptr srwlock_; #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) diff --git a/base/synchronization/condition_variable_starboard.cc b/base/synchronization/condition_variable_starboard.cc new file mode 100644 index 000000000000..c46217e0490d --- /dev/null +++ b/base/synchronization/condition_variable_starboard.cc @@ -0,0 +1,139 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "base/synchronization/condition_variable.h" + +#include "base/logging.h" +#include "base/synchronization/lock.h" +#include "base/threading/scoped_blocking_call.h" +#include "base/time/time.h" +#include "starboard/common/condition_variable.h" +#include "starboard/common/mutex.h" + +namespace base { + +ConditionVariable::ConditionVariable(Lock* user_lock) + : user_mutex_(user_lock->lock_.native_handle()) +#if DCHECK_IS_ON() + , + user_lock_(user_lock) +#endif +{ +#if SB_API_VERSION < 16 + bool result = SbConditionVariableCreate(&condition_, user_mutex_); + DCHECK(result); +#else +#if !SB_HAS_QUIRK(NO_CONDATTR_SETCLOCK_SUPPORT) + pthread_condattr_t attribute; + pthread_condattr_init(&attribute); + pthread_condattr_setclock(&attribute, CLOCK_MONOTONIC); + + int result = pthread_cond_init(&condition_, &attribute); + DCHECK(result == 0); + + pthread_condattr_destroy(&attribute); +#else + int result = pthread_cond_init(&condition_, nullptr); + DCHECK(result == 0); +#endif // !SB_HAS_QUIRK(NO_CONDATTR_SETCLOCK_SUPPORT) +#endif // SB_API_VERSION < 16 +} + +ConditionVariable::~ConditionVariable() { +#if SB_API_VERSION < 16 + bool result = SbConditionVariableDestroy(&condition_); + DCHECK(result); +#else + int result = pthread_cond_destroy(&condition_); + DCHECK(result == 0); +#endif // SB_API_VERSION < 16 +} + +void ConditionVariable::Wait() { + absl::optional + scoped_blocking_call; + if (waiting_is_blocking_) + scoped_blocking_call.emplace(FROM_HERE, BlockingType::MAY_BLOCK); + +#if DCHECK_IS_ON() + user_lock_->CheckHeldAndUnmark(); +#endif +#if SB_API_VERSION < 16 + SbConditionVariableResult result = + SbConditionVariableWait(&condition_, user_mutex_); + DCHECK(SbConditionVariableIsSignaled(result)); +#else + int result = pthread_cond_wait(&condition_, user_mutex_); + DCHECK(result == 0); +#endif // SB_API_VERSION < 16 +#if DCHECK_IS_ON() + user_lock_->CheckUnheldAndMark(); +#endif +} + +void ConditionVariable::TimedWait(const TimeDelta& max_time) { + absl::optional + scoped_blocking_call; + if (waiting_is_blocking_) + scoped_blocking_call.emplace(FROM_HERE, BlockingType::MAY_BLOCK); + int64_t duration = max_time.InMicroseconds(); + +#if DCHECK_IS_ON() + user_lock_->CheckHeldAndUnmark(); +#endif +#if SB_API_VERSION < 16 + SbConditionVariableResult result = + SbConditionVariableWaitTimed(&condition_, user_mutex_, duration); + DCHECK_NE(kSbConditionVariableFailed, result); +#else +#if !SB_HAS_QUIRK(NO_CONDATTR_SETCLOCK_SUPPORT) + int64_t timeout_time_usec = starboard::CurrentMonotonicTime(); +#else + int64_t timeout_time_usec = starboard::CurrentPosixTime(); +#endif // !SB_HAS_QUIRK(NO_CONDATTR_SETCLOCK_SUPPORT) + timeout_time_usec += max_time.InMicroseconds(); + + struct timespec timeout; + timeout.tv_sec = timeout_time_usec / 1000'000; + timeout.tv_nsec = (timeout_time_usec % 1000'000) * 1000; + + int result = pthread_cond_timedwait(&condition_, user_mutex_, &timeout); + DCHECK(result == 0 || result == ETIMEDOUT); +#endif +#if DCHECK_IS_ON() + user_lock_->CheckUnheldAndMark(); +#endif +} + +void ConditionVariable::Broadcast() { +#if SB_API_VERSION < 16 + bool result = SbConditionVariableBroadcast(&condition_); + DCHECK(result); +#else + int result = pthread_cond_broadcast(&condition_); + DCHECK(result == 0); +#endif // SB_API_VERSION < 16 +} + +void ConditionVariable::Signal() { +#if SB_API_VERSION < 16 + bool result = SbConditionVariableSignal(&condition_); + DCHECK(result); +#else + int result = pthread_cond_signal(&condition_); + DCHECK(result == 0); +#endif // SB_API_VERSION < 16 +} + +} // namespace base diff --git a/base/synchronization/condition_variable_unittest.cc b/base/synchronization/condition_variable_unittest.cc index cc2587d3c506..9cb96e868329 100644 --- a/base/synchronization/condition_variable_unittest.cc +++ b/base/synchronization/condition_variable_unittest.cc @@ -193,7 +193,7 @@ TEST_F(ConditionVariableTest, TimeoutTest) { lock.Release(); } -#if BUILDFLAG(IS_POSIX) +#if BUILDFLAG(IS_POSIX) && !defined(STARBOARD) const int kDiscontinuitySeconds = 2; void BackInTime(Lock* lock) { diff --git a/base/synchronization/lock.h b/base/synchronization/lock.h index 60ecf178c7e3..206abd000d4a 100644 --- a/base/synchronization/lock.h +++ b/base/synchronization/lock.h @@ -74,7 +74,9 @@ class LOCKABLE BASE_EXPORT Lock { // Whether Lock mitigates priority inversion when used from different thread // priorities. static bool HandlesMultipleThreadPriorities() { -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) + return false; +#elif BUILDFLAG(IS_WIN) // Windows mitigates priority inversion by randomly boosting the priority of // ready threads. // https://msdn.microsoft.com/library/windows/desktop/ms684831.aspx diff --git a/base/synchronization/lock_impl.h b/base/synchronization/lock_impl.h index b983547adaad..76b692a80d4c 100644 --- a/base/synchronization/lock_impl.h +++ b/base/synchronization/lock_impl.h @@ -11,7 +11,14 @@ #include "base/thread_annotations.h" #include "build/build_config.h" -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +#if SB_API_VERSION < 16 +#include "starboard/common/mutex.h" +#else +#include +#endif +#include "base/check_op.h" +#elif BUILDFLAG(IS_WIN) #include "base/win/windows_types.h" #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) #include @@ -45,7 +52,13 @@ class BASE_EXPORT LockImpl { friend class base::win::internal::AutoNativeLock; friend class base::win::internal::ScopedHandleVerifier; -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +#if SB_API_VERSION < 16 + using NativeHandle = SbMutex; +#else + using NativeHandle = pthread_mutex_t; +#endif // SB_API_VERSION < 16 +#elif BUILDFLAG(IS_WIN) using NativeHandle = CHROME_SRWLOCK; #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) using NativeHandle = pthread_mutex_t; @@ -93,7 +106,28 @@ void LockImpl::Lock() { LockInternal(); } -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +bool LockImpl::Try() { +#if SB_API_VERSION < 16 + SbMutexResult result = SbMutexAcquireTry(&native_handle_); + DCHECK_NE(kSbMutexDestroyed, result); + return SbMutexIsSuccess(result); +#else + int result = pthread_mutex_trylock(&native_handle_); + return result == 0; +#endif // SB_API_VERSION < 16 +} + +void LockImpl::Unlock() { +#if SB_API_VERSION < 16 + bool result = SbMutexRelease(&native_handle_); + DCHECK(result); +#else + int result = pthread_mutex_unlock(&native_handle_); + DCHECK(result == 0); +#endif //SB_API_VERSION < 16 +} +#elif BUILDFLAG(IS_WIN) bool LockImpl::Try() { return !!::TryAcquireSRWLockExclusive( reinterpret_cast(&native_handle_)); diff --git a/base/synchronization/lock_impl_starboard.cc b/base/synchronization/lock_impl_starboard.cc new file mode 100644 index 000000000000..545ba4c9646b --- /dev/null +++ b/base/synchronization/lock_impl_starboard.cc @@ -0,0 +1,57 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "base/synchronization/lock_impl.h" + +#include "base/check_op.h" + +#if SB_API_VERSION < 16 +#include "starboard/mutex.h" +#endif // SB_API_VERSION < 16 + +namespace base { +namespace internal { + +LockImpl::LockImpl() { +#if SB_API_VERSION < 16 + bool result = SbMutexCreate(&native_handle_); + DCHECK(result); +#else + int result = pthread_mutex_init(&native_handle_, nullptr); + DCHECK_EQ(result, 0); +#endif // SB_API_VERSION < 16 +} + +LockImpl::~LockImpl() { +#if SB_API_VERSION < 16 + bool result = SbMutexDestroy(&native_handle_); + DCHECK(result); +#else + int result = pthread_mutex_destroy(&native_handle_); + DCHECK_EQ(result, 0); +#endif // SB_API_VERSION < 16 +} + +void LockImpl::LockInternal() { +#if SB_API_VERSION < 16 + SbMutexResult result = SbMutexAcquire(&native_handle_); + DCHECK_NE(kSbMutexDestroyed, result); +#else + int result = pthread_mutex_lock(&native_handle_); + DCHECK_EQ(result, 0); +#endif // SB_API_VERSION < 16 +} + +} // namespace internal +} // namespace base diff --git a/base/synchronization/waitable_event.h b/base/synchronization/waitable_event.h index 0ba651964ff1..ab5e32ef9bc6 100644 --- a/base/synchronization/waitable_event.h +++ b/base/synchronization/waitable_event.h @@ -22,7 +22,7 @@ #include "base/functional/callback_forward.h" #include "base/mac/scoped_mach_port.h" #include "base/memory/ref_counted.h" -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) #include #include @@ -215,7 +215,7 @@ class BASE_EXPORT WaitableEvent { // the event, unlike the receive right, since a deleted event cannot be // signaled. mac::ScopedMachSendRight send_right_; -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) // On Windows, you must not close a HANDLE which is currently being waited on. // The MSDN documentation says that the resulting behaviour is 'undefined'. // To solve that issue each WaitableEventWatcher duplicates the given event diff --git a/base/synchronization/waitable_event_starboard.cc b/base/synchronization/waitable_event_starboard.cc new file mode 100644 index 000000000000..43010d671fc6 --- /dev/null +++ b/base/synchronization/waitable_event_starboard.cc @@ -0,0 +1,16 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// We use the same code as the POSIX version. +#include "base/synchronization/waitable_event_posix.cc" diff --git a/base/synchronization/waitable_event_watcher_starboard.cc b/base/synchronization/waitable_event_watcher_starboard.cc new file mode 100644 index 000000000000..518f92966332 --- /dev/null +++ b/base/synchronization/waitable_event_watcher_starboard.cc @@ -0,0 +1,16 @@ +// Copyright 2024 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// We use the same code as the POSIX version. +#include "base/synchronization/waitable_event_watcher_posix.cc" diff --git a/base/sys_info_starboard.cc b/base/sys_info_starboard.cc new file mode 100644 index 000000000000..a468c4aab2c7 --- /dev/null +++ b/base/sys_info_starboard.cc @@ -0,0 +1,97 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "base/system/sys_info.h" + +#include "base/notreached.h" +#include "starboard/common/system_property.h" +#include "starboard/system.h" + +namespace base { + using starboard::kSystemPropertyMaxLength; + +// static +int SysInfo::NumberOfProcessors() { + return SbSystemGetNumberOfProcessors(); +} + +int SysInfo::NumberOfEfficientProcessorsImpl() { + return NumberOfProcessors(); +} + +size_t SysInfo::VMAllocationGranularity() { + // This is referred to ONLY by persistent memory allocator and shared + // memory feature; not used in Cobalt production. + NOTIMPLEMENTED(); + return 4096U; +} + +// static +int64_t SysInfo::AmountOfFreeDiskSpace(const FilePath& path) { + // TODO: This is referred to ONLY by disk_cache::BackendImpl, which I do + // not think is currently used in Cobalt. There's no need to implement this + // unless we want to use it for something. If not, we should remove the + // reference to it, and this amazing implementation. + NOTIMPLEMENTED(); + return SB_INT64_C(1) * 1024 * 1024 * 1024; +} + +// static +uint64_t SysInfo::AmountOfPhysicalMemoryImpl() { + return SbSystemGetTotalCPUMemory(); +} + +// static +uint64_t SysInfo::AmountOfAvailablePhysicalMemoryImpl() { + return SbSystemGetTotalCPUMemory() - SbSystemGetUsedCPUMemory(); +} + +// static +int64_t SysInfo::AmountOfTotalDiskSpace(const FilePath& /* path */) { + NOTIMPLEMENTED(); + return SB_INT64_C(1) * 1024 * 1024 * 1024; +} + +// static +uint64_t SysInfo::AmountOfVirtualMemory() { + return AmountOfPhysicalMemoryImpl(); +} + +// static +std::string SysInfo::OperatingSystemName() { + char value[kSystemPropertyMaxLength]; + SbSystemGetProperty(kSbSystemPropertyPlatformName, value, + kSystemPropertyMaxLength); + return value; +} + +// static +std::string SysInfo::OperatingSystemVersion() { + return SysInfo::OperatingSystemName(); +} + +// static +void SysInfo::OperatingSystemVersionNumbers(int32_t* major_version, + int32_t* minor_version, + int32_t* bugfix_version) { + NOTIMPLEMENTED(); +} + +SysInfo::HardwareInfo SysInfo::GetHardwareInfoSync() { + NOTIMPLEMENTED(); + HardwareInfo info; + return info; +} + +} // namespace base diff --git a/base/system/sys_info_unittest.cc b/base/system/sys_info_unittest.cc index a0c96f726344..1f8cbbc747bb 100644 --- a/base/system/sys_info_unittest.cc +++ b/base/system/sys_info_unittest.cc @@ -61,8 +61,13 @@ TEST_F(SysInfoTest, NumProcs) { EXPECT_GE(SysInfo::NumberOfProcessors(), 1); EXPECT_GE(SysInfo::NumberOfEfficientProcessors(), 0); +#if defined(STARBOARD) + EXPECT_EQ(SysInfo::NumberOfEfficientProcessors(), + SysInfo::NumberOfProcessors()); +#else EXPECT_LT(SysInfo::NumberOfEfficientProcessors(), SysInfo::NumberOfProcessors()); +#endif } #if BUILDFLAG(IS_MAC) @@ -89,6 +94,7 @@ TEST_F(SysInfoTest, AmountOfMem) { EXPECT_GE(SysInfo::AmountOfVirtualMemory(), 0u); } +#if !defined(STARBOARD) #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) #define MAYBE_AmountOfAvailablePhysicalMemory \ @@ -128,6 +134,7 @@ TEST_F(SysInfoTest, MAYBE_AmountOfAvailablePhysicalMemory) { } #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || // BUILDFLAG(IS_ANDROID) +#endif TEST_F(SysInfoTest, AmountOfFreeDiskSpace) { // We aren't actually testing that it's correct, just that it's sane. @@ -175,6 +182,7 @@ TEST_F(SysInfoTest, OperatingSystemVersion) { EXPECT_FALSE(version.empty()); } +#if !defined(STARBOARD) TEST_F(SysInfoTest, OperatingSystemVersionNumbers) { int32_t os_major_version = -1; int32_t os_minor_version = -1; @@ -185,6 +193,7 @@ TEST_F(SysInfoTest, OperatingSystemVersionNumbers) { EXPECT_GT(os_minor_version, -1); EXPECT_GT(os_bugfix_version, -1); } +#endif // !defined(STARBOARD) #endif #if BUILDFLAG(IS_IOS) @@ -258,7 +267,9 @@ TEST_F(SysInfoTest, GetHardwareInfo) { EXPECT_TRUE(IsStringUTF8(hardware_info->manufacturer)); EXPECT_TRUE(IsStringUTF8(hardware_info->model)); bool empty_result_expected = -#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN) || \ +#if defined(STARBOARD) + true; +#elif BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN) || \ BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA) false; #else diff --git a/base/task/common/scoped_defer_task_posting.cc b/base/task/common/scoped_defer_task_posting.cc index caddbb5d9f6c..d012ec19989f 100644 --- a/base/task/common/scoped_defer_task_posting.cc +++ b/base/task/common/scoped_defer_task_posting.cc @@ -7,14 +7,41 @@ #include "base/compiler_specific.h" #include "third_party/abseil-cpp/absl/base/attributes.h" +#if defined(STARBOARD) +#include + +#include "base/check_op.h" +#include "starboard/thread.h" +#endif + namespace base { namespace { +#if defined(STARBOARD) +ABSL_CONST_INIT pthread_once_t s_once_flag = PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_local_key = 0; + +void InitThreadLocalKey() { + int res = pthread_key_create(&s_thread_local_key , NULL); + DCHECK(res == 0); +} + +void EnsureThreadLocalKeyInited() { + pthread_once(&s_once_flag, InitThreadLocalKey); +} + +ScopedDeferTaskPosting* GetScopedDeferTaskPosting() { + EnsureThreadLocalKeyInited(); + return static_cast( + pthread_getspecific(s_thread_local_key)); +} +#else // Holds a thread-local pointer to the current scope or null when no // scope is active. ABSL_CONST_INIT thread_local ScopedDeferTaskPosting* scoped_defer_task_posting = nullptr; +#endif } // namespace @@ -36,12 +63,16 @@ void ScopedDeferTaskPosting::PostOrDefer( // static ScopedDeferTaskPosting* ScopedDeferTaskPosting::Get() { +#if defined(STARBOARD) + return GetScopedDeferTaskPosting(); +#else // Workaround false-positive MSAN use-of-uninitialized-value on // thread_local storage for loaded libraries: // https://github.com/google/sanitizers/issues/1265 MSAN_UNPOISON(&scoped_defer_task_posting, sizeof(ScopedDeferTaskPosting*)); return scoped_defer_task_posting; +#endif } // static @@ -50,7 +81,12 @@ bool ScopedDeferTaskPosting::Set(ScopedDeferTaskPosting* scope) { // get nested scopes. In this case ignore all except the top one. if (Get() && scope) return false; +#if defined(STARBOARD) + EnsureThreadLocalKeyInited(); + pthread_setspecific(s_thread_local_key, scope); +#else scoped_defer_task_posting = scope; +#endif return true; } diff --git a/base/task/common/task_annotator.cc b/base/task/common/task_annotator.cc index 367eda578797..29197965eb1c 100644 --- a/base/task/common/task_annotator.cc +++ b/base/task/common/task_annotator.cc @@ -26,12 +26,62 @@ #include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_mojo_event_info.pbzero.h" // nogncheck #endif +#if defined(STARBOARD) +#include + +#include "base/check_op.h" +#include "starboard/thread.h" +#endif + namespace base { namespace { TaskAnnotator::ObserverForTesting* g_task_annotator_observer = nullptr; +#if defined(STARBOARD) +ABSL_CONST_INIT pthread_once_t s_once_task_flag = PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_local_task_key = 0; + +void InitThreadLocalTaskKey() { + int res = pthread_key_create(&s_thread_local_task_key, NULL); + DCHECK(res == 0); +} + +void EnsureThreadLocalTaskKeyInited() { + pthread_once(&s_once_task_flag, InitThreadLocalTaskKey); +} + +PendingTask* GetCurrentPendingTask() { + EnsureThreadLocalTaskKeyInited(); + return static_cast( + pthread_getspecific(s_thread_local_task_key)); +} + +ABSL_CONST_INIT pthread_once_t s_once_hash_flag = PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_local_hash_key = 0; + +void InitThreadLocalHashKey() { + int res = pthread_key_create(&s_thread_local_hash_key , NULL); + DCHECK(res == 0); +} + +void EnsureThreadLocalHashKeyInited() { + pthread_once(&s_once_hash_flag, InitThreadLocalHashKey); +} + +ABSL_CONST_INIT pthread_once_t s_once_tracker_flag = PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_local_tracker_key = 0; + +void InitThreadLocalTrackerKey() { + int res = pthread_key_create(&s_thread_local_tracker_key , NULL); + DCHECK(res == 0); +} + +void EnsureThreadLocalTrackerKeyInited() { + pthread_once(&s_once_tracker_flag, InitThreadLocalTrackerKey); +} +#else // The PendingTask currently in progress on each thread. Used to allow creating // a breadcrumb of program counters on the stack to help identify a task's // origin in crashes. @@ -45,10 +95,16 @@ ABSL_CONST_INIT thread_local TaskAnnotator::ScopedSetIpcHash* ABSL_CONST_INIT thread_local TaskAnnotator::LongTaskTracker* current_long_task_tracker = nullptr; +#endif // These functions can be removed, and the calls below replaced with direct // variable accesses, once the MSAN workaround is not necessary. TaskAnnotator::ScopedSetIpcHash* GetCurrentScopedIpcHash() { +#if defined(STARBOARD) + EnsureThreadLocalHashKeyInited(); + return static_cast( + pthread_getspecific(s_thread_local_hash_key)); +#else // Workaround false-positive MSAN use-of-uninitialized-value on // thread_local storage for loaded libraries: // https://github.com/google/sanitizers/issues/1265 @@ -56,9 +112,15 @@ TaskAnnotator::ScopedSetIpcHash* GetCurrentScopedIpcHash() { sizeof(TaskAnnotator::ScopedSetIpcHash*)); return current_scoped_ipc_hash; +#endif } TaskAnnotator::LongTaskTracker* GetCurrentLongTaskTracker() { +#if defined(STARBOARD) + EnsureThreadLocalTrackerKeyInited(); + return static_cast( + pthread_getspecific(s_thread_local_tracker_key)); +#else // Workaround false-positive MSAN use-of-uninitialized-value on // thread_local storage for loaded libraries: // https://github.com/google/sanitizers/issues/1265 @@ -66,17 +128,22 @@ TaskAnnotator::LongTaskTracker* GetCurrentLongTaskTracker() { sizeof(TaskAnnotator::LongTaskTracker*)); return current_long_task_tracker; +#endif } } // namespace const PendingTask* TaskAnnotator::CurrentTaskForThread() { +#if defined(STARBOARD) + return GetCurrentPendingTask(); +#else // Workaround false-positive MSAN use-of-uninitialized-value on // thread_local storage for loaded libraries: // https://github.com/google/sanitizers/issues/1265 MSAN_UNPOISON(¤t_pending_task, sizeof(PendingTask*)); return current_pending_task; +#endif } void TaskAnnotator::OnIPCReceived(const char* interface_name, @@ -177,8 +244,14 @@ void TaskAnnotator::RunTaskImpl(PendingTask& pending_task) { base::debug::Alias(&task_time); { +#if defined(STARBOARD) + EnsureThreadLocalTaskKeyInited(); + void* reset_to = pthread_getspecific(s_thread_local_task_key); + pthread_setspecific(s_thread_local_task_key, &pending_task); +#else const AutoReset resetter(¤t_pending_task, &pending_task); +#endif if (g_task_annotator_observer) { g_task_annotator_observer->BeforeRunTask(&pending_task); @@ -204,6 +277,10 @@ void TaskAnnotator::RunTaskImpl(PendingTask& pending_task) { : "%xmm6", "%xmm7", "%xmm8", "%xmm9", "%xmm10", "%xmm11", "%xmm12", "%xmm13", "%xmm14", "%xmm15"); #endif + +#if defined(STARBOARD) + pthread_setspecific(s_thread_local_task_key, reset_to); +#endif } // Stomp the markers. Otherwise they can stick around on the unused parts of @@ -282,9 +359,19 @@ TaskAnnotator::ScopedSetIpcHash::ScopedSetIpcHash( TaskAnnotator::ScopedSetIpcHash::ScopedSetIpcHash( uint32_t ipc_hash, const char* ipc_interface_name) +#if defined(STARBOARD) + : +#else : resetter_(¤t_scoped_ipc_hash, this), +#endif ipc_hash_(ipc_hash), - ipc_interface_name_(ipc_interface_name) {} + ipc_interface_name_(ipc_interface_name) { +#if defined(STARBOARD) + EnsureThreadLocalHashKeyInited(); + scoped_reset_value_ = pthread_getspecific(s_thread_local_hash_key); + pthread_setspecific(s_thread_local_hash_key, this); +#endif +} // Static uint32_t TaskAnnotator::ScopedSetIpcHash::MD5HashMetricName( @@ -299,15 +386,29 @@ uint32_t TaskAnnotator::ScopedSetIpcHash::MD5HashMetricName( TaskAnnotator::ScopedSetIpcHash::~ScopedSetIpcHash() { DCHECK_EQ(this, GetCurrentScopedIpcHash()); +#if defined(STARBOARD) + EnsureThreadLocalHashKeyInited(); + pthread_setspecific(s_thread_local_hash_key, scoped_reset_value_); +#endif } TaskAnnotator::LongTaskTracker::LongTaskTracker(const TickClock* tick_clock, PendingTask& pending_task, TaskAnnotator* task_annotator) +#if defined(STARBOARD) + : +#else : resetter_(¤t_long_task_tracker, this), +#endif tick_clock_(tick_clock), pending_task_(pending_task), task_annotator_(task_annotator) { +#if defined(STARBOARD) + EnsureThreadLocalTrackerKeyInited(); + scoped_reset_value_ = pthread_getspecific(s_thread_local_tracker_key); + pthread_setspecific(s_thread_local_tracker_key, this); +#endif + TRACE_EVENT_CATEGORY_GROUP_ENABLED("scheduler.long_tasks", &is_tracing_); if (is_tracing_) { task_start_time_ = tick_clock_->NowTicks(); @@ -316,6 +417,10 @@ TaskAnnotator::LongTaskTracker::LongTaskTracker(const TickClock* tick_clock, TaskAnnotator::LongTaskTracker::~LongTaskTracker() { DCHECK_EQ(this, GetCurrentLongTaskTracker()); +#if defined(STARBOARD) + EnsureThreadLocalTrackerKeyInited(); + pthread_setspecific(s_thread_local_tracker_key, scoped_reset_value_); +#endif if (!is_tracing_) return; diff --git a/base/task/common/task_annotator.h b/base/task/common/task_annotator.h index ffc9fe344d29..e0a8e7dbfecd 100644 --- a/base/task/common/task_annotator.h +++ b/base/task/common/task_annotator.h @@ -138,7 +138,11 @@ class BASE_EXPORT [[maybe_unused, nodiscard]] TaskAnnotator::ScopedSetIpcHash { private: ScopedSetIpcHash(uint32_t ipc_hash, const char* ipc_interface_name); +#if defined(STARBOARD) + void* scoped_reset_value_; +#else const AutoReset resetter_; +#endif uint32_t ipc_hash_; const char* ipc_interface_name_; }; @@ -168,7 +172,11 @@ class BASE_EXPORT [[maybe_unused, nodiscard]] TaskAnnotator::LongTaskTracker { private: void EmitReceivedIPCDetails(perfetto::EventContext& ctx); +#if defined(STARBOARD) + void* scoped_reset_value_; +#else const AutoReset resetter_; +#endif // For tracking task duration raw_ptr tick_clock_; // Not owned. diff --git a/base/task/current_thread.cc b/base/task/current_thread.cc index b062ee6f827b..86f4b70d4044 100644 --- a/base/task/current_thread.cc +++ b/base/task/current_thread.cc @@ -211,7 +211,16 @@ MessagePumpForIO* CurrentIOThread::GetMessagePumpForIO() const { #if !BUILDFLAG(IS_NACL) -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +bool CurrentIOThread::Watch(SbSocket socket, + bool persistent, + SbSocketWaiterInterest interests, + SocketWatcher* controller, + Watcher* delegate) { + return static_cast(GetMessagePumpForIO()) + ->Watch(socket, persistent, interests, controller, delegate); +} +#elif BUILDFLAG(IS_WIN) HRESULT CurrentIOThread::RegisterIOHandler( HANDLE file, MessagePumpForIO::IOHandler* handler) { diff --git a/base/task/current_thread.h b/base/task/current_thread.h index c7e113456e01..d1d8ed7b1025 100644 --- a/base/task/current_thread.h +++ b/base/task/current_thread.h @@ -270,7 +270,17 @@ class BASE_EXPORT CurrentIOThread : public CurrentThread { #if !BUILDFLAG(IS_NACL) -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) + typedef base::MessagePumpIOStarboard::Watcher Watcher; + typedef base::MessagePumpIOStarboard::SocketWatcher SocketWatcher; + typedef base::MessagePumpIOStarboard::IOObserver IOObserver; + + bool Watch(SbSocket socket, + bool persistent, + SbSocketWaiterInterest interests, + SocketWatcher* controller, + Watcher* delegate); +#elif BUILDFLAG(IS_WIN) // Please see MessagePumpWin for definitions of these methods. HRESULT RegisterIOHandler(HANDLE file, MessagePumpForIO::IOHandler* handler); bool RegisterJobObject(HANDLE job, MessagePumpForIO::IOHandler* handler); diff --git a/base/task/scoped_set_task_priority_for_current_thread.cc b/base/task/scoped_set_task_priority_for_current_thread.cc index afc3f0c1fed3..c52d561ef8b7 100644 --- a/base/task/scoped_set_task_priority_for_current_thread.cc +++ b/base/task/scoped_set_task_priority_for_current_thread.cc @@ -7,32 +7,104 @@ #include "base/compiler_specific.h" #include "third_party/abseil-cpp/absl/base/attributes.h" +#if defined(STARBOARD) +#include + +#include "base/check_op.h" +#include "starboard/thread.h" +#endif + namespace base { namespace internal { namespace { +#if defined(STARBOARD) +ABSL_CONST_INIT pthread_once_t s_once_flag = PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_local_key = 0; + +void InitThreadLocalKey() { + int res = pthread_key_create(&s_thread_local_key , NULL); + DCHECK(res == 0); +} + +void EnsureThreadLocalKeyInited() { + pthread_once(&s_once_flag, InitThreadLocalKey); +} + +ABSL_CONST_INIT pthread_once_t s_once_set_flag = PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_local_set_for_thread = 0; +void InitThreadLocalBoolKey() { + int res = pthread_key_create(&s_thread_local_set_for_thread, NULL); + DCHECK(res == 0); +} + +void EnsureThreadLocalBoolKeyInited() { + pthread_once(&s_once_set_flag, InitThreadLocalBoolKey); +} + +bool IsValueSetForThread() { + EnsureThreadLocalBoolKeyInited(); + void* set_for_thread = pthread_getspecific(s_thread_local_set_for_thread); + return !!set_for_thread ? reinterpret_cast(set_for_thread) != 0 + : false; +} +#else ABSL_CONST_INIT thread_local TaskPriority task_priority_for_current_thread = TaskPriority::USER_BLOCKING; +#endif } // namespace ScopedSetTaskPriorityForCurrentThread::ScopedSetTaskPriorityForCurrentThread( TaskPriority priority) +#if defined(STARBOARD) +{ + EnsureThreadLocalKeyInited(); + scoped_reset_value_ = reinterpret_cast(static_cast( + static_cast(GetTaskPriorityForCurrentThread()))); + + pthread_setspecific(s_thread_local_key, + reinterpret_cast(static_cast( + static_cast(priority)))); + EnsureThreadLocalBoolKeyInited(); + pthread_setspecific(s_thread_local_set_for_thread, + reinterpret_cast(static_cast(true))); +} +#else : resetter_(&task_priority_for_current_thread, priority, TaskPriority::USER_BLOCKING) {} +#endif +#if defined(STARBOARD) +ScopedSetTaskPriorityForCurrentThread:: + ~ScopedSetTaskPriorityForCurrentThread() { + EnsureThreadLocalKeyInited(); + pthread_setspecific(s_thread_local_key, scoped_reset_value_); +} +#else ScopedSetTaskPriorityForCurrentThread:: ~ScopedSetTaskPriorityForCurrentThread() = default; +#endif TaskPriority GetTaskPriorityForCurrentThread() { +#if defined(STARBOARD) + EnsureThreadLocalKeyInited(); + void* task_priority_for_current_thread = + pthread_getspecific(s_thread_local_key); + return IsValueSetForThread() ? static_cast(static_cast( + reinterpret_cast( + task_priority_for_current_thread))) + : TaskPriority::USER_BLOCKING; +#else // Workaround false-positive MSAN use-of-uninitialized-value on // thread_local storage for loaded libraries: // https://github.com/google/sanitizers/issues/1265 MSAN_UNPOISON(&task_priority_for_current_thread, sizeof(TaskPriority)); return task_priority_for_current_thread; +#endif } } // namespace internal diff --git a/base/task/scoped_set_task_priority_for_current_thread.h b/base/task/scoped_set_task_priority_for_current_thread.h index 3e934936b202..4f53c51db4f9 100644 --- a/base/task/scoped_set_task_priority_for_current_thread.h +++ b/base/task/scoped_set_task_priority_for_current_thread.h @@ -27,7 +27,11 @@ class BASE_EXPORT ~ScopedSetTaskPriorityForCurrentThread(); private: +#if defined(STARBOARD) + void* scoped_reset_value_; +#else const AutoReset resetter_; +#endif }; // Returns the priority of the task running on the current thread, diff --git a/base/task/sequence_manager/hierarchical_timing_wheel.h b/base/task/sequence_manager/hierarchical_timing_wheel.h index 96bbe6cb4c93..9a1504bf71da 100644 --- a/base/task/sequence_manager/hierarchical_timing_wheel.h +++ b/base/task/sequence_manager/hierarchical_timing_wheel.h @@ -350,7 +350,10 @@ class HierarchicalTimingWheel { private: bool IsHeap(size_t hierarchy_index) { +/* Cobalt return hierarchy_index == 0 or hierarchy_index == TotalWheels + 1; +Cobalt */ + return hierarchy_index == 0 || hierarchy_index == TotalWheels + 1; } auto& GetHeapForHierarchyIndex(size_t hierarchy_index) { diff --git a/base/task/sequence_manager/sequence_manager_impl.cc b/base/task/sequence_manager/sequence_manager_impl.cc index e688f876762c..15b96bde3bcc 100644 --- a/base/task/sequence_manager/sequence_manager_impl.cc +++ b/base/task/sequence_manager/sequence_manager_impl.cc @@ -38,12 +38,39 @@ #include "third_party/abseil-cpp/absl/base/attributes.h" #include "third_party/abseil-cpp/absl/types/optional.h" +#if defined(STARBOARD) +#include + +#include "base/check_op.h" +#include "starboard/thread.h" +#endif + namespace base { namespace sequence_manager { namespace { +#if defined(STARBOARD) +ABSL_CONST_INIT pthread_once_t s_once_flag = PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_local_key = 0; + +void InitThreadLocalKey() { + int res = pthread_key_create(&s_thread_local_key, NULL); + DCHECK(res == 0); +} + +void EnsureThreadLocalKeyInited() { + pthread_once(&s_once_flag, InitThreadLocalKey); +} + +internal::SequenceManagerImpl* GetThreadLocalSequenceManager() { + EnsureThreadLocalKeyInited(); + return static_cast( + pthread_getspecific(s_thread_local_key)); +} +#else ABSL_CONST_INIT thread_local internal::SequenceManagerImpl* thread_local_sequence_manager = nullptr; +#endif class TracedBaseValue : public trace_event::ConvertableToTraceFormat { public: @@ -159,12 +186,16 @@ bool g_explicit_high_resolution_timer_win = false; // static SequenceManagerImpl* SequenceManagerImpl::GetCurrent() { +#if defined(STARBOARD) + return GetThreadLocalSequenceManager(); +#else // Workaround false-positive MSAN use-of-uninitialized-value on // thread_local storage for loaded libraries: // https://github.com/google/sanitizers/issues/1265 MSAN_UNPOISON(&thread_local_sequence_manager, sizeof(SequenceManagerImpl*)); return thread_local_sequence_manager; +#endif } SequenceManagerImpl::SequenceManagerImpl( @@ -235,7 +266,12 @@ SequenceManagerImpl::~SequenceManagerImpl() { // OK, now make it so that no one can find us. if (GetMessagePump()) { DCHECK_EQ(this, GetCurrent()); +#if defined(STARBOARD) + EnsureThreadLocalKeyInited(); + pthread_setspecific(s_thread_local_key, nullptr); +#else thread_local_sequence_manager = nullptr; +#endif } } @@ -332,7 +368,7 @@ void SequenceManagerImpl::BindToMessagePump(std::unique_ptr pump) { #endif // On iOS attach to the native loop when there is one. -#if BUILDFLAG(IS_IOS) +#if BUILDFLAG(IS_IOS) || defined(STARBOARD) if (settings_.message_loop_type == MessagePumpType::UI) { controller_->AttachToMessagePump(); } @@ -360,7 +396,12 @@ void SequenceManagerImpl::CompleteInitializationOnBoundThread() { if (GetMessagePump()) { DCHECK(!GetCurrent()) << "Can't register a second SequenceManagerImpl on the same thread."; +#if defined(STARBOARD) + EnsureThreadLocalKeyInited(); + pthread_setspecific(s_thread_local_key, this); +#else thread_local_sequence_manager = this; +#endif } } @@ -1118,7 +1159,7 @@ bool SequenceManagerImpl::IsTaskExecutionAllowed() const { return controller_->IsTaskExecutionAllowed(); } -#if BUILDFLAG(IS_IOS) +#if BUILDFLAG(IS_IOS) || defined(STARBOARD) void SequenceManagerImpl::AttachToMessagePump() { return controller_->AttachToMessagePump(); } diff --git a/base/task/sequence_manager/sequence_manager_impl.h b/base/task/sequence_manager/sequence_manager_impl.h index 5ab3d6b8b341..a0a0c169dd9f 100644 --- a/base/task/sequence_manager/sequence_manager_impl.h +++ b/base/task/sequence_manager/sequence_manager_impl.h @@ -172,7 +172,7 @@ class BASE_EXPORT SequenceManagerImpl void SetAddQueueTimeToTasks(bool enable); void SetTaskExecutionAllowed(bool allowed); bool IsTaskExecutionAllowed() const; -#if BUILDFLAG(IS_IOS) +#if BUILDFLAG(IS_IOS) || defined(STARBOARD) void AttachToMessagePump(); #endif bool IsIdleForTesting() override; diff --git a/base/task/sequence_manager/task_queue_impl.cc b/base/task/sequence_manager/task_queue_impl.cc index 6d92e7bfe5ae..477821710bad 100644 --- a/base/task/sequence_manager/task_queue_impl.cc +++ b/base/task/sequence_manager/task_queue_impl.cc @@ -539,6 +539,7 @@ void TaskQueueImpl::ScheduleDelayedWorkTask(Task pending_task) { TraceQueueSize(); } +#if !defined(STARBOARD) void TaskQueueImpl::RecordQueuingDelayedTaskMetrics(const Task& pending_task, LazyNow* lazy_now) { // The sampling depends on having a high-resolution clock. @@ -566,6 +567,7 @@ void TaskQueueImpl::RecordQueuingDelayedTaskMetrics(const Task& pending_task, static_cast(main_thread_only().delayed_incoming_queue.size())); } } +#endif void TaskQueueImpl::ReloadEmptyImmediateWorkQueue() { DCHECK(main_thread_only().immediate_work_queue->Empty()); diff --git a/base/task/sequence_manager/task_queue_impl.h b/base/task/sequence_manager/task_queue_impl.h index e631d9b7eb8a..a1d25373a4bb 100644 --- a/base/task/sequence_manager/task_queue_impl.h +++ b/base/task/sequence_manager/task_queue_impl.h @@ -502,10 +502,15 @@ class BASE_EXPORT TaskQueueImpl { void MoveReadyImmediateTasksToImmediateWorkQueueLocked() EXCLUSIVE_LOCKS_REQUIRED(any_thread_lock_); +#if defined(STARBOARD) + void RecordQueuingDelayedTaskMetrics(const Task& pending_task, + LazyNow* lazy_now) {} +#else // Records the delay for some tasks in the main thread and the size of the // |delayed_incoming_queue| pseudorandomly in a histogram. void RecordQueuingDelayedTaskMetrics(const Task& pending_task, LazyNow* lazy_now); +#endif // LazilyDeallocatedDeque use TimeTicks to figure out when to resize. We // should use real time here always. diff --git a/base/task/sequence_manager/thread_controller.h b/base/task/sequence_manager/thread_controller.h index 138bdcdda77a..2db10ba82ea8 100644 --- a/base/task/sequence_manager/thread_controller.h +++ b/base/task/sequence_manager/thread_controller.h @@ -132,7 +132,7 @@ class BASE_EXPORT ThreadController { // Returns true if the current run loop should quit when idle. virtual bool ShouldQuitRunLoopWhenIdle() = 0; -#if BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID) +#if BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID) || defined(STARBOARD) // On iOS, the main message loop cannot be Run(). Instead call // AttachToMessagePump(), which connects this ThreadController to the // UI thread's CFRunLoop and allows PostTask() to work. @@ -430,8 +430,13 @@ class BASE_EXPORT ThreadController { [[maybe_unused]] const raw_ref outer_; #if BUILDFLAG(ENABLE_BASE_TRACING) +#if defined(STARBOARD) + TerminatingFlowLambda terminating_wakeup_lambda_{ + perfetto::TerminatingFlow::FromPointer(const_cast(this))}; +#else TerminatingFlowLambda terminating_wakeup_lambda_{ perfetto::TerminatingFlow::FromPointer(this)}; +#endif #endif std::stack> run_levels_ diff --git a/base/task/sequence_manager/thread_controller_impl.cc b/base/task/sequence_manager/thread_controller_impl.cc index 424a7af54466..ce77f88be9d4 100644 --- a/base/task/sequence_manager/thread_controller_impl.cc +++ b/base/task/sequence_manager/thread_controller_impl.cc @@ -363,7 +363,7 @@ MessagePump* ThreadControllerImpl::GetBoundMessagePump() const { return nullptr; } -#if BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID) +#if BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID) || defined(STARBOARD) void ThreadControllerImpl::AttachToMessagePump() { NOTREACHED(); } diff --git a/base/task/sequence_manager/thread_controller_impl.h b/base/task/sequence_manager/thread_controller_impl.h index f49f71b147ed..262a4bdd6c57 100644 --- a/base/task/sequence_manager/thread_controller_impl.h +++ b/base/task/sequence_manager/thread_controller_impl.h @@ -61,7 +61,7 @@ class BASE_EXPORT ThreadControllerImpl : public ThreadController, void SetTaskExecutionAllowed(bool allowed) override; bool IsTaskExecutionAllowed() const override; MessagePump* GetBoundMessagePump() const override; -#if BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID) +#if BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID) || defined(STARBOARD) void AttachToMessagePump() override; #endif #if BUILDFLAG(IS_IOS) diff --git a/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc b/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc index 7e027675a960..e2eb0fd38864 100644 --- a/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc +++ b/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc @@ -731,7 +731,7 @@ void ThreadControllerWithMessagePumpImpl::AttachToMessagePump() { void ThreadControllerWithMessagePumpImpl::DetachFromMessagePump() { static_cast(pump_.get())->Detach(); } -#elif BUILDFLAG(IS_ANDROID) +#elif BUILDFLAG(IS_ANDROID) || defined(STARBOARD) void ThreadControllerWithMessagePumpImpl::AttachToMessagePump() { CHECK(main_thread_only().work_batch_size == 1); // Aborting the message pump currently relies on the batch size being 1. diff --git a/base/task/sequence_manager/thread_controller_with_message_pump_impl.h b/base/task/sequence_manager/thread_controller_with_message_pump_impl.h index 5f5b42fbb487..e3c6c97863c3 100644 --- a/base/task/sequence_manager/thread_controller_with_message_pump_impl.h +++ b/base/task/sequence_manager/thread_controller_with_message_pump_impl.h @@ -74,7 +74,7 @@ class BASE_EXPORT ThreadControllerWithMessagePumpImpl bool IsTaskExecutionAllowed() const override; MessagePump* GetBoundMessagePump() const override; void PrioritizeYieldingToNative(base::TimeTicks prioritize_until) override; -#if BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID) +#if BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID) || defined(STARBOARD) void AttachToMessagePump() override; #endif #if BUILDFLAG(IS_IOS) diff --git a/base/task/sequenced_task_runner.cc b/base/task/sequenced_task_runner.cc index 67cf5d1cfbec..1e9d1e4725de 100644 --- a/base/task/sequenced_task_runner.cc +++ b/base/task/sequenced_task_runner.cc @@ -11,12 +11,39 @@ #include "base/time/time.h" #include "third_party/abseil-cpp/absl/base/attributes.h" +#if defined(STARBOARD) +#include + +#include "base/check_op.h" +#include "starboard/thread.h" +#endif + namespace base { namespace { +#if defined(STARBOARD) +ABSL_CONST_INIT pthread_once_t s_once_flag = PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_local_key = 0; + +void InitThreadLocalKey() { + int res = pthread_key_create(&s_thread_local_key, NULL); + DCHECK(res == 0); +} + +void EnsureThreadLocalKeyInited() { + pthread_once(&s_once_flag, InitThreadLocalKey); +} + +SequencedTaskRunner::CurrentDefaultHandle* GetCurrentDefaultHandle() { + EnsureThreadLocalKeyInited(); + return static_cast( + pthread_getspecific(s_thread_local_key)); +} +#else ABSL_CONST_INIT thread_local SequencedTaskRunner::CurrentDefaultHandle* current_default_handle = nullptr; +#endif } // namespace @@ -77,9 +104,21 @@ bool SequencedTaskRunner::PostDelayedTaskAt( : delayed_run_time - TimeTicks::Now()); } +#if defined(STARBOARD) +// static +scoped_refptr* + SequencedTaskRunner::null_sequenced_task_runner_( + new scoped_refptr); +#endif + // static const scoped_refptr& SequencedTaskRunner::GetCurrentDefault() { +#if defined(STARBOARD) + auto current_default_handle = GetCurrentDefaultHandle(); + return (!current_default_handle ? *null_sequenced_task_runner_ + : current_default_handle->task_runner_); +#endif CHECK(current_default_handle) << "Error: This caller requires a sequenced context (i.e. the current " "task needs to run from a SequencedTaskRunner). If you're in a test " @@ -89,18 +128,33 @@ SequencedTaskRunner::GetCurrentDefault() { // static bool SequencedTaskRunner::HasCurrentDefault() { +#if defined(STARBOARD) + auto current_default_handle = GetCurrentDefaultHandle(); +#endif return !!current_default_handle; } SequencedTaskRunner::CurrentDefaultHandle::CurrentDefaultHandle( scoped_refptr task_runner) +#if defined(STARBOARD) + : +#else : resetter_(¤t_default_handle, this, nullptr), +#endif task_runner_(std::move(task_runner)) { +#if defined(STARBOARD) + EnsureThreadLocalKeyInited(); + pthread_setspecific(s_thread_local_key, this); +#endif DCHECK(task_runner_->RunsTasksInCurrentSequence()); } SequencedTaskRunner::CurrentDefaultHandle::~CurrentDefaultHandle() { DCHECK(task_runner_->RunsTasksInCurrentSequence()); +#if defined(STARBOARD) + auto current_default_handle = GetCurrentDefaultHandle(); + pthread_setspecific(s_thread_local_key, nullptr); +#endif DCHECK_EQ(current_default_handle, this); } diff --git a/base/task/sequenced_task_runner.h b/base/task/sequenced_task_runner.h index 03529c3cd396..6563505420d5 100644 --- a/base/task/sequenced_task_runner.h +++ b/base/task/sequenced_task_runner.h @@ -315,7 +315,9 @@ class BASE_EXPORT SequencedTaskRunner : public TaskRunner { friend class SequencedTaskRunner; friend class CurrentHandleOverride; +#if !defined(STARBOARD) const AutoReset resetter_; +#endif scoped_refptr task_runner_; }; @@ -334,6 +336,10 @@ class BASE_EXPORT SequencedTaskRunner : public TaskRunner { virtual bool DeleteOrReleaseSoonInternal(const Location& from_here, void (*deleter)(const void*), const void* object); + +#if defined(STARBOARD) + static scoped_refptr* null_sequenced_task_runner_; +#endif }; // Sample usage with std::unique_ptr : diff --git a/base/task/single_thread_task_executor_unittest.cc b/base/task/single_thread_task_executor_unittest.cc index 34840cb34c0c..703733379912 100644 --- a/base/task/single_thread_task_executor_unittest.cc +++ b/base/task/single_thread_task_executor_unittest.cc @@ -1257,7 +1257,7 @@ TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitOrderAfter) { // On Linux, the pipe buffer size is 64KiB by default. The bug caused one byte // accumulated in the pipe per two posts, so we should repeat 128K times to // reproduce the bug. -#if BUILDFLAG(IS_CHROMEOS) +#if BUILDFLAG(IS_CHROMEOS) || defined(STARBOARD) // TODO(crbug.com/1188497): This test is unreasonably slow on CrOS and flakily // times out (100x slower than other platforms which take < 1s to complete // it). @@ -1398,12 +1398,21 @@ TEST_P(SingleThreadTaskExecutorTypedTest, IsIdleForTestingNonNestableTask) { EXPECT_TRUE(CurrentThread::Get()->IsIdleForTesting()); } +#if defined(STARBOARD) +// MessagePumpUIStarboard lives on top of an external message pump +// and behaves differently than normal message pumps. +INSTANTIATE_TEST_SUITE_P(All, + SingleThreadTaskExecutorTypedTest, + ::testing::Values(MessagePumpType::DEFAULT), + SingleThreadTaskExecutorTypedTest::ParamInfoToString); +#else INSTANTIATE_TEST_SUITE_P(All, SingleThreadTaskExecutorTypedTest, ::testing::Values(MessagePumpType::DEFAULT, MessagePumpType::UI, MessagePumpType::IO), SingleThreadTaskExecutorTypedTest::ParamInfoToString); +#endif #if BUILDFLAG(IS_WIN) diff --git a/base/task/single_thread_task_runner.cc b/base/task/single_thread_task_runner.cc index f11574e5161f..b5f962a05c0f 100644 --- a/base/task/single_thread_task_runner.cc +++ b/base/task/single_thread_task_runner.cc @@ -16,16 +16,42 @@ #include "base/run_loop.h" #include "third_party/abseil-cpp/absl/base/attributes.h" +#if defined(STARBOARD) +#include + +#include "base/check_op.h" +#include "starboard/thread.h" +#endif + namespace base { namespace { +#if defined(STARBOARD) +ABSL_CONST_INIT pthread_once_t s_once_flag = PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_local_key = 0; + +void InitThreadLocalKey() { + int res = pthread_key_create(&s_thread_local_key , NULL); + DCHECK(res == 0); +} + +void EnsureThreadLocalKeyInited() { + pthread_once(&s_once_flag, InitThreadLocalKey); +} +#else ABSL_CONST_INIT thread_local SingleThreadTaskRunner::CurrentDefaultHandle* current_default_handle = nullptr; +#endif // This function can be removed, and the calls below replaced with direct // variable accesses, once the MSAN workaround is not necessary. SingleThreadTaskRunner::CurrentDefaultHandle* GetCurrentDefaultHandle() { +#if defined(STARBOARD) + EnsureThreadLocalKeyInited(); + return static_cast( + pthread_getspecific(s_thread_local_key)); +#else // Workaround false-positive MSAN use-of-uninitialized-value on // thread_local storage for loaded libraries: // https://github.com/google/sanitizers/issues/1265 @@ -33,6 +59,7 @@ SingleThreadTaskRunner::CurrentDefaultHandle* GetCurrentDefaultHandle() { sizeof(SingleThreadTaskRunner::CurrentDefaultHandle*)); return current_default_handle; +#endif } } // namespace @@ -61,15 +88,26 @@ bool SingleThreadTaskRunner::HasCurrentDefault() { SingleThreadTaskRunner::CurrentDefaultHandle::CurrentDefaultHandle( scoped_refptr task_runner) +#if defined(STARBOARD) + : +#else : resetter_(¤t_default_handle, this, nullptr), +#endif task_runner_(std::move(task_runner)), sequenced_task_runner_current_default_(task_runner_) { +#if defined(STARBOARD) + EnsureThreadLocalKeyInited(); + pthread_setspecific(s_thread_local_key, this); +#endif DCHECK(task_runner_->BelongsToCurrentThread()); } SingleThreadTaskRunner::CurrentDefaultHandle::~CurrentDefaultHandle() { DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK_EQ(GetCurrentDefaultHandle(), this); +#if defined(STARBOARD) + pthread_setspecific(s_thread_local_key, nullptr); +#endif } SingleThreadTaskRunner::CurrentHandleOverride::CurrentHandleOverride( diff --git a/base/task/single_thread_task_runner.h b/base/task/single_thread_task_runner.h index b4409c907b60..1f36225a8bed 100644 --- a/base/task/single_thread_task_runner.h +++ b/base/task/single_thread_task_runner.h @@ -78,7 +78,9 @@ class BASE_EXPORT SingleThreadTaskRunner : public SequencedTaskRunner { friend class SingleThreadTaskRunner; friend class CurrentHandleOverride; +#if !defined(STARBOARD) const AutoReset resetter_; +#endif scoped_refptr task_runner_; diff --git a/base/task/thread_pool/environment_config_unittest.cc b/base/task/thread_pool/environment_config_unittest.cc index 3a726d07e88b..c3398cba71c4 100644 --- a/base/task/thread_pool/environment_config_unittest.cc +++ b/base/task/thread_pool/environment_config_unittest.cc @@ -15,7 +15,7 @@ TEST(ThreadPoolEnvironmentConfig, CanUseBackgroundPriorityForWorker) { #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) EXPECT_TRUE(CanUseBackgroundThreadTypeForWorkerThread()); #elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA) || \ - BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_NACL) + BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_NACL) || defined(STARBOARD) EXPECT_FALSE(CanUseBackgroundThreadTypeForWorkerThread()); #else #error Platform doesn't match any block @@ -24,7 +24,7 @@ TEST(ThreadPoolEnvironmentConfig, CanUseBackgroundPriorityForWorker) { #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_ANDROID) EXPECT_TRUE(CanUseUtilityThreadTypeForWorkerThread()); #elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_FUCHSIA) || \ - BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_NACL) + BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_NACL) || defined(STARBOARD) EXPECT_FALSE(CanUseUtilityThreadTypeForWorkerThread()); #else #error Platform doesn't match any block diff --git a/base/task/thread_pool/task_tracker.cc b/base/task/thread_pool/task_tracker.cc index 94db917305a5..de4b05989722 100644 --- a/base/task/thread_pool/task_tracker.cc +++ b/base/task/thread_pool/task_tracker.cc @@ -34,6 +34,13 @@ #include "third_party/abseil-cpp/absl/base/attributes.h" #include "third_party/abseil-cpp/absl/types/optional.h" +#if defined(STARBOARD) +#include + +#include "base/check_op.h" +#include "starboard/thread.h" +#endif + namespace base { namespace internal { @@ -120,7 +127,27 @@ auto EmitThreadPoolTraceEventMetadata(perfetto::EventContext& ctx, #endif // BUILDFLAG(ENABLE_BASE_TRACING) } +#if defined(STARBOARD) +ABSL_CONST_INIT pthread_once_t s_once_flag = PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_local_key = 0; + +void InitThreadLocalKey() { + int res = pthread_key_create(&s_thread_local_key , NULL); + DCHECK(res == 0); +} + +void EnsureThreadLocalKeyInited() { + pthread_once(&s_once_flag, InitThreadLocalKey); +} + +bool GetFizzleBlockShutdownTasks() { + EnsureThreadLocalKeyInited(); + void* fizzle_block_shutdown_tasks = pthread_getspecific(s_thread_local_key); + return !!fizzle_block_shutdown_tasks ? reinterpret_cast(fizzle_block_shutdown_tasks) != 0 : false; +} +#else ABSL_CONST_INIT thread_local bool fizzle_block_shutdown_tasks = false; +#endif } // namespace @@ -313,7 +340,11 @@ bool TaskTracker::WillPostTask(Task* task, // A non BLOCK_SHUTDOWN task is allowed to be posted iff shutdown hasn't // started and the task is not delayed. if (shutdown_behavior != TaskShutdownBehavior::BLOCK_SHUTDOWN || +#if defined(STARBOARD) + !task->delayed_run_time.is_null() || GetFizzleBlockShutdownTasks()) { +#else !task->delayed_run_time.is_null() || fizzle_block_shutdown_tasks) { +#endif return false; } @@ -418,11 +449,21 @@ bool TaskTracker::IsShutdownComplete() const { } void TaskTracker::BeginFizzlingBlockShutdownTasks() { +#if defined(STARBOARD) + EnsureThreadLocalKeyInited(); + pthread_setspecific(s_thread_local_key, reinterpret_cast(static_cast(true))); +#else fizzle_block_shutdown_tasks = true; +#endif } void TaskTracker::EndFizzlingBlockShutdownTasks() { +#if defined(STARBOARD) + EnsureThreadLocalKeyInited(); + pthread_setspecific(s_thread_local_key, reinterpret_cast(static_cast(false))); +#else fizzle_block_shutdown_tasks = false; +#endif } void TaskTracker::RunTask(Task task, diff --git a/base/task/thread_pool/test_utils.cc b/base/task/thread_pool/test_utils.cc index 880b419fcafd..e163e750f28b 100644 --- a/base/task/thread_pool/test_utils.cc +++ b/base/task/thread_pool/test_utils.cc @@ -6,8 +6,10 @@ #include +#include "base/check.h" #include "base/debug/leak_annotations.h" #include "base/functional/bind.h" +#include "base/functional/overloaded.h" #include "base/memory/raw_ptr.h" #include "base/synchronization/condition_variable.h" #include "base/task/thread_pool/pooled_parallel_task_runner.h" @@ -263,25 +265,64 @@ MockJobTask::~MockJobTask() = default; MockJobTask::MockJobTask( base::RepeatingCallback worker_task, size_t num_tasks_to_run) +/* Cobalt : worker_task_(std::move(worker_task)), remaining_num_tasks_to_run_(num_tasks_to_run) {} +Cobalt */ + : task_(std::move(worker_task)), + remaining_num_tasks_to_run_(num_tasks_to_run) { + CHECK(!absl::get(task_).is_null()); +} MockJobTask::MockJobTask(base::OnceClosure worker_task) +/* Cobalt : worker_task_(base::BindRepeating( [](base::OnceClosure&& worker_task, JobDelegate*) mutable { std::move(worker_task).Run(); }, base::Passed(std::move(worker_task)))), remaining_num_tasks_to_run_(1) {} +Cobalt */ + : task_(std::move(worker_task)), remaining_num_tasks_to_run_(1) { + CHECK(!absl::get(task_).is_null()); +} + +void MockJobTask::SetNumTasksToRun(size_t num_tasks_to_run) { + if (num_tasks_to_run == 0) { + remaining_num_tasks_to_run_ = 0; + return; + } + if (auto* closure = absl::get_if(&task_); closure) { + // 0 is already handled above, so this can only be an attempt to set to + // a non-zero value for a OnceClosure. In that case, the only permissible + // value is 1, and the closure must not be null. + // + // Note that there is no need to check `!is_null()` for repeating callbacks, + // since `Run(JobDelegate*)` never consumes the repeating callback variant. + CHECK(!closure->is_null()); + CHECK_EQ(1u, num_tasks_to_run); + } + remaining_num_tasks_to_run_ = num_tasks_to_run; +} size_t MockJobTask::GetMaxConcurrency(size_t /* worker_count */) const { return remaining_num_tasks_to_run_.load(); } void MockJobTask::Run(JobDelegate* delegate) { +/* Cobalt worker_task_.Run(delegate); size_t before = remaining_num_tasks_to_run_.fetch_sub(1); DCHECK_GT(before, 0U); +Cobalt */ + absl::visit( + base::Overloaded{ + [](OnceClosure& closure) { std::move(closure).Run(); }, + [delegate](const RepeatingCallback& callback) { + callback.Run(delegate); + }}, + task_); + CHECK_GT(remaining_num_tasks_to_run_.fetch_sub(1), 0u); } scoped_refptr MockJobTask::GetJobTaskSource( diff --git a/base/task/thread_pool/test_utils.h b/base/task/thread_pool/test_utils.h index 1db33ffe808b..7437835ba317 100644 --- a/base/task/thread_pool/test_utils.h +++ b/base/task/thread_pool/test_utils.h @@ -24,6 +24,7 @@ #include "base/thread_annotations.h" #include "build/build_config.h" #include "testing/gmock/include/gmock/gmock.h" +#include "third_party/abseil-cpp/absl/types/variant.h" namespace base { namespace internal { @@ -97,9 +98,12 @@ class MockJobTask : public base::RefCountedThreadSafe { // Updates the remaining number of time |worker_task| runs to // |num_tasks_to_run|. +/* Cobalt void SetNumTasksToRun(size_t num_tasks_to_run) { remaining_num_tasks_to_run_ = num_tasks_to_run; } +Cobalt */ + void SetNumTasksToRun(size_t num_tasks_to_run); size_t GetMaxConcurrency(size_t worker_count) const; void Run(JobDelegate* delegate); @@ -114,7 +118,10 @@ class MockJobTask : public base::RefCountedThreadSafe { ~MockJobTask(); +/* Cobalt base::RepeatingCallback worker_task_; +Cobalt */ + absl::variant> task_; std::atomic_size_t remaining_num_tasks_to_run_; }; diff --git a/base/task/thread_pool/thread_group.cc b/base/task/thread_pool/thread_group.cc index f5aca2f952d5..92d8c7739028 100644 --- a/base/task/thread_pool/thread_group.cc +++ b/base/task/thread_pool/thread_group.cc @@ -19,13 +19,40 @@ #include "base/win/scoped_winrt_initializer.h" #endif +#if defined(STARBOARD) +#include + +#include "base/check_op.h" +#include "starboard/thread.h" +#endif + namespace base { namespace internal { namespace { +#if defined(STARBOARD) +ABSL_CONST_INIT pthread_once_t s_once_flag = PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_local_key = 0; + +void InitThreadLocalKey() { + int res = pthread_key_create(&s_thread_local_key , NULL); + DCHECK(res == 0); +} + +void EnsureThreadLocalKeyInited() { + pthread_once(&s_once_flag, InitThreadLocalKey); +} + +const ThreadGroup* GetCurrentThreadGroup() { + EnsureThreadLocalKeyInited(); + return static_cast( + pthread_getspecific(s_thread_local_key)); +} +#else // ThreadGroup that owns the current thread, if any. ABSL_CONST_INIT thread_local const ThreadGroup* current_thread_group = nullptr; +#endif } // namespace @@ -77,16 +104,30 @@ ThreadGroup::~ThreadGroup() = default; void ThreadGroup::BindToCurrentThread() { DCHECK(!CurrentThreadHasGroup()); +#if defined(STARBOARD) + EnsureThreadLocalKeyInited(); + pthread_setspecific(s_thread_local_key, this); +#else current_thread_group = this; +#endif } void ThreadGroup::UnbindFromCurrentThread() { DCHECK(IsBoundToCurrentThread()); +#if defined(STARBOARD) + EnsureThreadLocalKeyInited(); + pthread_setspecific(s_thread_local_key, nullptr); +#else current_thread_group = nullptr; +#endif } bool ThreadGroup::IsBoundToCurrentThread() const { +#if defined(STARBOARD) + return GetCurrentThreadGroup() == this; +#else return current_thread_group == this; +#endif } void ThreadGroup::Start() { @@ -340,7 +381,11 @@ ThreadGroup::GetScopedWindowsThreadEnvironment(WorkerEnvironment environment) { // static bool ThreadGroup::CurrentThreadHasGroup() { +#if defined(STARBOARD) + return GetCurrentThreadGroup() != nullptr; +#else return current_thread_group != nullptr; +#endif } } // namespace internal diff --git a/base/task/thread_pool/thread_group_impl.cc b/base/task/thread_pool/thread_group_impl.cc index 24f41898fcd5..7ca082ae6bb8 100644 --- a/base/task/thread_pool/thread_group_impl.cc +++ b/base/task/thread_pool/thread_group_impl.cc @@ -38,6 +38,7 @@ #include "base/time/time_override.h" #include "build/build_config.h" #include "third_party/abseil-cpp/absl/types/optional.h" +#include "starboard/configuration_constants.h" #if BUILDFLAG(IS_WIN) #include "base/win/scoped_com_initializer.h" @@ -51,7 +52,11 @@ namespace internal { namespace { +#ifdef STARBOARD +const size_t kMaxNumberOfWorkers = kSbMaxThreads; +#else constexpr size_t kMaxNumberOfWorkers = 256; +#endif // In a background thread group: // - Blocking calls take more time than in a foreground thread group. diff --git a/base/task/thread_pool/thread_group_impl_unittest.cc b/base/task/thread_pool/thread_group_impl_unittest.cc index 70a3d1850338..3e6e2cb185b6 100644 --- a/base/task/thread_pool/thread_group_impl_unittest.cc +++ b/base/task/thread_pool/thread_group_impl_unittest.cc @@ -1340,7 +1340,11 @@ INSTANTIATE_TEST_SUITE_P(ReclaimType, TEST_F(ThreadGroupImplBlockingTest, MaximumWorkersTest) { CreateAndStartThreadGroup(); +#ifdef STARBOARD + const size_t kMaxNumberOfWorkers = kSbMaxThreads; +#else constexpr size_t kMaxNumberOfWorkers = 256; +#endif constexpr size_t kNumExtraTasks = 10; TestWaitableEvent early_blocking_threads_running; @@ -1647,7 +1651,11 @@ INSTANTIATE_TEST_SUITE_P(WillBlock, // Verify that worker detachment doesn't race with worker cleanup, regression // test for https://crbug.com/810464. TEST_F(ThreadGroupImplImplStartInBodyTest, RacyCleanup) { +#ifdef STARBOARD + const size_t kLocalMaxTasks = kSbMaxThreads; +#else constexpr size_t kLocalMaxTasks = 256; +#endif // STARBOARD constexpr TimeDelta kReclaimTimeForRacyCleanupTest = Milliseconds(10); thread_group_->Start(kLocalMaxTasks, kLocalMaxTasks, diff --git a/base/task/thread_pool/thread_pool_impl_unittest.cc b/base/task/thread_pool/thread_pool_impl_unittest.cc index e0f712cf7375..c67c5642d526 100644 --- a/base/task/thread_pool/thread_pool_impl_unittest.cc +++ b/base/task/thread_pool/thread_pool_impl_unittest.cc @@ -1056,6 +1056,12 @@ void VerifyHasStringsOnStack(const std::string& pool_str, } // namespace +// Starboard does not support switching thread priority and therefore background +// scheduler worker that has TaskPriority::BEST_EFFORT can not be used. +// See CanUseBackgroundPriorityForSchedulerWorker() for more details. +// And Starboard can also reproduce the StackTrace().ToString() crash described +// down below on Linux. +#ifndef STARBOARD #if BUILDFLAG(IS_POSIX) // Many POSIX bots flakily crash on |debug::StackTrace().ToString()|, // https://crbug.com/840429. @@ -1151,6 +1157,7 @@ TEST_P(ThreadPoolImplTest, MAYBE_IdentifiableStacks) { thread_pool_->FlushForTesting(); } +#endif // STARBOARD TEST_P(ThreadPoolImplTest, WorkerThreadObserver) { auto owned_observer = diff --git a/base/task/thread_pool/thread_pool_instance.cc b/base/task/thread_pool/thread_pool_instance.cc index cd0b13db62b5..8694e10e73a9 100644 --- a/base/task/thread_pool/thread_pool_instance.cc +++ b/base/task/thread_pool/thread_pool_instance.cc @@ -14,6 +14,7 @@ #include "base/threading/platform_thread.h" #include "base/time/time.h" #include "build/build_config.h" +#include "starboard/configuration_constants.h" namespace base { @@ -97,8 +98,14 @@ void ThreadPoolInstance::StartWithDefaultParams() { // * The system is utilized maximally by foreground threads. // * The main thread is assumed to be busy, cap foreground workers at // |num_cores - 1|. +#if defined(STARBOARD) + const int kMaxNumberOfThreads = kSbMaxThreads; + const size_t max_num_foreground_threads = static_cast(std::min( + (std::max(3, SysInfo::NumberOfProcessors() - 1)), kMaxNumberOfThreads)); +#else const size_t max_num_foreground_threads = static_cast(std::max(3, SysInfo::NumberOfProcessors() - 1)); +#endif // defined(STARBOARD) Start({max_num_foreground_threads}); } #endif // !BUILDFLAG(IS_NACL) diff --git a/base/task/thread_pool/worker_thread.cc b/base/task/thread_pool/worker_thread.cc index 508d314a770f..6e4cf5365911 100644 --- a/base/task/thread_pool/worker_thread.cc +++ b/base/task/thread_pool/worker_thread.cc @@ -304,7 +304,8 @@ void WorkerThread::UpdateThreadType(ThreadType desired_thread_type) { } void WorkerThread::ThreadMain() { -#if (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL)) || BUILDFLAG(IS_FUCHSIA) +#if defined(STARBOARD) +#elif (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL)) || BUILDFLAG(IS_FUCHSIA) DCHECK(io_thread_task_runner_); FileDescriptorWatcher file_descriptor_watcher(io_thread_task_runner_); #endif diff --git a/base/task/thread_pool/worker_thread_unittest.cc b/base/task/thread_pool/worker_thread_unittest.cc index c5c2a5027f2c..d178e269dfc8 100644 --- a/base/task/thread_pool/worker_thread_unittest.cc +++ b/base/task/thread_pool/worker_thread_unittest.cc @@ -11,10 +11,12 @@ #include #include +#if !defined(COBALT_PENDING_CLEAN_UP) #include "base/allocator/partition_allocator/partition_alloc_buildflags.h" #include "base/allocator/partition_allocator/partition_alloc_config.h" #include "base/allocator/partition_allocator/shim/allocator_shim.h" #include "base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc.h" +#endif #include "base/compiler_specific.h" #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" diff --git a/base/test/BUILD.gn b/base/test/BUILD.gn index cc75b7235f43..9130082a9a5b 100644 --- a/base/test/BUILD.gn +++ b/base/test/BUILD.gn @@ -170,9 +170,40 @@ static_library("test_support") { "//third_party/libxml:xml_reader", ] + if (is_starboard) { + sources -= [ + "launcher/unit_test_launcher.h", + "test_shared_memory_util.cc", + "test_shared_memory_util.h", + ] + + sources += [ + "time_helpers.cc", + "time_helpers.h", + ] + sources -= [ + "metrics/histogram_enum_reader.cc", + "metrics/histogram_enum_reader.h", + ] + + # These rely on FILE/fopen/fclose. + sources -= [ + "gtest_links.cc", + "gtest_links.h", + "gtest_tags.cc", + "gtest_tags.h", + "gtest_xml_unittest_result_printer.cc", + "gtest_xml_unittest_result_printer.h", + "gtest_xml_util.cc", + "gtest_xml_util.h", + "perf_log.cc", + "perf_log.h", + ] + } + if (enable_base_tracing) { public_deps += [ "//third_party/perfetto:perfetto_test_support" ] - if (!is_chromeos) { + if (!is_chromeos && !is_starboard) { # TODO(rasikan): Add to ios and chromeos when unblocked by the chromiumos # change to add the shared lib to the chrome-binary-tests directory. public_deps += [ ":test_trace_processor" ] @@ -185,7 +216,7 @@ static_library("test_support") { } } - if (is_win) { + if (is_win && !is_starboard) { sources += [ "async_results_test_values_win.h", "fake_iasync_operation_win.h", @@ -208,12 +239,12 @@ static_library("test_support") { ] } - if (is_linux || is_chromeos) { + if (!use_cobalt_customizations && (is_linux || is_chromeos)) { sources += [ "test_file_util_linux.cc" ] public_deps += [ "//third_party/test_fonts/fontconfig:test_support" ] } - if (is_mac) { + if (is_mac && !is_starboard) { frameworks = [ "AppKit.framework" ] sources += [ "mock_chrome_application_mac.h", @@ -222,7 +253,7 @@ static_library("test_support") { ] } - if (is_android) { + if (is_android && !use_cobalt_customizations) { sources += [ "android/java_handler_thread_helpers.cc", "android/java_handler_thread_helpers.h", @@ -264,7 +295,7 @@ static_library("test_support") { } } - if (is_posix || is_fuchsia) { + if (!is_starboard && (is_posix || is_fuchsia)) { sources += [ "scoped_locale.cc", "scoped_locale.h", @@ -336,7 +367,9 @@ source_set("test_support_perf") { public_configs = [ ":perf_test_config" ] } -static_library("run_all_unittests") { +# TODO: b/315170518 - Revert to static library after fixing +# symbol visibility issues for windows based modular platform builds. +source_set("run_all_unittests") { testonly = true sources = [ "run_all_unittests.cc" ] deps = [ ":test_support" ] @@ -352,6 +385,7 @@ source_set("native_library_test_utils") { ] } +if (!use_cobalt_customizations) { # This shared library is dynamically loaded by ImmediateCrash unittests. shared_library("immediate_crash_test_helper") { sources = [ "immediate_crash_test_helper.cc" ] @@ -374,7 +408,9 @@ shared_library("immediate_crash_test_helper") { configs -= [ "//build/config/android:hide_all_but_jni_onload" ] } } +} +if (!use_cobalt_customizations) { # This shared library is dynamically loaded by NativeLibrary unittests. shared_library("test_shared_library") { testonly = true @@ -382,7 +418,9 @@ shared_library("test_shared_library") { deps = [ ":native_library_test_utils" ] } +} +if (!use_cobalt_customizations) { if (is_fuchsia || is_linux || is_chromeos) { shared_library("malloc_wrapper") { testonly = true @@ -390,8 +428,9 @@ if (is_fuchsia || is_linux || is_chromeos) { deps = [ "//base" ] } } +} -if (is_android) { +if (is_android && !use_cobalt_customizations) { generate_jni("base_unittests_jni_headers") { testonly = true sources = [ @@ -476,12 +515,14 @@ if (is_ios) { # Trivial executable which outputs space-delimited argv to stdout, # used for testing. +if (!use_cobalt_customizations) { executable("test_child_process") { testonly = true sources = [ "test_child_process.cc" ] } +} -if (enable_base_tracing) { +if (enable_base_tracing && !use_cobalt_customizations) { # We encapsulate the trace processor in a separate shared library to prevent # any duplicate symbol issues. Perfetto symbols are exported by chromium’s # base via a public_dep on libperfetto; libtrace_processor also depends on diff --git a/base/test/launcher/test_launcher.h b/base/test/launcher/test_launcher.h index 2598ac510d9d..a1fc3b2b3aa8 100644 --- a/base/test/launcher/test_launcher.h +++ b/base/test/launcher/test_launcher.h @@ -26,6 +26,8 @@ #include "base/timer/timer.h" #include "build/build_config.h" +#if !defined(STARBOARD) + namespace base { // Constants for GTest command-line flags. @@ -385,4 +387,5 @@ std::string TruncateSnippetFocused(const base::StringPiece snippet, } // namespace base +#endif // !defined(STARBOARD) #endif // BASE_TEST_LAUNCHER_TEST_LAUNCHER_H_ diff --git a/base/test/launcher/unit_test_launcher.h b/base/test/launcher/unit_test_launcher.h index 38f0eaf0b564..8ae0751a8d5d 100644 --- a/base/test/launcher/unit_test_launcher.h +++ b/base/test/launcher/unit_test_launcher.h @@ -17,6 +17,8 @@ #include "base/test/launcher/test_launcher.h" #include "build/build_config.h" +#if !defined(STARBOARD) + namespace base { // Callback that runs a test suite and returns exit code. @@ -189,4 +191,6 @@ class MergeTestFilterSwitchHandler : public DuplicateSwitchHandler { } // namespace base +#endif // !defined(STARBOARD) + #endif // BASE_TEST_LAUNCHER_UNIT_TEST_LAUNCHER_H_ diff --git a/base/test/memory/dangling_ptr_instrumentation.cc b/base/test/memory/dangling_ptr_instrumentation.cc index 0a4a789425e8..32f009967504 100644 --- a/base/test/memory/dangling_ptr_instrumentation.cc +++ b/base/test/memory/dangling_ptr_instrumentation.cc @@ -17,11 +17,13 @@ namespace base::test { // static base::expected DanglingPtrInstrumentation::Create() { +#if BUILDFLAG(USE_PARTITION_ALLOC) if (!FeatureList::IsEnabled(features::kPartitionAllocBackupRefPtr)) { return base::unexpected( "DanglingPtrInstrumentation requires the feature flag " "'PartitionAllocBackupRefPtr' to be on."); } +#endif // Note: We don't need to enable the `PartitionAllocDanglingPtr` feature, // because this does provide an alternative "implementation", by incrementing // the two counters. @@ -66,10 +68,12 @@ DanglingPtrInstrumentation& DanglingPtrInstrumentation::operator=( void DanglingPtrInstrumentation::Register() { CHECK_EQ(g_observer, nullptr); g_observer = this; +#if BUILDFLAG(USE_PARTITION_ALLOC) old_detected_fn_ = partition_alloc::GetDanglingRawPtrDetectedFn(); old_dereferenced_fn_ = partition_alloc::GetDanglingRawPtrReleasedFn(); partition_alloc::SetDanglingRawPtrDetectedFn(IncreaseCountDetected); partition_alloc::SetDanglingRawPtrReleasedFn(IncreaseCountReleased); +#endif } void DanglingPtrInstrumentation::Unregister() { @@ -77,8 +81,10 @@ void DanglingPtrInstrumentation::Unregister() { return; } g_observer = nullptr; +#if BUILDFLAG(USE_PARTITION_ALLOC) partition_alloc::SetDanglingRawPtrDetectedFn(old_detected_fn_); partition_alloc::SetDanglingRawPtrReleasedFn(old_dereferenced_fn_); +#endif } raw_ptr DanglingPtrInstrumentation::g_observer = diff --git a/base/test/native_library_test_utils.h b/base/test/native_library_test_utils.h index 1eb022210b49..b0548c9fbe50 100644 --- a/base/test/native_library_test_utils.h +++ b/base/test/native_library_test_utils.h @@ -7,7 +7,7 @@ #include "build/build_config.h" -#if BUILDFLAG(IS_WIN) +#if BUILDFLAG(IS_WIN) || defined(COMPILER_MSVC) #define NATIVE_LIBRARY_TEST_ALWAYS_EXPORT __declspec(dllexport) #else #define NATIVE_LIBRARY_TEST_ALWAYS_EXPORT __attribute__((visibility("default"))) diff --git a/base/test/perf_test_suite.cc b/base/test/perf_test_suite.cc index 5a6fd800f806..89b55e2c20fc 100644 --- a/base/test/perf_test_suite.cc +++ b/base/test/perf_test_suite.cc @@ -41,7 +41,9 @@ void PerfTestSuite::Initialize() { log_path = log_path.ReplaceExtension(FILE_PATH_LITERAL("log")); log_path = log_path.InsertBeforeExtension(FILE_PATH_LITERAL("_perf")); } +#if !defined(STARBOARD) ASSERT_TRUE(InitPerfLog(log_path)); +#endif // Raise to high priority to have more precise measurements. Since we don't // aim at 1% precision, it is not necessary to run at realtime level. @@ -51,7 +53,9 @@ void PerfTestSuite::Initialize() { void PerfTestSuite::Shutdown() { TestSuite::Shutdown(); +#if !defined(STARBOARD) FinalizePerfLog(); +#endif } } // namespace base diff --git a/base/test/perf_time_logger.cc b/base/test/perf_time_logger.cc index f27d2a33b37d..0a6478b1120d 100644 --- a/base/test/perf_time_logger.cc +++ b/base/test/perf_time_logger.cc @@ -20,7 +20,9 @@ void PerfTimeLogger::Done() { // we use a floating-point millisecond value because it is more // intuitive than microseconds and we want more precision than // integer milliseconds +#if !defined(STARBOARD) LogPerfResult(test_name_.c_str(), timer_.Elapsed().InMillisecondsF(), "ms"); +#endif logged_ = true; } diff --git a/base/test/power_monitor_test_utils.cc b/base/test/power_monitor_test_utils.cc index 37f07b995aeb..a94a8ef1f710 100644 --- a/base/test/power_monitor_test_utils.cc +++ b/base/test/power_monitor_test_utils.cc @@ -37,6 +37,18 @@ base::BatteryLevelProvider::BatteryState TestBatteryLevelProvider::CreateBatteryState(int battery_count, bool is_external_power_connected, int charge_percent) { +#if defined(STARBOARD) +// TODO: Remove once we use c++20 + return { + battery_count, + is_external_power_connected, + charge_percent, + 100, + absl::nullopt, + base::BatteryLevelProvider::BatteryLevelUnit::kRelative, + base::TimeTicks::Now(), + }; +#else return { .battery_count = battery_count, .is_external_power_connected = is_external_power_connected, @@ -46,5 +58,7 @@ TestBatteryLevelProvider::CreateBatteryState(int battery_count, .capture_time = base::TimeTicks::Now(), }; } +#endif +} } // namespace base::test diff --git a/base/test/run_all_unittests.cc b/base/test/run_all_unittests.cc index 4f0611aca9aa..c3aa04995d6a 100644 --- a/base/test/run_all_unittests.cc +++ b/base/test/run_all_unittests.cc @@ -9,6 +9,20 @@ #include "base/time/time.h" #include "build/build_config.h" + +#if defined(STARBOARD) +#include "base/test/allow_check_is_test_for_testing.h" +#include "starboard/client_porting/wrap_main/wrap_main.h" + + +int TestSuiteRun(int argc, char** argv) { + base::AtExitManager exit_manager; + base::test::AllowCheckIsTestForTesting(); + return base::TestSuite(argc, argv).Run(); +} + +STARBOARD_WRAP_SIMPLE_MAIN(TestSuiteRun); +#else #if BUILDFLAG(IS_WIN) #include "base/win/com_init_util.h" #endif // BUILDFLAG(IS_WIN) @@ -71,3 +85,4 @@ int main(int argc, char** argv) { argc, argv, base::BindOnce(&base::TestSuite::Run, base::Unretained(&test_suite))); } +#endif diff --git a/base/test/task_environment.cc b/base/test/task_environment.cc index 1b7694535dc4..2e437a312b33 100644 --- a/base/test/task_environment.cc +++ b/base/test/task_environment.cc @@ -48,7 +48,8 @@ #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" -#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#if defined(STARBOARD) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) #include "base/files/file_descriptor_watcher_posix.h" #include "third_party/abseil-cpp/absl/types/optional.h" #endif @@ -494,7 +495,8 @@ void TaskEnvironment::InitializeThreadPool() { void TaskEnvironment::CompleteInitialization() { DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_); -#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#if defined(STARBOARD) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) if (main_thread_type() == MainThreadType::IO) { file_descriptor_watcher_ = std::make_unique(GetMainThreadTaskRunner()); diff --git a/base/test/task_environment.h b/base/test/task_environment.h index 5bbf6f0a7f83..76575bdd8c4c 100644 --- a/base/test/task_environment.h +++ b/base/test/task_environment.h @@ -481,7 +481,8 @@ class TaskEnvironment { // Only set for instances using TimeSource::MOCK_TIME. std::unique_ptr mock_clock_; -#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#if defined(STARBOARD) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) // Enables the FileDescriptorWatcher API iff running a MainThreadType::IO. std::unique_ptr file_descriptor_watcher_; #endif diff --git a/base/test/task_environment_unittest.cc b/base/test/task_environment_unittest.cc index 57f5f83d6e5a..8136b3763ee2 100644 --- a/base/test/task_environment_unittest.cc +++ b/base/test/task_environment_unittest.cc @@ -367,6 +367,7 @@ TEST_F(TaskEnvironmentTest, MainThreadType) { EXPECT_FALSE(CurrentIOThread::IsSet()); } +#if !defined(STARBOARD) #if BUILDFLAG(IS_POSIX) TEST_F(TaskEnvironmentTest, SupportsFileDescriptorWatcherOnIOMainThread) { TaskEnvironment task_environment(TaskEnvironment::MainThreadType::IO); @@ -410,6 +411,7 @@ TEST_F(TaskEnvironmentTest, run_loop.Run(); } #endif // BUILDFLAG(IS_POSIX) +#endif // !defined(STARBOARD) TEST_F(TaskEnvironmentTest, MockTimeStartsWithWholeMilliseconds) { TaskEnvironment task_environment(TaskEnvironment::TimeSource::MOCK_TIME); diff --git a/base/test/test_shared_memory_util.cc b/base/test/test_shared_memory_util.cc index c6fb5d445eb3..01c6a386da2c 100644 --- a/base/test/test_shared_memory_util.cc +++ b/base/test/test_shared_memory_util.cc @@ -132,7 +132,7 @@ bool CheckReadOnlyPlatformSharedMemoryRegionForTesting( return CheckReadOnlySharedMemoryFuchsiaHandle(region.GetPlatformHandle()); #elif BUILDFLAG(IS_WIN) return CheckReadOnlySharedMemoryWindowsHandle(region.GetPlatformHandle()); -#elif BUILDFLAG(IS_ANDROID) +#elif BUILDFLAG(IS_ANDROID) || defined(STARBOARD) return CheckReadOnlySharedMemoryFdPosix(region.GetPlatformHandle()); #else return CheckReadOnlySharedMemoryFdPosix(region.GetPlatformHandle().fd); diff --git a/base/test/test_suite.cc b/base/test/test_suite.cc index e323e705327e..76958e35512b 100644 --- a/base/test/test_suite.cc +++ b/base/test/test_suite.cc @@ -4,7 +4,9 @@ #include "base/test/test_suite.h" +#ifndef STARBOARD #include +#endif #include @@ -53,6 +55,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "testing/multiprocess_func_list.h" +#if !defined(STARBOARD) #if BUILDFLAG(IS_APPLE) #include "base/mac/scoped_nsautorelease_pool.h" #endif // BUILDFLAG(IS_APPLE) @@ -85,6 +88,7 @@ #include "base/debug/handle_hooks_win.h" #endif // BUILDFLAG(IS_WIN) +#endif #if BUILDFLAG(USE_PARTITION_ALLOC) #include "base/allocator/partition_alloc_support.h" @@ -159,7 +163,9 @@ class FeatureListScopedToEachTest : public testing::EmptyTestEventListener { command_line->GetSwitchValueASCII(switches::kDisableFeatures); enabled += ",TestFeatureForBrowserTest1"; disabled += ",TestFeatureForBrowserTest2"; +#if !defined(STARBOARD) scoped_feature_list_.InitFromCommandLine(enabled, disabled); +#endif // The enable-features and disable-features flags were just slurped into a // FeatureList, so remove them from the command line. Tests should enable @@ -282,12 +288,20 @@ void InitializeLogging() { constexpr auto kLoggingDest = logging::LOG_TO_SYSTEM_DEBUG_LOG | logging::LOG_TO_STDERR; #endif +#if defined(STARBOARD) +// TODO: Remove once we use c++20 + CHECK(logging::InitLogging({kLoggingDest})); +#else CHECK(logging::InitLogging({.logging_dest = kLoggingDest})); +#endif // We want process and thread IDs because we may have multiple processes. #if BUILDFLAG(IS_ANDROID) // To view log output with IDs and timestamps use "adb logcat -v threadtime". logging::SetLogItems(false, false, false, false); +#elif defined(STARBOARD) + // Starboard applications are single process + logging::SetLogItems(false, true, false, false); #else // We want process and thread IDs because we may have multiple processes. logging::SetLogItems(true, true, false, false); @@ -296,11 +310,13 @@ void InitializeLogging() { } // namespace +#if !defined(STARBOARD) int RunUnitTestsUsingBaseTestSuite(int argc, char** argv) { TestSuite test_suite(argc, argv); return LaunchUnitTests(argc, argv, BindOnce(&TestSuite::Run, Unretained(&test_suite))); } +#endif TestSuite::TestSuite(int argc, char** argv) { PreInitialize(); @@ -381,7 +397,7 @@ void TestSuite::PreInitialize() { // On Android, AtExitManager is created in // testing/android/native_test_wrapper.cc before main() is called. -#if !BUILDFLAG(IS_ANDROID) +#if !BUILDFLAG(IS_ANDROID) && !defined(STARBOARD) at_exit_manager_ = std::make_unique(); #endif @@ -408,6 +424,7 @@ void TestSuite::AddTestLauncherResultPrinter() { return; } +#if !defined(STARBOARD) printer_ = new XmlUnitTestResultPrinter; CHECK(printer_->Initialize(output_path)) << "Output path is " << output_path.AsUTF8Unsafe() @@ -415,6 +432,7 @@ void TestSuite::AddTestLauncherResultPrinter() { testing::TestEventListeners& listeners = testing::UnitTest::GetInstance()->listeners(); listeners.Append(printer_); +#endif } // Don't add additional code to this method. Instead add it to @@ -503,6 +521,7 @@ void TestSuite::UnitTestAssertHandler(const char* file, } #endif // BUILDFLAG(IS_ANDROID) +#if !defined(STARBOARD) // XmlUnitTestResultPrinter inherits gtest format, where assert has summary // and message. In GTest, summary is just a logged text, and message is a // logged text, concatenated with stack trace of assert. @@ -512,10 +531,15 @@ void TestSuite::UnitTestAssertHandler(const char* file, const std::string stack_trace_str = summary_str + std::string(stack_trace); printer_->OnAssert(file, line, summary_str, stack_trace_str); } +#endif +#if defined(STARBOARD) + SbSystemRequestStop(1); +#else // The logging system actually prints the message before calling the assert // handler. Just exit now to avoid printing too many stack traces. _exit(1); +#endif } #if BUILDFLAG(IS_WIN) @@ -649,7 +673,8 @@ void TestSuite::Initialize() { // TODO(jshin): Should we set the locale via an OS X locale API here? i18n::SetICUDefaultLocale("en_US"); -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) +#if defined(STARBOARD) +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) test_fonts::SetUpFontconfig(); #endif diff --git a/base/test/time_helpers.cc b/base/test/time_helpers.cc new file mode 100644 index 000000000000..631c80b3aca5 --- /dev/null +++ b/base/test/time_helpers.cc @@ -0,0 +1,144 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "base/test/time_helpers.h" + +#include +#include +#include + +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "base/time/time.h" +#include "starboard/types.h" + +// Negative statuses are considered warnings, so this only fails if the status +// value is greater than zero. +#define UCHECK(status) DCHECK_GE(U_ZERO_ERROR, status) << __FUNCTION__ << ": " + +namespace base { +namespace test { +namespace time_helpers { + +namespace { +base::Time UDateToTime(UDate udate) { + return base::Time::UnixEpoch() + + Microseconds(static_cast( + udate * base::Time::kMicrosecondsPerMillisecond)); +} + +const char* ToMonthString(int month) { + switch (month) { + case 1: + return "Jan"; + case 2: + return "Feb"; + case 3: + return "Mar"; + case 4: + return "Apr"; + case 5: + return "May"; + case 6: + return "Jun"; + case 7: + return "Jul"; + case 8: + return "Aug"; + case 9: + return "Sep"; + case 10: + return "Oct"; + case 11: + return "Nov"; + case 12: + return "Dec"; + } + + return "UNK"; +} +} // namespace + +base::Time FieldsToTime(TimeZone timezone, + int year, + int month, + int date, + int hour, + int minute, + int second) { + UErrorCode status = U_ZERO_ERROR; + UChar timezone_buffer[32] = {0}; + UChar* timezone_uname = NULL; + switch (timezone) { + case kTimeZonePacific: + u_strFromUTF8(timezone_buffer, std::size(timezone_buffer), NULL, + "America/Los_Angeles", -1, &status); + timezone_uname = timezone_buffer; + break; + case kTimeZoneGMT: + u_strFromUTF8(timezone_buffer, std::size(timezone_buffer), NULL, + "Etc/GMT", -1, &status); + timezone_uname = timezone_buffer; + case kTimeZoneLocal: + // Leave NULL. + break; + } + + UCHECK(status); + UCalendar* calendar = + ucal_open(timezone_uname, -1, uloc_getDefault(), UCAL_DEFAULT, &status); + DCHECK(calendar); + UCHECK(status); + ucal_setMillis(calendar, 0, &status); // Clear the calendar. + UCHECK(status); + ucal_setDateTime(calendar, year, month - 1 + UCAL_JANUARY, date, hour, minute, + second, &status); + UCHECK(status); + UDate udate = ucal_getMillis(calendar, &status); + UCHECK(status); + ucal_close(calendar); + return UDateToTime(udate); +} + +base::Time TestDateToTime(TimeZone timezone) { + return FieldsToTime(timezone, + 2007, // year + 10, // month + 15, // date + 12, // hour + 45, // minute + 0); // second +} + +std::string TimeFormatUTC(base::Time time) { + Time::Exploded exploded; + time.UTCExplode(&exploded); + return base::StringPrintf("%s %02d %02d:%02d:%02d %04d", + ToMonthString(exploded.month), + exploded.day_of_month, exploded.hour, + exploded.minute, exploded.second, exploded.year); +} + +std::string TimeFormatLocal(base::Time time) { + Time::Exploded exploded; + time.LocalExplode(&exploded); + return base::StringPrintf("%s %02d %02d:%02d:%02d %04d", + ToMonthString(exploded.month), + exploded.day_of_month, exploded.hour, + exploded.minute, exploded.second, exploded.year); +} + +} // namespace time_helpers +} // namespace test +} // namespace base \ No newline at end of file diff --git a/base/test/time_helpers.h b/base/test/time_helpers.h new file mode 100644 index 000000000000..2598e955645d --- /dev/null +++ b/base/test/time_helpers.h @@ -0,0 +1,68 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// These functions use ICU to generate test values based on specific and local +// time zones. + +#ifndef BASE_TEST_TIME_HELPERS_H_ +#define BASE_TEST_TIME_HELPERS_H_ + +#include + +#include "base/time/time.h" + +namespace base { +namespace test { +namespace time_helpers { + +// Some TimeZone values that can be used in tests. +enum TimeZone { + kTimeZoneGMT, + kTimeZoneLocal, + kTimeZonePacific, +}; + +// Converts a set of fields in a timezone to a time. +// |timezone| is one of the enumerated timezones. +// |year| is a full 4-digit year +// |month| is a 1-based month (1 = January) +// |date| is a 1-based day-of-month (first day of month is 1) +// |hour| is the 0-based 24-hour clock hour-of-the-day +// (midnight is 0, noon is 12, 3pm is 15) +// |minute| is the 0-based clock minute-of-the-hour (0-59) +// |second| is the 0-based clock second-of-the-minute (0-59) +base::Time FieldsToTime(TimeZone timezone, + int year, + int month, + int date, + int hour, + int minute, + int second); + +// Defines a standard date to use in tests. +// Mon, Oct 15 12:45:00 PDT 2007 +base::Time TestDateToTime(TimeZone timezone); + +// Formats the given time into a UTC something parsable by +// base::Time::FromString(). +std::string TimeFormatUTC(base::Time time); + +// Formats the given time into a local something parsable by +// base::Time::FromString(). +std::string TimeFormatLocal(base::Time time); + +} // namespace time_helpers +} // namespace test +} // namespace base +#endif // BASE_TEST_TIME_HELPERS_H_ \ No newline at end of file diff --git a/base/third_party/double_conversion/BUILD.gn b/base/third_party/double_conversion/BUILD.gn index 8380f58d995b..70f3f1729582 100644 --- a/base/third_party/double_conversion/BUILD.gn +++ b/base/third_party/double_conversion/BUILD.gn @@ -5,10 +5,12 @@ config("config") { visibility = [ ":double_conversion" ] + if (!use_cobalt_customizations || !is_win) { cflags = [ "-Wno-unused-const-variable", "-Wno-unused-function", ] + } } static_library("double_conversion") { diff --git a/base/third_party/nspr/prtime.cc b/base/third_party/nspr/prtime.cc index 5d36b5b000e2..73ca1c9cb6b3 100644 --- a/base/third_party/nspr/prtime.cc +++ b/base/third_party/nspr/prtime.cc @@ -77,6 +77,10 @@ #include #include +#if defined(STARBOARD) +#include "starboard/client_porting/eztime/eztime.h" +#endif + /* * The COUNT_LEAPS macro counts the number of leap years passed by * till the start of the given year Y. At the start of the year 4 @@ -96,12 +100,47 @@ #define COUNT_DAYS(Y) (((Y)-1) * 365 + COUNT_LEAPS(Y)) #define DAYS_BETWEEN_YEARS(A, B) (COUNT_DAYS(B) - COUNT_DAYS(A)) +#if defined(STARBOARD) +static void sb_localtime_r(const time_t* secs, struct tm* time) { + if (secs == nullptr || time == nullptr) { + return; + } + const EzTimeT eztime_secs = static_cast(*secs); + EzTimeExploded eztime_result; + if (!EzTimeTExplode(&eztime_secs, EzTimeZone::kEzTimeZoneLocal, + &eztime_result)) { + return; + } + + time->tm_sec = eztime_result.tm_sec; + time->tm_min = eztime_result.tm_min; + time->tm_hour = eztime_result.tm_hour; + time->tm_mday = eztime_result.tm_mday; + time->tm_mon = eztime_result.tm_mon; + time->tm_year = eztime_result.tm_year; + time->tm_wday = eztime_result.tm_wday; + time->tm_yday = eztime_result.tm_yday; + time->tm_isdst = eztime_result.tm_isdst; +} + +time_t sb_mktime(struct tm *tm) { + if (tm == nullptr) { + return -1; + } + EzTimeExploded exploded = {tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, + tm->tm_wday, tm->tm_yday, tm->tm_isdst}; + EzTimeT secs = EzTimeTImplode(&exploded, EzTimeZone::kEzTimeZoneLocal); + return static_cast(secs); +} +#else /* Implements the Unix localtime_r() function for windows */ #if BUILDFLAG(IS_WIN) static void localtime_r(const time_t* secs, struct tm* time) { (void) localtime_s(time, secs); } #endif +#endif /* * Static variables used by functions in this file @@ -1138,7 +1177,9 @@ PR_ParseTimeString( and if you're wrong, it will "fix" it for you. */ localTime.tm_isdst = -1; -#if _MSC_VER == 1400 /* 1400 = Visual C++ 2005 (8.0) */ +#if defined(STARBOARD) + secs = sb_mktime(&localTime); +#elif _MSC_VER == 1400 /* 1400 = Visual C++ 2005 (8.0) */ /* * mktime will return (time_t) -1 if the input is a date * after 23:59:59, December 31, 3000, US Pacific Time (not @@ -1175,7 +1216,11 @@ PR_ParseTimeString( zone_offset for the date we are parsing is the same as the zone offset on 00:00:00 2 Jan 1970 GMT. */ secs = 86400; +#if defined(STARBOARD) + sb_localtime_r(&secs, &localTime); +#else localtime_r(&secs, &localTime); +#endif zone_offset = localTime.tm_min + 60 * localTime.tm_hour + 1440 * (localTime.tm_mday - 2); diff --git a/base/threading/hang_watcher.cc b/base/threading/hang_watcher.cc index f55bcc864856..db5d5611e9c1 100644 --- a/base/threading/hang_watcher.cc +++ b/base/threading/hang_watcher.cc @@ -32,6 +32,13 @@ #include "build/build_config.h" #include "third_party/abseil-cpp/absl/base/attributes.h" +#if defined(STARBOARD) +#include + +#include "base/check_op.h" +#include "starboard/thread.h" +#endif + namespace base { namespace { @@ -44,8 +51,30 @@ namespace { enum class LoggingLevel { kNone = 0, kUmaOnly = 1, kUmaAndCrash = 2 }; HangWatcher* g_instance = nullptr; + +#if defined(STARBOARD) +ABSL_CONST_INIT pthread_once_t s_once_flag = PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_local_key = 0; + +void InitThreadLocalKey() { + int res = pthread_key_create(&s_thread_local_key , NULL); + DCHECK(res == 0); +} + +void EnsureThreadLocalKeyInited() { + pthread_once(&s_once_flag, InitThreadLocalKey); +} + +internal::HangWatchState* GetHangWatchState() { + EnsureThreadLocalKeyInited(); + return static_cast( + pthread_getspecific(s_thread_local_key)); +} +#else ABSL_CONST_INIT thread_local internal::HangWatchState* hang_watch_state = nullptr; +#endif + std::atomic g_use_hang_watcher{false}; std::atomic g_hang_watcher_process_type{ HangWatcher::ProcessType::kBrowserProcess}; @@ -1165,7 +1194,13 @@ uint64_t HangWatchDeadline::SwitchBitsForTesting() { } HangWatchState::HangWatchState(HangWatcher::ThreadType thread_type) +#if defined(STARBOARD) + : thread_type_(thread_type) { + EnsureThreadLocalKeyInited(); + pthread_setspecific(s_thread_local_key, this); +#else : resetter_(&hang_watch_state, this, nullptr), thread_type_(thread_type) { +#endif // TODO(crbug.com/1223033): Remove this once macOS uses system-wide ids. // On macOS the thread ids used by CrashPad are not the same as the ones // provided by PlatformThread. Make sure to use the same for correct @@ -1189,6 +1224,11 @@ HangWatchState::~HangWatchState() { // WatchHangsInScopes. DCHECK(!current_watch_hangs_in_scope_); #endif + +#if defined(STARBOARD) + EnsureThreadLocalKeyInited(); + pthread_setspecific(s_thread_local_key, nullptr); +#endif } // static @@ -1267,12 +1307,16 @@ void HangWatchState::DecrementNestingLevel() { // static HangWatchState* HangWatchState::GetHangWatchStateForCurrentThread() { +#if defined(STARBOARD) + return GetHangWatchState(); +#else // Workaround false-positive MSAN use-of-uninitialized-value on // thread_local storage for loaded libraries: // https://github.com/google/sanitizers/issues/1265 MSAN_UNPOISON(&hang_watch_state, sizeof(internal::HangWatchState*)); return hang_watch_state; +#endif } PlatformThreadId HangWatchState::GetThreadID() const { diff --git a/base/threading/hang_watcher.h b/base/threading/hang_watcher.h index 4edf4284b9fe..c74804a5958e 100644 --- a/base/threading/hang_watcher.h +++ b/base/threading/hang_watcher.h @@ -649,7 +649,9 @@ class BASE_EXPORT HangWatchState { // the deadline. THREAD_CHECKER(thread_checker_); +#if !defined(STARBOARD) const AutoReset resetter_; +#endif // If the deadline fails to be updated before TimeTicks::Now() ever // reaches the value contained in it this constistutes a hang. diff --git a/base/threading/platform_thread.cc b/base/threading/platform_thread.cc index d96b2237df10..33abed1fd06f 100644 --- a/base/threading/platform_thread.cc +++ b/base/threading/platform_thread.cc @@ -11,12 +11,93 @@ #include "base/fuchsia/scheduler.h" #endif +#if defined(STARBOARD) +#include + +#include "base/check_op.h" +#endif + namespace base { namespace { +#if defined(STARBOARD) + +enum TlsValue { + kDefault = 0, + kBackground = 1, + kUtility = 2, + kResourceEfficient = 3 , + kCompositing = 4, + kDisplayCritical = 5, + kRealtimeAudio = 6, + kMaxValue = kRealtimeAudio, +}; + +TlsValue ThreadTypeToTlsValue(ThreadType type) { + if (type == ThreadType::kMaxValue) { + type = ThreadType::kRealtimeAudio; + } + switch(type) { + case ThreadType::kDefault: + return TlsValue::kDefault; + case ThreadType::kBackground: + return TlsValue::kBackground; + case ThreadType::kUtility: + return TlsValue::kUtility; + case ThreadType::kResourceEfficient: + return TlsValue::kResourceEfficient; + case ThreadType::kCompositing: + return TlsValue::kCompositing; + case ThreadType::kDisplayCritical: + return TlsValue::kDisplayCritical; + case ThreadType::kRealtimeAudio: + return TlsValue::kRealtimeAudio; + } +} + +ThreadType TlsValueToThreadType(TlsValue tls_value) { + if (tls_value == TlsValue::kMaxValue) { + tls_value = TlsValue::kRealtimeAudio; + } + switch(tls_value) { + case TlsValue::kDefault: + return ThreadType::kDefault; + case TlsValue::kBackground: + return ThreadType::kBackground; + case TlsValue::kUtility: + return ThreadType::kUtility; + case TlsValue::kResourceEfficient: + return ThreadType::kResourceEfficient; + case TlsValue::kCompositing: + return ThreadType::kCompositing; + case TlsValue::kDisplayCritical: + return ThreadType::kDisplayCritical; + case TlsValue::kRealtimeAudio: + return ThreadType::kRealtimeAudio; + } +} + +ABSL_CONST_INIT pthread_once_t s_once_flag = PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_local_key = 0; + +void InitThreadLocalKey() { + int res = pthread_key_create(&s_thread_local_key , NULL); + DCHECK(res == 0); +} + +void EnsureThreadLocalKeyInited() { + pthread_once(&s_once_flag, InitThreadLocalKey); +} + +ThreadType GetCurrentThreadTypeValue() { + EnsureThreadLocalKeyInited(); + return TlsValueToThreadType(TlsValue(reinterpret_cast(pthread_getspecific(s_thread_local_key)))); +} +#else ABSL_CONST_INIT thread_local ThreadType current_thread_type = ThreadType::kDefault; +#endif } // namespace @@ -36,7 +117,12 @@ void PlatformThread::SetCurrentThreadType(ThreadType thread_type) { // static ThreadType PlatformThread::GetCurrentThreadType() { +#if defined(STARBOARD) + ThreadType type = GetCurrentThreadTypeValue(); + return type; +#else return current_thread_type; +#endif } // static @@ -58,7 +144,12 @@ void SetCurrentThreadType(ThreadType thread_type, MessagePumpType pump_type_hint) { CHECK_LE(thread_type, ThreadType::kMaxValue); SetCurrentThreadTypeImpl(thread_type, pump_type_hint); +#if defined(STARBOARD) + EnsureThreadLocalKeyInited(); + pthread_setspecific(s_thread_local_key, reinterpret_cast(ThreadTypeToTlsValue(thread_type))); +#else current_thread_type = thread_type; +#endif } } // namespace internal diff --git a/base/threading/platform_thread.h b/base/threading/platform_thread.h index a7553e1a12f7..0623f595f790 100644 --- a/base/threading/platform_thread.h +++ b/base/threading/platform_thread.h @@ -22,7 +22,9 @@ #include "build/chromeos_buildflags.h" #include "third_party/abseil-cpp/absl/types/optional.h" -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +#include "starboard/thread.h" +#elif BUILDFLAG(IS_WIN) #include "base/win/windows_types.h" #elif BUILDFLAG(IS_FUCHSIA) #include @@ -36,7 +38,9 @@ namespace base { // Used for logging. Always an integer value. -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +typedef SbThreadId PlatformThreadId; +#elif BUILDFLAG(IS_WIN) typedef DWORD PlatformThreadId; #elif BUILDFLAG(IS_FUCHSIA) typedef zx_handle_t PlatformThreadId; @@ -50,7 +54,13 @@ static_assert(std::is_integral_v, "Always an integer value."); // Used to operate on threads. class PlatformThreadHandle { public: -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +#if SB_API_VERSION < 16 + typedef SbThread Handle; +#else + typedef pthread_t Handle; +#endif +#elif BUILDFLAG(IS_WIN) typedef void* Handle; #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) typedef pthread_t Handle; diff --git a/base/threading/platform_thread_ref.h b/base/threading/platform_thread_ref.h index 2b0e12e130c6..04142c433765 100644 --- a/base/threading/platform_thread_ref.h +++ b/base/threading/platform_thread_ref.h @@ -15,7 +15,10 @@ #include "base/base_export.h" #include "build/build_config.h" -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +#include +#include "starboard/thread.h" +#elif BUILDFLAG(IS_WIN) #include "base/win/windows_types.h" #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) #include @@ -33,7 +36,13 @@ namespace base { // to distinguish a new thread from an old, dead thread. class PlatformThreadRef { public: -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +#if SB_API_VERSION < 16 + typedef SbThread RefType; +#else + using RefType = pthread_t; +#endif +#elif BUILDFLAG(IS_WIN) using RefType = DWORD; #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) using RefType = pthread_t; diff --git a/base/threading/platform_thread_starboard.cc b/base/threading/platform_thread_starboard.cc new file mode 100644 index 000000000000..594ede6c08f1 --- /dev/null +++ b/base/threading/platform_thread_starboard.cc @@ -0,0 +1,262 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "base/threading/platform_thread.h" + +#include +#include +#include + +#include "base/logging.h" +#include "base/threading/thread_id_name_manager.h" +#include "base/threading/thread_restrictions.h" +#include "starboard/configuration_constants.h" +#include "starboard/thread.h" + +namespace base { + +namespace { + +struct ThreadParams { + PlatformThread::Delegate* delegate; + bool joinable; + SbThreadPriority thread_priority; + std::string thread_name; +}; + +void* ThreadFunc(void* params) { + ThreadParams* thread_params = static_cast(params); + PlatformThread::Delegate* delegate = thread_params->delegate; + +#if SB_API_VERSION >= 16 + if (kSbHasThreadPrioritySupport) { + SbThreadSetPriority(thread_params->thread_priority); + } +#endif // SB_API_VERSION >= 16 + pthread_setname_np(pthread_self(), thread_params->thread_name.c_str()); + + absl::optional disallow_singleton; + if (!thread_params->joinable) { + disallow_singleton.emplace(); + } + + delete thread_params; + + ThreadIdNameManager::GetInstance()->RegisterThread( + PlatformThread::CurrentHandle().platform_handle(), + PlatformThread::CurrentId()); + + delegate->ThreadMain(); + + ThreadIdNameManager::GetInstance()->RemoveName( + PlatformThread::CurrentHandle().platform_handle(), + PlatformThread::CurrentId()); + + return NULL; +} + +#if SB_API_VERSION >= 16 +bool CreateThread(size_t stack_size, + SbThreadPriority priority, + bool joinable, + const char* name, + PlatformThread::Delegate* delegate, + PlatformThreadHandle* thread_handle) { + ThreadParams* params = new ThreadParams; + params->delegate = delegate; + params->joinable = joinable; + params->thread_priority = priority; + if (name != nullptr) { + params->thread_name = name; + } + + pthread_attr_t attr; + if (pthread_attr_init(&attr) != 0) { + return false; + } + + pthread_attr_setstacksize(&attr, stack_size); + + if (!joinable) { + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + } + + pthread_t thread = 0; + pthread_create(&thread, &attr, ThreadFunc, params); + pthread_attr_destroy(&attr); + + if (thread != 0) { + if (thread_handle) { + *thread_handle = PlatformThreadHandle(thread); + } + + return true; + } + + return false; +} +#else +bool CreateThread(size_t stack_size, + SbThreadPriority priority, + bool joinable, + const char* name, + PlatformThread::Delegate* delegate, + PlatformThreadHandle* thread_handle) { + ThreadParams* params = new ThreadParams; + params->delegate = delegate; + params->joinable = joinable; + + SbThread thread = SbThreadCreate(stack_size, priority, kSbThreadNoAffinity, joinable, + name, ThreadFunc, params); + if (SbThreadIsValid(thread)) { + if (thread_handle) { + *thread_handle = PlatformThreadHandle(thread); + } + + return true; + } + + return false; +} +#endif // SB_API_VERSION >= 16 + +inline SbThreadPriority toSbPriority(ThreadType priority) { + switch (priority) { + case ThreadType::kBackground: + return kSbThreadPriorityLowest; + case ThreadType::kUtility: + return kSbThreadPriorityLow; + case ThreadType::kResourceEfficient: + return kSbThreadPriorityNormal; + case ThreadType::kDefault: + return kSbThreadNoPriority; + case ThreadType::kCompositing: + return kSbThreadPriorityHigh; + case ThreadType::kDisplayCritical: + return kSbThreadPriorityHighest; + case ThreadType::kRealtimeAudio: + return kSbThreadPriorityRealTime; + }; + NOTREACHED(); +} +} // namespace + +// static +PlatformThreadId PlatformThread::CurrentId() { + return SbThreadGetId(); +} + +// static +PlatformThreadRef PlatformThread::CurrentRef() { +#if SB_API_VERSION < 16 + return PlatformThreadRef(SbThreadGetCurrent()); +#else + return PlatformThreadRef(pthread_self()); +#endif // SB_API_VERSION < 16 + +} + +// static +PlatformThreadHandle PlatformThread::CurrentHandle() { +#if SB_API_VERSION < 16 + return PlatformThreadHandle(SbThreadGetCurrent()); +#else + return PlatformThreadHandle(pthread_self()); +#endif // SB_API_VERSION < 16 +} + +// static +void PlatformThread::YieldCurrentThread() { + sched_yield(); +} + +// static +void PlatformThread::Sleep(TimeDelta duration) { + usleep(duration.InMicroseconds()); +} + +// static +void PlatformThread::SetName(const std::string& name) { + ThreadIdNameManager::GetInstance()->SetName(name); + + std::string buffer(name, 0, kSbMaxThreadNameLength - 1); + pthread_setname_np(pthread_self(), buffer.c_str()); +} + +// static +const char* PlatformThread::GetName() { + return ThreadIdNameManager::GetInstance()->GetName(CurrentId()); +} + +// static +bool PlatformThread::CreateWithType(size_t stack_size, + Delegate* delegate, + PlatformThreadHandle* thread_handle, + ThreadType priority, + MessagePumpType /* pump_type_hint */) { + return CreateThread(stack_size, toSbPriority(priority), + true /* joinable thread */, NULL, delegate, + thread_handle); +} + +// static +bool PlatformThread::CreateNonJoinableWithType(size_t stack_size, + Delegate* delegate, + ThreadType priority, + MessagePumpType /* pump_type_hint */) { + return CreateThread(stack_size, toSbPriority(priority), + false /* joinable thread */, NULL, delegate, NULL); +} + +// static +void PlatformThread::Join(PlatformThreadHandle thread_handle) { + // Joining another thread may block the current thread for a long time, since + // the thread referred to by |thread_handle| may still be running long-lived / + // blocking tasks. + internal::AssertBlockingAllowed(); +#if SB_API_VERSION < 16 + SbThreadJoin(thread_handle.platform_handle(), NULL); +#else + pthread_join(thread_handle.platform_handle(), NULL); +#endif // SB_API_VERSION < 16 +} + +void PlatformThread::Detach(PlatformThreadHandle thread_handle) { +#if SB_API_VERSION < 16 + SbThreadDetach(thread_handle.platform_handle()); +#else + pthread_detach(thread_handle.platform_handle()); +#endif // SB_API_VERSION < 16 +} + +void internal::SetCurrentThreadTypeImpl(ThreadType /* thread_type */, MessagePumpType /*pump_type_hint*/) { + NOTIMPLEMENTED(); +} + +// static +bool PlatformThread::CanChangeThreadType(ThreadType /* from */, ThreadType /* to */) { + return false; +} + +size_t PlatformThread::GetDefaultThreadStackSize() { + return 0; +} + +// static +ThreadPriorityForTest PlatformThread::GetCurrentThreadPriorityForTest() { + NOTIMPLEMENTED(); + return ThreadPriorityForTest::kNormal; +} + +} // namespace base diff --git a/base/threading/platform_thread_unittest.cc b/base/threading/platform_thread_unittest.cc index 5e089296868a..e504bcf9a1ee 100644 --- a/base/threading/platform_thread_unittest.cc +++ b/base/threading/platform_thread_unittest.cc @@ -418,6 +418,7 @@ TEST(PlatformThreadTest, } #endif // BUILDFLAG(IS_WIN) +#if !defined(STARBOARD) // Ideally PlatformThread::CanChangeThreadType() would be true on all // platforms for all priorities. This not being the case. This test documents // and hardcodes what we know. Please inform scheduler-dev@chromium.org if this @@ -483,6 +484,7 @@ TEST(PlatformThreadTest, CanChangeThreadType) { ThreadType::kBackground)); #endif } +#endif // !defined(STARBOARD) TEST(PlatformThreadTest, SetCurrentThreadTypeTest) { TestPriorityResultingFromThreadType(ThreadType::kBackground, @@ -538,6 +540,9 @@ TEST(PlatformThreadTest, SetHugeThreadName) { PlatformThread::SetName(long_name); } +// TODO: b/327008491 - Not used by Cobalt, but should be tested to get +// closer to Chrome. +#if !defined(STARBOARD) TEST(PlatformThreadTest, GetDefaultThreadStackSize) { size_t stack_size = PlatformThread::GetDefaultThreadStackSize(); #if BUILDFLAG(IS_IOS) && BUILDFLAG(USE_BLINK) @@ -552,6 +557,7 @@ TEST(PlatformThreadTest, GetDefaultThreadStackSize) { EXPECT_LT(stack_size, 20u * (1 << 20)); #endif } +#endif #if BUILDFLAG(IS_APPLE) diff --git a/base/threading/post_task_and_reply_impl.cc b/base/threading/post_task_and_reply_impl.cc index fb72de394510..bf6f1c6de48d 100644 --- a/base/threading/post_task_and_reply_impl.cc +++ b/base/threading/post_task_and_reply_impl.cc @@ -141,6 +141,20 @@ bool PostTaskAndReplyImpl::PostTaskAndReply(const Location& from_here, DCHECK(task) << from_here.ToString(); DCHECK(reply) << from_here.ToString(); +#if defined(STARBOARD) + // This is a slight performance optimization for Starboard. + // With Starboard, HasCurrentDefault() and GetCurrentDefault() are quite + // expensive, and GetCurrentDefault() is safe to call and will return + // nullptr when needed. + const auto& current_context = SequencedTaskRunner::GetCurrentDefault(); + const bool has_sequenced_context = !!current_context; + const bool post_task_success = PostTask( + from_here, + BindOnce(&PostTaskAndReplyRelay::RunTaskAndPostReply, + PostTaskAndReplyRelay( + from_here, std::move(task), std::move(reply), + has_sequenced_context ? current_context : nullptr))); +#else const bool has_sequenced_context = SequencedTaskRunner::HasCurrentDefault(); const bool post_task_success = PostTask( @@ -150,6 +164,7 @@ bool PostTaskAndReplyImpl::PostTaskAndReply(const Location& from_here, has_sequenced_context ? SequencedTaskRunner::GetCurrentDefault() : nullptr))); +#endif // PostTaskAndReply() requires a SequencedTaskRunner::CurrentDefaultHandle to // post the reply. Having no SequencedTaskRunner::CurrentDefaultHandle is diff --git a/base/threading/scoped_blocking_call.cc b/base/threading/scoped_blocking_call.cc index 3fe45d86d844..e1c3679c3452 100644 --- a/base/threading/scoped_blocking_call.cc +++ b/base/threading/scoped_blocking_call.cc @@ -19,6 +19,12 @@ #if DCHECK_IS_ON() #include "base/auto_reset.h" #include "third_party/abseil-cpp/absl/base/attributes.h" +#if defined(STARBOARD) +#include + +#include "base/check_op.h" +#include "starboard/thread.h" +#endif #endif namespace base { @@ -26,11 +32,31 @@ namespace base { namespace { #if DCHECK_IS_ON() +#if defined(STARBOARD) +ABSL_CONST_INIT pthread_once_t s_once_flag = PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_local_key = 0; + +void InitThreadLocalKey() { + int res = pthread_key_create(&s_thread_local_key , NULL); + DCHECK(res == 0); +} + +void EnsureThreadLocalKeyInited() { + pthread_once(&s_once_flag, InitThreadLocalKey); +} + +bool GetConstructionInProgress() { + EnsureThreadLocalKeyInited(); + void* construction_in_progress = pthread_getspecific(s_thread_local_key); + return !!construction_in_progress ? reinterpret_cast(construction_in_progress) != 0 : false; +} +#else // Used to verify that the trace events used in the constructor do not result in // instantiating a ScopedBlockingCall themselves (which would cause an infinite // reentrancy loop). ABSL_CONST_INIT thread_local bool construction_in_progress = false; #endif +#endif } // namespace @@ -40,7 +66,12 @@ ScopedBlockingCall::ScopedBlockingCall(const Location& from_here, blocking_type, UncheckedScopedBlockingCall::BlockingCallType::kRegular) { #if DCHECK_IS_ON() +#if defined(STARBOARD) + EnsureThreadLocalKeyInited(); + pthread_setspecific(s_thread_local_key, reinterpret_cast(static_cast(true))); +#else const AutoReset resetter(&construction_in_progress, true, false); +#endif #endif internal::AssertBlockingAllowed(); @@ -49,6 +80,10 @@ ScopedBlockingCall::ScopedBlockingCall(const Location& from_here, ctx.event()->set_source_location_iid( base::trace_event::InternedSourceLocation::Get(&ctx, from_here)); }); + +#if DCHECK_IS_ON() && defined(STARBOARD) + pthread_setspecific(s_thread_local_key, reinterpret_cast(static_cast(false))); +#endif } ScopedBlockingCall::~ScopedBlockingCall() { @@ -64,7 +99,12 @@ ScopedBlockingCallWithBaseSyncPrimitives:: blocking_type, UncheckedScopedBlockingCall::BlockingCallType::kBaseSyncPrimitives) { #if DCHECK_IS_ON() +#if defined(STARBOARD) + EnsureThreadLocalKeyInited(); + pthread_setspecific(s_thread_local_key, reinterpret_cast(static_cast(true))); +#else const AutoReset resetter(&construction_in_progress, true, false); +#endif #endif internal::AssertBaseSyncPrimitivesAllowed(); @@ -76,6 +116,10 @@ ScopedBlockingCallWithBaseSyncPrimitives:: source_location_data->set_file_name(from_here.file_name()); source_location_data->set_function_name(from_here.function_name()); }); + +#if DCHECK_IS_ON() && defined(STARBOARD) + pthread_setspecific(s_thread_local_key, reinterpret_cast(static_cast(false))); +#endif } ScopedBlockingCallWithBaseSyncPrimitives:: diff --git a/base/threading/scoped_blocking_call_internal.cc b/base/threading/scoped_blocking_call_internal.cc index 695e74360d49..adaaf17394ff 100644 --- a/base/threading/scoped_blocking_call_internal.cc +++ b/base/threading/scoped_blocking_call_internal.cc @@ -22,28 +22,70 @@ #include "build/build_config.h" #include "third_party/abseil-cpp/absl/base/attributes.h" +#if defined(STARBOARD) +#include + +#include "starboard/thread.h" +#endif + namespace base { namespace internal { namespace { +#if defined(STARBOARD) +ABSL_CONST_INIT pthread_once_t s_once_observer_flag = PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_local_observer_key = 0; +ABSL_CONST_INIT pthread_once_t s_once_call_flag = PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_local_call_key = 0; + +void InitThreadLocalObserverKey() { + int res = pthread_key_create(&s_thread_local_observer_key , NULL); + DCHECK(res == 0); +} + +void InitThreadLocalCallKey() { + int res = pthread_key_create(&s_thread_local_call_key , NULL); + DCHECK(res == 0); +} + +void EnsureThreadLocalObserverKeyInited() { + pthread_once(&s_once_observer_flag, InitThreadLocalObserverKey); +} + +void EnsureThreadLocalCallKeyInited() { + pthread_once(&s_once_call_flag, InitThreadLocalCallKey); +} +#else ABSL_CONST_INIT thread_local BlockingObserver* blocking_observer = nullptr; // Last ScopedBlockingCall instantiated on this thread. ABSL_CONST_INIT thread_local UncheckedScopedBlockingCall* last_scoped_blocking_call = nullptr; +#endif // These functions can be removed, and the calls below replaced with direct // variable accesses, once the MSAN workaround is not necessary. BlockingObserver* GetBlockingObserver() { +#if defined(STARBOARD) + EnsureThreadLocalObserverKeyInited(); + return static_cast( + pthread_getspecific(s_thread_local_observer_key)); +#else // Workaround false-positive MSAN use-of-uninitialized-value on // thread_local storage for loaded libraries: // https://github.com/google/sanitizers/issues/1265 MSAN_UNPOISON(&blocking_observer, sizeof(BlockingObserver*)); return blocking_observer; +#endif } UncheckedScopedBlockingCall* GetLastScopedBlockingCall() { +#if defined(STARBOARD) + EnsureThreadLocalCallKeyInited(); + return static_cast( + pthread_getspecific(s_thread_local_call_key)); +#else // Workaround false-positive MSAN use-of-uninitialized-value on // thread_local storage for loaded libraries: // https://github.com/google/sanitizers/issues/1265 @@ -51,6 +93,7 @@ UncheckedScopedBlockingCall* GetLastScopedBlockingCall() { sizeof(UncheckedScopedBlockingCall*)); return last_scoped_blocking_call; +#endif } // Set to true by scoped_blocking_call_unittest to ensure unrelated threads @@ -67,11 +110,21 @@ bool IsBackgroundPriorityWorker() { void SetBlockingObserverForCurrentThread( BlockingObserver* new_blocking_observer) { DCHECK(!GetBlockingObserver()); +#if defined(STARBOARD) + EnsureThreadLocalObserverKeyInited(); + pthread_setspecific(s_thread_local_observer_key, new_blocking_observer); +#else blocking_observer = new_blocking_observer; +#endif } void ClearBlockingObserverForCurrentThread() { +#if defined(STARBOARD) + EnsureThreadLocalObserverKeyInited(); + pthread_setspecific(s_thread_local_observer_key, nullptr); +#else blocking_observer = nullptr; +#endif } IOJankMonitoringWindow::ScopedMonitoredCall::ScopedMonitoredCall() @@ -320,10 +373,18 @@ UncheckedScopedBlockingCall::UncheckedScopedBlockingCall( BlockingCallType blocking_call_type) : blocking_observer_(GetBlockingObserver()), previous_scoped_blocking_call_(GetLastScopedBlockingCall()), +#if !defined(STARBOARD) resetter_(&last_scoped_blocking_call, this), +#endif is_will_block_(blocking_type == BlockingType::WILL_BLOCK || (previous_scoped_blocking_call_ && previous_scoped_blocking_call_->is_will_block_)) { +#if defined(STARBOARD) + EnsureThreadLocalCallKeyInited(); + reset_to_ = pthread_getspecific(s_thread_local_call_key); + pthread_setspecific(s_thread_local_call_key, this); +#endif + // Only monitor non-nested ScopedBlockingCall(MAY_BLOCK) calls on foreground // threads. Cancels() any pending monitored call when a WILL_BLOCK or // ScopedBlockingCallWithBaseSyncPrimitives nests into a @@ -357,6 +418,11 @@ UncheckedScopedBlockingCall::~UncheckedScopedBlockingCall() { DCHECK_EQ(this, GetLastScopedBlockingCall()); if (blocking_observer_ && !previous_scoped_blocking_call_) blocking_observer_->BlockingEnded(); + +#if defined(STARBOARD) + EnsureThreadLocalCallKeyInited(); + pthread_setspecific(s_thread_local_call_key, reset_to_); +#endif } } // namespace internal diff --git a/base/threading/scoped_blocking_call_internal.h b/base/threading/scoped_blocking_call_internal.h index cf43581268ae..0d5f4181859f 100644 --- a/base/threading/scoped_blocking_call_internal.h +++ b/base/threading/scoped_blocking_call_internal.h @@ -191,7 +191,11 @@ class BASE_EXPORT [[maybe_unused, nodiscard]] UncheckedScopedBlockingCall { // Previous ScopedBlockingCall instantiated on this thread. const raw_ptr previous_scoped_blocking_call_; +#if defined(STARBOARD) + void* reset_to_; +#else const base::AutoReset resetter_; +#endif // Whether the BlockingType of the current thread was WILL_BLOCK after this // ScopedBlockingCall was instantiated. diff --git a/base/threading/sequence_local_storage_map.cc b/base/threading/sequence_local_storage_map.cc index 136f4768a25a..3dcb03dfac57 100644 --- a/base/threading/sequence_local_storage_map.cc +++ b/base/threading/sequence_local_storage_map.cc @@ -10,13 +10,40 @@ #include "base/check_op.h" #include "third_party/abseil-cpp/absl/base/attributes.h" +#if defined(STARBOARD) +#include + +#include "base/check_op.h" +#include "starboard/thread.h" +#endif + namespace base { namespace internal { namespace { +#if defined(STARBOARD) +ABSL_CONST_INIT pthread_once_t s_once_flag = PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_local_key = 0; + +void InitThreadLocalKey() { + int res = pthread_key_create(&s_thread_local_key , NULL); + DCHECK(res == 0); +} + +void EnsureThreadLocalKeyInited() { + pthread_once(&s_once_flag, InitThreadLocalKey); +} + +SequenceLocalStorageMap* GetCurrentSequenceLocalStorage() { + EnsureThreadLocalKeyInited(); + return static_cast( + pthread_getspecific(s_thread_local_key)); +} +#else ABSL_CONST_INIT thread_local SequenceLocalStorageMap* current_sequence_local_storage = nullptr; +#endif } // namespace @@ -32,12 +59,20 @@ SequenceLocalStorageMap& SequenceLocalStorageMap::GetForCurrentThread() { "ScopedSetSequenceLocalStorageMapForCurrentThread to store a " "SequenceLocalStorageMap object in TLS."; +#if defined(STARBOARD) + return *GetCurrentSequenceLocalStorage(); +#else return *current_sequence_local_storage; +#endif } // static bool SequenceLocalStorageMap::IsSetForCurrentThread() { +#if defined(STARBOARD) + return GetCurrentSequenceLocalStorage() != nullptr; +#else return current_sequence_local_storage != nullptr; +#endif } void* SequenceLocalStorageMap::Get(int slot_id) { @@ -97,12 +132,27 @@ SequenceLocalStorageMap::ValueDestructorPair::operator=( ScopedSetSequenceLocalStorageMapForCurrentThread:: ScopedSetSequenceLocalStorageMapForCurrentThread( SequenceLocalStorageMap* sequence_local_storage) +#if defined(STARBOARD) +{ + EnsureThreadLocalKeyInited(); + pthread_setspecific(s_thread_local_key, sequence_local_storage); +} +#else : resetter_(¤t_sequence_local_storage, sequence_local_storage, nullptr) {} +#endif +#if defined(STARBOARD) +ScopedSetSequenceLocalStorageMapForCurrentThread:: + ~ScopedSetSequenceLocalStorageMapForCurrentThread() { + EnsureThreadLocalKeyInited(); + pthread_setspecific(s_thread_local_key, nullptr); +} +#else ScopedSetSequenceLocalStorageMapForCurrentThread:: ~ScopedSetSequenceLocalStorageMapForCurrentThread() = default; +#endif } // namespace internal } // namespace base diff --git a/base/threading/sequence_local_storage_map.h b/base/threading/sequence_local_storage_map.h index ff225dbaf321..64e1bf04eeda 100644 --- a/base/threading/sequence_local_storage_map.h +++ b/base/threading/sequence_local_storage_map.h @@ -100,8 +100,10 @@ class BASE_EXPORT ~ScopedSetSequenceLocalStorageMapForCurrentThread(); +#if !defined(STARBOARD) private: const base::AutoReset resetter_; +#endif }; } // namespace internal } // namespace base diff --git a/base/threading/thread.cc b/base/threading/thread.cc index 2af7eae2c8d2..716a2a43fbb9 100644 --- a/base/threading/thread.cc +++ b/base/threading/thread.cc @@ -29,6 +29,12 @@ #include "build/build_config.h" #include "third_party/abseil-cpp/absl/base/attributes.h" +#if defined(STARBOARD) +#include + +#include "base/check_op.h" +#include "starboard/thread.h" +#else #if (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL)) || BUILDFLAG(IS_FUCHSIA) #include "base/files/file_descriptor_watcher_posix.h" #include "third_party/abseil-cpp/absl/types/optional.h" @@ -37,17 +43,38 @@ #if BUILDFLAG(IS_WIN) #include "base/win/scoped_com_initializer.h" #endif +#endif namespace base { #if DCHECK_IS_ON() namespace { +#if defined(STARBOARD) +ABSL_CONST_INIT pthread_once_t s_once_flag = PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_local_key = 0; + +void InitThreadLocalKey() { + int res = pthread_key_create(&s_thread_local_key , NULL); + DCHECK(res == 0); +} + +void EnsureThreadLocalKeyInited() { + pthread_once(&s_once_flag, InitThreadLocalKey); +} + +bool GetWasQuitProperly() { + EnsureThreadLocalKeyInited(); + void* was_quit_properly = pthread_getspecific(s_thread_local_key); + return !!was_quit_properly ? reinterpret_cast(was_quit_properly) != 0 : false; +} +#else // We use this thread-local variable to record whether or not a thread exited // because its Stop method was called. This allows us to catch cases where // MessageLoop::QuitWhenIdle() is called directly, which is unexpected when // using a Thread to setup and run a MessageLoop. ABSL_CONST_INIT thread_local bool was_quit_properly = false; +#endif } // namespace #endif @@ -345,14 +372,23 @@ void Thread::Run(RunLoop* run_loop) { // static void Thread::SetThreadWasQuitProperly(bool flag) { #if DCHECK_IS_ON() +#if defined(STARBOARD) + EnsureThreadLocalKeyInited(); + pthread_setspecific(s_thread_local_key, reinterpret_cast(static_cast(flag))); +#else was_quit_properly = flag; #endif +#endif } // static bool Thread::GetThreadWasQuitProperly() { #if DCHECK_IS_ON() +#if defined(STARBOARD) + return GetWasQuitProperly(); +#else return was_quit_properly; +#endif #else return true; #endif @@ -380,6 +416,7 @@ void Thread::ThreadMain() { delegate_->BindToCurrentThread(timer_slack_); DCHECK(CurrentThread::Get()); DCHECK(SingleThreadTaskRunner::HasCurrentDefault()); +#if !defined(STARBOARD) #if (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL)) || BUILDFLAG(IS_FUCHSIA) // Allow threads running a MessageLoopForIO to use FileDescriptorWatcher API. std::unique_ptr file_descriptor_watcher; @@ -397,6 +434,7 @@ void Thread::ThreadMain() { ? new win::ScopedCOMInitializer() : new win::ScopedCOMInitializer(win::ScopedCOMInitializer::kMTA)); } +#endif #endif // Let the thread do extra initialization. diff --git a/base/threading/thread_id_name_manager.cc b/base/threading/thread_id_name_manager.cc index 338dacb3cd1c..7701cf04507e 100644 --- a/base/threading/thread_id_name_manager.cc +++ b/base/threading/thread_id_name_manager.cc @@ -15,13 +15,41 @@ #include "base/trace_event/heap_profiler_allocation_context_tracker.h" // no-presubmit-check #include "third_party/abseil-cpp/absl/base/attributes.h" +#if defined(STARBOARD) +#include + +#include "base/check_op.h" +#include "starboard/thread.h" +#endif + namespace base { namespace { static const char kDefaultName[] = ""; static std::string* g_default_name; +#if defined(STARBOARD) +ABSL_CONST_INIT pthread_once_t s_once_flag = PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_local_key = 0; + +void InitThreadLocalKey() { + int res = pthread_key_create(&s_thread_local_key , NULL); + DCHECK(res == 0); +} + +void EnsureThreadLocalKeyInited() { + pthread_once(&s_once_flag, InitThreadLocalKey); +} + +const char* GetThreadName() { + EnsureThreadLocalKeyInited(); + const char* thread_name = static_cast( + pthread_getspecific(s_thread_local_key)); + return !!thread_name ? thread_name : kDefaultName; +} +#else ABSL_CONST_INIT thread_local const char* thread_name = kDefaultName; +#endif } ThreadIdNameManager::Observer::~Observer() = default; @@ -80,7 +108,12 @@ void ThreadIdNameManager::SetName(const std::string& name) { auto id_to_handle_iter = thread_id_to_handle_.find(id); +#if defined(STARBOARD) + EnsureThreadLocalKeyInited(); + pthread_setspecific(s_thread_local_key, const_cast(leaked_str->c_str())); +#else thread_name = leaked_str->c_str(); +#endif for (Observer* obs : observers_) obs->OnThreadNameChanged(leaked_str->c_str()); @@ -119,7 +152,11 @@ const char* ThreadIdNameManager::GetName(PlatformThreadId id) { } const char* ThreadIdNameManager::GetNameForCurrentThread() { +#if defined(STARBOARD) + return GetThreadName(); +#else return thread_name; +#endif } void ThreadIdNameManager::RemoveName(PlatformThreadHandle::Handle handle, diff --git a/base/threading/thread_local_storage.cc b/base/threading/thread_local_storage.cc index 457b9860961b..a0ae20dcc45a 100644 --- a/base/threading/thread_local_storage.cc +++ b/base/threading/thread_local_storage.cc @@ -6,6 +6,7 @@ #include #include +#include #include "base/check_op.h" #include "base/compiler_specific.h" @@ -417,7 +418,7 @@ void PlatformThreadLocalStorage::OnThreadExit() { return; OnThreadExitInternal(tls_vector); } -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) void PlatformThreadLocalStorage::OnThreadExit(void* value) { // On posix this function may be called twice. The first pass calls dtors and // sets state to kDestroyed. The second pass sets kDestroyed to diff --git a/base/threading/thread_local_storage.h b/base/threading/thread_local_storage.h index c33ef6b2ad02..dfe83837b12c 100644 --- a/base/threading/thread_local_storage.h +++ b/base/threading/thread_local_storage.h @@ -10,7 +10,9 @@ #include "base/base_export.h" #include "build/build_config.h" -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) +#include +#elif BUILDFLAG(IS_WIN) #include "base/win/windows_types.h" #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) #include @@ -39,10 +41,10 @@ class ThreadLocalStorageTestInternal; // * ThreadLocalStorage::StaticSlot/Slot for more direct control of the slot. class BASE_EXPORT PlatformThreadLocalStorage { public: -#if BUILDFLAG(IS_WIN) +#if BUILDFLAG(IS_WIN) && !defined(STARBOARD) typedef unsigned long TLSKey; enum : unsigned { TLS_KEY_OUT_OF_INDEXES = TLS_OUT_OF_INDEXES }; -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) typedef pthread_key_t TLSKey; // The following is a "reserved key" which is used in our generic Chromium // ThreadLocalStorage implementation. We expect that an OS will not return @@ -65,9 +67,10 @@ class BASE_EXPORT PlatformThreadLocalStorage { static void FreeTLS(TLSKey key); static void SetTLSValue(TLSKey key, void* value); static void* GetTLSValue(TLSKey key) { -#if BUILDFLAG(IS_WIN) + return pthread_getspecific(key); +#if BUILDFLAG(IS_WIN) && !defined(STARBOARD) return TlsGetValue(key); -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || defined(STARBOARD) return pthread_getspecific(key); #endif } @@ -80,7 +83,9 @@ class BASE_EXPORT PlatformThreadLocalStorage { // Destructors may end up being called multiple times on a terminating // thread, as other destructors may re-set slots that were previously // destroyed. -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) + static void OnThreadExit(void* value); +#elif BUILDFLAG(IS_WIN) // Since Windows which doesn't support TLS destructor, the implementation // should use GetTLSValue() to retrieve the value of TLS slot. static void OnThreadExit(); diff --git a/base/threading/thread_local_storage_starboard.cc b/base/threading/thread_local_storage_starboard.cc new file mode 100644 index 000000000000..e9e65b20e93f --- /dev/null +++ b/base/threading/thread_local_storage_starboard.cc @@ -0,0 +1,48 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "base/threading/thread_local_storage.h" + +#include "base/notreached.h" + +namespace base { + +namespace internal { + +// static +bool PlatformThreadLocalStorage::AllocTLS(TLSKey* key) { + int res = pthread_key_create(key, base::internal::PlatformThreadLocalStorage::OnThreadExit); + if (res != 0) { + NOTREACHED(); + return false; + } + + return true; +} + +// static +void PlatformThreadLocalStorage::FreeTLS(TLSKey key) { + pthread_key_delete(key); +} + +// static +void PlatformThreadLocalStorage::SetTLSValue(TLSKey key, void* value) { + if (pthread_setspecific(key, value) != 0) { + NOTREACHED(); + } +} + +} // namespace internal + +} // namespace base diff --git a/base/threading/thread_local_storage_unittest.cc b/base/threading/thread_local_storage_unittest.cc index c51b3900418e..b01ba4b368de 100644 --- a/base/threading/thread_local_storage_unittest.cc +++ b/base/threading/thread_local_storage_unittest.cc @@ -96,7 +96,7 @@ void ThreadLocalStorageCleanup(void *value) { TLSSlot().Set(value); } -#if BUILDFLAG(IS_POSIX) +#if BUILDFLAG(IS_POSIX) && !defined(STARBOARD) constexpr intptr_t kDummyValue = 0xABCD; constexpr size_t kKeyCount = 20; @@ -318,7 +318,7 @@ TEST(ThreadLocalStorageTest, TLSReclaim) { } } -#if BUILDFLAG(IS_POSIX) +#if BUILDFLAG(IS_POSIX) && !defined(STARBOARD) // Unlike POSIX, Windows does not iterate through the OS TLS to cleanup any // values there. Instead a per-module thread destruction function is called. // However, it is not possible to perform a check after this point (as the code diff --git a/base/threading/thread_restrictions.cc b/base/threading/thread_restrictions.cc index dd6e049f8db8..016841c24b98 100644 --- a/base/threading/thread_restrictions.cc +++ b/base/threading/thread_restrictions.cc @@ -226,6 +226,12 @@ void DisallowSingleton() { GetSingletonDisallowedTls() = BooleanWithStack(true); } +#if defined(STARBOARD) +bool GetSingletonAllowed() { + return !!GetSingletonDisallowedTls(); +} +#endif + ScopedDisallowSingleton::ScopedDisallowSingleton() : resetter_(&GetSingletonDisallowedTls(), BooleanWithStack(true)) {} diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h index 4c9ff4452fa1..bb14c4744e90 100644 --- a/base/threading/thread_restrictions.h +++ b/base/threading/thread_restrictions.h @@ -966,6 +966,14 @@ INLINE_OR_NOT_TAIL_CALLED void AssertSingletonAllowed() // Disallow using singleton on the current thread. INLINE_OR_NOT_TAIL_CALLED void DisallowSingleton() EMPTY_BODY_IF_DCHECK_IS_OFF; +#if defined(STARBOARD) +#if DCHECK_IS_ON() +INLINE_OR_NOT_TAIL_CALLED bool GetSingletonAllowed() EMPTY_BODY_IF_DCHECK_IS_OFF; +#else +INLINE_OR_NOT_TAIL_CALLED bool GetSingletonAllowed() { return true; } +#endif +#endif + // Disallows singletons within its scope. class BASE_EXPORT [[maybe_unused, nodiscard]] ScopedDisallowSingleton { public: diff --git a/base/threading/thread_task_runner_handle.cc b/base/threading/thread_task_runner_handle.cc new file mode 100644 index 000000000000..b00bfae384a7 --- /dev/null +++ b/base/threading/thread_task_runner_handle.cc @@ -0,0 +1,16 @@ +#include "base/threading/thread_task_runner_handle.h" + +namespace base { +namespace { + +base::LazyInstance>::Leaky + thread_task_runner_tls = LAZY_INSTANCE_INITIALIZER; + +} // namespace + +// static +const scoped_refptr& ThreadTaskRunnerHandle::Get() { + ThreadTaskRunnerHandle* current = thread_task_runner_tls.Pointer()->Get(); + return current->task_runner_; +} +} diff --git a/base/threading/thread_task_runner_handle.h b/base/threading/thread_task_runner_handle.h new file mode 100644 index 000000000000..5d1b45368446 --- /dev/null +++ b/base/threading/thread_task_runner_handle.h @@ -0,0 +1,20 @@ +#ifndef BASE_THREADING_THREAD_TASK_RUNNER_HANDLE_H_ +#define BASE_THREADING_THREAD_TASK_RUNNER_HANDLE_H_ + +#include "base/memory/ref_counted.h" +#include "base/single_thread_task_runner.h" +#include "base/lazy_instance.h" +#include "base/threading/thread_local.h" + +namespace base { + +class ThreadTaskRunnerHandle { + public: + static const scoped_refptr& Get(); + private: + scoped_refptr task_runner_; +}; + +} + +#endif diff --git a/base/time/pr_time_unittest.cc b/base/time/pr_time_unittest.cc index 1381613569c3..457a83897e88 100644 --- a/base/time/pr_time_unittest.cc +++ b/base/time/pr_time_unittest.cc @@ -11,10 +11,27 @@ #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" +#if defined(STARBOARD) +#include "starboard/client_porting/eztime/eztime.h" +#endif + using base::Time; namespace { +#if defined(STARBOARD) +time_t sb_mktime(struct tm *tm) { + if (tm == nullptr) { + return -1; + } + EzTimeExploded exploded = {tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, + tm->tm_wday, tm->tm_yday, tm->tm_isdst}; + EzTimeT secs = EzTimeTImplode(&exploded, EzTimeZone::kEzTimeZoneLocal); + return static_cast(secs); +} +#endif + // time_t representation of 15th Oct 2007 12:45:00 PDT PRTime comparison_time_pdt = 1192477500 * Time::kMicrosecondsPerSecond; @@ -42,8 +59,13 @@ class PRTimeTest : public testing::Test { 0, // day of year (ignored, output only) -1 // DST in effect, -1 tells mktime to figure it out }; +#if defined(STARBOARD) + comparison_time_local_ = + sb_mktime(&local_comparison_tm) * Time::kMicrosecondsPerSecond; +#else comparison_time_local_ = mktime(&local_comparison_tm) * Time::kMicrosecondsPerSecond; +#endif ASSERT_GT(comparison_time_local_, 0); const int microseconds = 441381; @@ -58,8 +80,13 @@ class PRTimeTest : public testing::Test { 0, // day of year (ignored, output only) -1 // DST in effect, -1 tells mktime to figure it out }; +#if defined(STARBOARD) + comparison_time_local_2_ = + sb_mktime(&local_comparison_tm_2) * Time::kMicrosecondsPerSecond; +#else comparison_time_local_2_ = mktime(&local_comparison_tm_2) * Time::kMicrosecondsPerSecond; +#endif ASSERT_GT(comparison_time_local_2_, 0); comparison_time_local_2_ += microseconds; } @@ -68,6 +95,14 @@ class PRTimeTest : public testing::Test { PRTime comparison_time_local_2_; }; +#if !defined(STARBOARD) +// More of the no local time on Starboard issue. We can't use these standard +// functions to check NSPR Time against because they don't always work on all +// platforms, making these tests inherently flaky and non-portable. + +// Tests the PR_ParseTimeString nspr helper function for +// a variety of time strings. + // Tests the PR_ParseTimeString nspr helper function for // a variety of time strings. TEST_F(PRTimeTest, ParseTimeTest1) { @@ -91,6 +126,7 @@ TEST_F(PRTimeTest, ParseTimeTest1) { EXPECT_EQ(PR_SUCCESS, result); EXPECT_EQ(current_time64, parsed_time); } +#endif TEST_F(PRTimeTest, ParseTimeTest2) { PRTime parsed_time = 0; diff --git a/base/time/time.cc b/base/time/time.cc index f6a83921b3ac..6af612366cb5 100644 --- a/base/time/time.cc +++ b/base/time/time.cc @@ -174,7 +174,8 @@ double Time::ToDoubleT() const { : std::numeric_limits::infinity(); } -#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#if defined(STARBOARD) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) // static Time Time::FromTimeSpec(const timespec& ts) { return FromDoubleT(ts.tv_sec + diff --git a/base/time/time.h b/base/time/time.h index 3bea32f0606e..d84b2f69d01a 100644 --- a/base/time/time.h +++ b/base/time/time.h @@ -77,6 +77,9 @@ #include "build/build_config.h" #include "build/chromeos_buildflags.h" +#if defined(STARBOARD) +#include "starboard/common/time.h" +#else #if BUILDFLAG(IS_APPLE) #include "base/time/buildflags/buildflags.h" #endif @@ -114,6 +117,7 @@ struct TimeSpan; } // namespace Windows } // namespace ABI #endif +#endif namespace base { @@ -129,7 +133,80 @@ class BASE_EXPORT TimeDelta { public: constexpr TimeDelta() = default; -#if BUILDFLAG(IS_WIN) +#if defined(STARBOARD) + static constexpr int64_t kHoursPerDay = 24; + static constexpr int64_t kSecondsPerMinute = 60; + static constexpr int64_t kMinutesPerHour = 60; + static constexpr int64_t kSecondsPerHour = + kSecondsPerMinute * kMinutesPerHour; + static constexpr int64_t kMillisecondsPerSecond = 1000; + static constexpr int64_t kMillisecondsPerDay = + kMillisecondsPerSecond * kSecondsPerHour * kHoursPerDay; + static constexpr int64_t kMicrosecondsPerMillisecond = 1000; + static constexpr int64_t kMicrosecondsPerSecond = + kMicrosecondsPerMillisecond * kMillisecondsPerSecond; + static constexpr int64_t kMicrosecondsPerMinute = + kMicrosecondsPerSecond * kSecondsPerMinute; + static constexpr int64_t kMicrosecondsPerHour = + kMicrosecondsPerMinute * kMinutesPerHour; + static constexpr int64_t kMicrosecondsPerDay = + kMicrosecondsPerHour * kHoursPerDay; + static constexpr int64_t kMicrosecondsPerWeek = kMicrosecondsPerDay * 7; + static constexpr int64_t kNanosecondsPerMicrosecond = 1000; + static constexpr int64_t kNanosecondsPerSecond = + kNanosecondsPerMicrosecond * kMicrosecondsPerSecond; + + template + static constexpr TimeDelta FromDays(T n) { + return FromInternalValue(MakeClampedNum(n) * kMicrosecondsPerDay); + } + + template + static constexpr TimeDelta FromHours(T n) { + return TimeDelta::FromInternalValue(MakeClampedNum(n) * + kMicrosecondsPerHour); + } + template + static constexpr TimeDelta FromMinutes(T n) { + return TimeDelta::FromInternalValue(MakeClampedNum(n) * + kMicrosecondsPerMinute); + } + template + static constexpr TimeDelta FromSeconds(T n) { + return TimeDelta::FromInternalValue(MakeClampedNum(n) * + kMicrosecondsPerSecond); + } + template + static constexpr TimeDelta FromSecondsD(T n) { + return TimeDelta::FromInternalValue(MakeClampedNum(n) * + kMicrosecondsPerSecond); + } + template + static constexpr TimeDelta FromMilliseconds(T n) { + return TimeDelta::FromInternalValue(MakeClampedNum(n) * + kMicrosecondsPerMillisecond); + } + template + static constexpr TimeDelta FromMillisecondsD(T n) { + return TimeDelta::FromInternalValue(MakeClampedNum(n) * + kMicrosecondsPerMillisecond); + } + template + static constexpr TimeDelta FromMicroseconds(T n) { + return TimeDelta::FromInternalValue(MakeClampedNum(n)); + } + template + static constexpr TimeDelta FromNanoseconds(T n) { + return TimeDelta::FromInternalValue(MakeClampedNum(n) / + kNanosecondsPerMicrosecond); + } + template + static constexpr TimeDelta FromHertz(T n) { + return n ? TimeDelta::FromInternalValue(kMicrosecondsPerSecond / + MakeClampedNum(n)) + : TimeDelta::Max(); + } +#elif BUILDFLAG(IS_WIN) static TimeDelta FromQPCValue(LONGLONG qpc_value); // TODO(crbug.com/989694): Avoid base::TimeDelta factory functions // based on absolute time @@ -662,7 +739,8 @@ class BASE_EXPORT Time : public time_internal::TimeBase