From 63f347f44c5b98af1c183fd749d5db05ccb28e73 Mon Sep 17 00:00:00 2001 From: Andrew Savage Date: Thu, 23 Jan 2025 15:07:00 +0000 Subject: [PATCH 1/7] tls --- base/observer_list_threadsafe.cc | 36 ++++ base/observer_list_threadsafe.h | 29 +++ base/run_loop.cc | 99 ++++++++++ .../sampling_heap_profiler.cc | 34 ++++ base/sequence_token.cc | 134 +++++++++++++ base/sequence_token.h | 13 ++ base/task/common/scoped_defer_task_posting.cc | 36 ++++ base/task/common/task_annotator.cc | 107 ++++++++++- base/task/common/task_annotator.h | 8 + ...ed_set_task_priority_for_current_thread.cc | 72 +++++++ ...ped_set_task_priority_for_current_thread.h | 4 + .../sequence_manager/sequence_manager_impl.cc | 45 ++++- base/task/sequenced_task_runner.cc | 54 ++++++ base/task/sequenced_task_runner.h | 6 + base/task/single_thread_task_runner.cc | 38 ++++ base/task/single_thread_task_runner.h | 2 + base/task/thread_pool/task_tracker.cc | 41 ++++ base/task/thread_pool/thread_group.cc | 45 +++++ base/threading/hang_watcher.cc | 44 +++++ base/threading/hang_watcher.h | 2 + base/threading/platform_thread.cc | 91 +++++++++ base/threading/scoped_blocking_call.cc | 44 +++++ .../scoped_blocking_call_internal.cc | 66 +++++++ .../threading/scoped_blocking_call_internal.h | 4 + base/threading/sequence_local_storage_map.cc | 50 +++++ base/threading/sequence_local_storage_map.h | 2 + base/threading/thread.cc | 38 ++++ base/threading/thread_id_name_manager.cc | 37 ++++ base/trace_event/trace_log.cc | 177 ++++++++++++++++++ base/tracing/tracing_tls.cc | 6 + 30 files changed, 1361 insertions(+), 3 deletions(-) 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/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/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/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/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/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/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/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_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/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/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/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/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/trace_event/trace_log.cc b/base/trace_event/trace_log.cc index de7223099b90..762e0d4ff1fb 100644 --- a/base/trace_event/trace_log.cc +++ b/base/trace_event/trace_log.cc @@ -76,9 +76,18 @@ extern char __executable_start; #endif +#if defined(STARBOARD) +#include + +#include "base/check_op.h" +#include "starboard/thread.h" +#endif + namespace base { namespace trace_event { +void ThreadLocalEventBufferDestructor(void* buffer); + namespace { // Controls the number of trace events we will buffer in-memory @@ -109,10 +118,77 @@ constexpr TimeDelta kThreadFlushTimeout = Seconds(3); TraceLog* g_trace_log_for_testing = nullptr; +#if defined(STARBOARD) +ABSL_CONST_INIT pthread_once_t s_once_thread_local_event_buffer_flag = + PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_local_event_buffer_key = 0; + +void InitThreadLocalEventBufferKey() { + int res = pthread_key_create(&s_thread_local_event_buffer_key, NULL); + DCHECK(res == 0); +} + +void EnsureThreadLocalEventBufferKeyInited() { + pthread_once(&s_once_thread_local_event_buffer_flag, + InitThreadLocalEventBufferKey); +} + +TraceLog::ThreadLocalEventBuffer* GetThreadLocalEventBuffer() { + EnsureThreadLocalEventBufferKeyInited(); + return static_cast( + pthread_getspecific(s_thread_local_event_buffer_key)); +} + +ABSL_CONST_INIT pthread_once_t s_thread_blocks_message_loop_once_flag = + PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_blocks_message_loop_key = 0; + +void InitThreadBlocksMessageLoop() { + int res = pthread_key_create(&s_thread_blocks_message_loop_key, NULL); + DCHECK(res == 0); +} + +void EnsureThreadBlocksMessageLoopKeyKeyInited() { + pthread_once(&s_thread_blocks_message_loop_once_flag, + InitThreadBlocksMessageLoop); +} + +bool GetThreadBlocksMessageLoop() { + EnsureThreadBlocksMessageLoopKeyKeyInited(); + void* thread_blocks_message_loop = + pthread_getspecific(s_thread_blocks_message_loop_key); + return !!thread_blocks_message_loop + ? reinterpret_cast(thread_blocks_message_loop) != 0 + : false; +} + +ABSL_CONST_INIT pthread_once_t s_thread_is_in_trace_event_once_flag = + PTHREAD_ONCE_INIT; +ABSL_CONST_INIT pthread_key_t s_thread_is_in_trace_event_key = 0; + +void InitThreadIsInTraceEvent() { + int res = pthread_key_create(&s_thread_is_in_trace_event_key, NULL); + DCHECK(res == 0); +} + +void EnsureThreadIsInTraceEventKeyInited() { + pthread_once(&s_thread_is_in_trace_event_once_flag, InitThreadIsInTraceEvent); +} + +bool GetThreadIsInTraceEvent() { + EnsureThreadIsInTraceEventKeyInited(); + void* thread_is_in_trace_event = + pthread_getspecific(s_thread_is_in_trace_event_key); + return !!thread_is_in_trace_event + ? reinterpret_cast(thread_is_in_trace_event) != 0 + : false; +} +#else ABSL_CONST_INIT thread_local TraceLog::ThreadLocalEventBuffer* thread_local_event_buffer = nullptr; ABSL_CONST_INIT thread_local bool thread_blocks_message_loop = false; ABSL_CONST_INIT thread_local bool thread_is_in_trace_event = false; +#endif ThreadTicks ThreadNow() { return ThreadTicks::IsSupported() @@ -489,11 +565,18 @@ class TraceLog::ThreadLocalEventBuffer void FlushWhileLocked(); void CheckThisIsCurrentBuffer() const { +#if defined(STARBOARD) + auto thread_local_event_buffer = GetThreadLocalEventBuffer(); +#endif DCHECK_EQ(thread_local_event_buffer, this); } +#if defined(STARBOARD) + void* reset_to_ = nullptr; +#else const AutoReset resetter_{&thread_local_event_buffer, this, nullptr}; +#endif // Since TraceLog is a leaky singleton, trace_log_ will always be valid // as long as the thread exists. raw_ptr trace_log_; @@ -502,9 +585,17 @@ class TraceLog::ThreadLocalEventBuffer int generation_; }; +void ThreadLocalEventBufferDestructor(void* buffer) { + delete static_cast(buffer); +} + TraceLog::ThreadLocalEventBuffer::ThreadLocalEventBuffer(TraceLog* trace_log) : trace_log_(trace_log), generation_(trace_log->generation()) { +#if defined(STARBOARD) + reset_to_ = pthread_getspecific(s_thread_local_event_buffer_key); + pthread_setspecific(s_thread_local_event_buffer_key, this); +#endif // ThreadLocalEventBuffer is created only if the thread has a message loop, so // the following message_loop won't be NULL. CurrentThread::Get()->AddDestructionObserver(this); @@ -529,6 +620,10 @@ TraceLog::ThreadLocalEventBuffer::~ThreadLocalEventBuffer() { AutoLock lock(trace_log_->lock_); FlushWhileLocked(); trace_log_->thread_task_runners_.erase(PlatformThread::CurrentId()); + +#if defined(STARBOARD) + pthread_setspecific(s_thread_local_event_buffer_key, reset_to_); +#endif } TraceEvent* TraceLog::ThreadLocalEventBuffer::AddTraceEvent( @@ -685,18 +780,31 @@ void TraceLog::InitializeThreadLocalEventBufferIfSupported() { // - to handle the final flush. // For a thread without a message loop or if the message loop may be blocked, // the trace events will be added into the main buffer directly. +#if defined(STARBOARD) + bool thread_blocks_message_loop = GetThreadBlocksMessageLoop(); +#endif if (thread_blocks_message_loop || !CurrentThread::IsSet() || !SingleThreadTaskRunner::HasCurrentDefault()) { return; } HEAP_PROFILER_SCOPED_IGNORE; +#if defined(STARBOARD) + auto thread_local_event_buffer = GetThreadLocalEventBuffer(); +#endif if (thread_local_event_buffer && !CheckGeneration(thread_local_event_buffer->generation())) { delete thread_local_event_buffer; } +#if defined(STARBOARD) + thread_local_event_buffer = GetThreadLocalEventBuffer(); +#endif if (!thread_local_event_buffer) { thread_local_event_buffer = new ThreadLocalEventBuffer(this); } +#if defined(STARBOARD) + pthread_setspecific(s_thread_local_event_buffer_key, + thread_local_event_buffer); +#endif } bool TraceLog::OnMemoryDump(const MemoryDumpArgs& args, @@ -1497,6 +1605,9 @@ void TraceLog::FlushCurrentThread(int generation, bool discard_events) { } } +#if defined(STARBOARD) + auto thread_local_event_buffer = GetThreadLocalEventBuffer(); +#endif // This will flush the thread local buffer. delete thread_local_event_buffer; @@ -1563,6 +1674,9 @@ bool TraceLog::ShouldAddAfterUpdatingState( // Avoid re-entrance of AddTraceEvent. This may happen in GPU process when // ECHO_TO_CONSOLE is enabled: AddTraceEvent -> LOG(ERROR) -> // GpuProcessLogMessageHandler -> PostPendingTask -> TRACE_EVENT ... +#if defined(STARBOARD) + bool thread_is_in_trace_event = GetThreadIsInTraceEvent(); +#endif if (thread_is_in_trace_event) { return false; } @@ -1576,9 +1690,28 @@ bool TraceLog::ShouldAddAfterUpdatingState( // call (if any), but don't bother if the new name is empty. Note this will // not detect a thread name change within the same char* buffer address: we // favor common case performance over corner case correctness. +#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* current_thread_name = + static_cast(pthread_getspecific(s_thread_local_key)); +#else thread_local const char* current_thread_name = nullptr; +#endif if (new_name != current_thread_name && new_name && *new_name) { current_thread_name = new_name; +#if defined(STARBOARD) + pthread_setspecific(s_thread_local_key, + const_cast(current_thread_name)); +#endif AutoLock thread_info_lock(thread_info_lock_); @@ -1726,7 +1859,13 @@ TraceEventHandle TraceLog::AddTraceEventWithThreadIdAndTimestamps( } DCHECK(!timestamp.is_null()); +#if defined(STARBOARD) + void* reset_to = pthread_getspecific(s_thread_is_in_trace_event_key); + pthread_setspecific(s_thread_is_in_trace_event_key, + reinterpret_cast(static_cast(true))); +#else const AutoReset resetter(&thread_is_in_trace_event, true, false); +#endif // Flow bind_ids don't have scopes, so we need to mangle in-process ones to // avoid collisions. @@ -1742,6 +1881,9 @@ TraceEventHandle TraceLog::AddTraceEventWithThreadIdAndTimestamps( // |thread_local_event_buffer| can be null if the current thread doesn't // have a message loop or the message loop is blocked. InitializeThreadLocalEventBufferIfSupported(); +#if defined(STARBOARD) + auto thread_local_event_buffer = GetThreadLocalEventBuffer(); +#endif event_buffer = thread_local_event_buffer; } @@ -1792,6 +1934,10 @@ TraceEventHandle TraceLog::AddTraceEventWithThreadIdAndTimestamps( if (!console_message.empty()) LOG(ERROR) << console_message; +#if defined(STARBOARD) + pthread_setspecific(s_thread_is_in_trace_event_key, reset_to); +#endif + return handle; } @@ -1891,10 +2037,19 @@ void TraceLog::UpdateTraceEventDurationExplicit( // Avoid re-entrance of AddTraceEvent. This may happen in GPU process when // ECHO_TO_CONSOLE is enabled: AddTraceEvent -> LOG(ERROR) -> // GpuProcessLogMessageHandler -> PostPendingTask -> TRACE_EVENT ... +#if defined(STARBOARD) + bool thread_is_in_trace_event = GetThreadIsInTraceEvent(); +#endif if (thread_is_in_trace_event) { return; } +#if defined(STARBOARD) + void* reset_to = pthread_getspecific(s_thread_is_in_trace_event_key); + pthread_setspecific(s_thread_is_in_trace_event_key, + reinterpret_cast(static_cast(true))); +#else const AutoReset resetter(&thread_is_in_trace_event, true); +#endif #if BUILDFLAG(IS_WIN) // Generate an ETW event that marks the end of a complete event. @@ -1931,6 +2086,10 @@ void TraceLog::UpdateTraceEventDurationExplicit( if (!console_message.empty()) LOG(ERROR) << console_message; + +#if defined(STARBOARD) + pthread_setspecific(s_thread_is_in_trace_event_key, reset_to); +#endif } uint64_t TraceLog::MangleEventId(uint64_t id) { @@ -2016,6 +2175,15 @@ void TraceLog::AddMetadataEventsWhileLocked() { it.second); } +#if defined(USE_COBALT_CUSTOMIZATIONS) && !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) + AutoLock thread_info_lock(thread_info_lock_); + for (const auto& it : thread_names_) { + if (it.second.empty()) + continue; + AddMetadataEventWhileLocked(it.first, "thread_name", "name", it.second); + } +#endif + // If buffer is full, add a metadata record to report this. if (!buffer_limit_reached_timestamp_.is_null()) { AddMetadataEventWhileLocked(current_thread_id, "trace_buffer_overflowed", @@ -2038,6 +2206,9 @@ TraceEvent* TraceLog::GetEventByHandleInternal(TraceEventHandle handle, DCHECK(handle.chunk_index <= TraceBufferChunk::kMaxChunkIndex); DCHECK(handle.event_index <= TraceBufferChunk::kTraceBufferChunkSize - 1); +#if defined(STARBOARD) + auto thread_local_event_buffer = GetThreadLocalEventBuffer(); +#endif if (thread_local_event_buffer) { TraceEvent* trace_event = thread_local_event_buffer->GetEventByHandle(handle); @@ -2128,7 +2299,13 @@ size_t TraceLog::GetObserverCountForTest() const { } void TraceLog::SetCurrentThreadBlocksMessageLoop() { +#if defined(STARBOARD) + auto thread_local_event_buffer = GetThreadLocalEventBuffer(); + pthread_setspecific(s_thread_blocks_message_loop_key, + reinterpret_cast(static_cast(true))); +#else thread_blocks_message_loop = true; +#endif // This will flush the thread local buffer. delete thread_local_event_buffer; } diff --git a/base/tracing/tracing_tls.cc b/base/tracing/tracing_tls.cc index 1fd40644ba4e..2c7ec8f532ab 100644 --- a/base/tracing/tracing_tls.cc +++ b/base/tracing/tracing_tls.cc @@ -9,8 +9,14 @@ namespace tracing { // static bool* GetThreadIsInTraceEvent() { +#if defined(STARBOARD) + // This is only used in //services, which Cobalt does not use. + static bool thread_is_in_trace_event = false; + return &thread_is_in_trace_event; +#else thread_local bool thread_is_in_trace_event = false; return &thread_is_in_trace_event; +#endif } } // namespace tracing From ad6ac3669b2a803d2189a0ff4d0bce3a9ffdbd37 Mon Sep 17 00:00:00 2001 From: Andrew Savage Date: Thu, 23 Jan 2025 15:38:28 +0000 Subject: [PATCH 2/7] starboard impls --- base/base_paths.h | 4 + base/base_paths_starboard.cc | 119 ++++++ base/base_paths_starboard.h | 48 +++ base/message_loop/message_pump_for_io.h | 8 +- base/message_loop/message_pump_for_ui.h | 8 +- .../message_loop/message_pump_io_starboard.cc | 345 ++++++++++++++++++ base/message_loop/message_pump_io_starboard.h | 177 +++++++++ .../message_pump_io_starboard_unittest.cc | 279 ++++++++++++++ .../message_loop/message_pump_ui_starboard.cc | 173 +++++++++ base/message_loop/message_pump_ui_starboard.h | 103 ++++++ 10 files changed, 1260 insertions(+), 4 deletions(-) create mode 100644 base/base_paths_starboard.cc create mode 100644 base/base_paths_starboard.h create mode 100644 base/message_loop/message_pump_io_starboard.cc create mode 100644 base/message_loop/message_pump_io_starboard.h create mode 100644 base/message_loop/message_pump_io_starboard_unittest.cc create mode 100644 base/message_loop/message_pump_ui_starboard.cc create mode 100644 base/message_loop/message_pump_ui_starboard.h 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/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_ From b809c8da0c091a1e295879ccc3021329b71a3e98 Mon Sep 17 00:00:00 2001 From: Andrew Savage Date: Thu, 23 Jan 2025 15:41:45 +0000 Subject: [PATCH 3/7] sb time --- base/third_party/nspr/prtime.cc | 47 ++++++++++++++++- base/time/pr_time_unittest.cc | 36 +++++++++++++ base/time/time.cc | 3 +- base/time/time.h | 88 ++++++++++++++++++++++++++++++-- base/time/time_now_starboard.cc | 70 ++++++++++++++++++++++++++ base/time/time_starboard.cc | 89 +++++++++++++++++++++++++++++++++ base/time/time_unittest.cc | 62 ++++++++++++++++++++++- 7 files changed, 388 insertions(+), 7 deletions(-) create mode 100644 base/time/time_now_starboard.cc create mode 100644 base/time/time_starboard.cc 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/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