123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422 |
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "base/notreached.h"
- #include "base/threading/platform_thread.h"
- #include <errno.h>
- #include <pthread.h>
- #include <sched.h>
- #include <stddef.h>
- #include <stdint.h>
- #include <sys/time.h>
- #include <sys/types.h>
- #include <unistd.h>
- #include <memory>
- #include <tuple>
- #include "base/allocator/buildflags.h"
- #include "base/debug/activity_tracker.h"
- #include "base/lazy_instance.h"
- #include "base/logging.h"
- #include "base/memory/raw_ptr.h"
- #include "base/threading/platform_thread_internal_posix.h"
- #include "base/threading/scoped_blocking_call.h"
- #include "base/threading/thread_id_name_manager.h"
- #include "base/threading/thread_restrictions.h"
- #include "build/build_config.h"
- #if !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_NACL)
- #include "base/posix/can_lower_nice_to.h"
- #endif
- #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
- #include <sys/syscall.h>
- #include <atomic>
- #endif
- #if BUILDFLAG(IS_FUCHSIA)
- #include <zircon/process.h>
- #else
- #include <sys/resource.h>
- #endif
- #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
- #include "base/allocator/partition_allocator/starscan/pcscan.h"
- #include "base/allocator/partition_allocator/starscan/stack/stack.h"
- #endif
- namespace base {
- void InitThreading();
- void TerminateOnThread();
- size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes);
- namespace {
- struct ThreadParams {
- ThreadParams() = default;
- raw_ptr<PlatformThread::Delegate> delegate = nullptr;
- bool joinable = false;
- ThreadType thread_type = ThreadType::kDefault;
- MessagePumpType message_pump_type = MessagePumpType::DEFAULT;
- };
- void* ThreadFunc(void* params) {
- PlatformThread::Delegate* delegate = nullptr;
- {
- std::unique_ptr<ThreadParams> thread_params(
- static_cast<ThreadParams*>(params));
- delegate = thread_params->delegate;
- if (!thread_params->joinable)
- base::DisallowSingleton();
- #if !BUILDFLAG(IS_NACL)
- #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
- partition_alloc::internal::PCScan::NotifyThreadCreated(
- partition_alloc::internal::GetStackPointer());
- #endif
- #if BUILDFLAG(IS_APPLE)
- PlatformThread::SetCurrentThreadRealtimePeriodValue(
- delegate->GetRealtimePeriod());
- #endif
- // Threads on linux/android may inherit their priority from the thread
- // where they were created. This explicitly sets the priority of all new
- // threads.
- PlatformThread::SetCurrentThreadType(thread_params->thread_type);
- #endif
- }
- ThreadIdNameManager::GetInstance()->RegisterThread(
- PlatformThread::CurrentHandle().platform_handle(),
- PlatformThread::CurrentId());
- delegate->ThreadMain();
- ThreadIdNameManager::GetInstance()->RemoveName(
- PlatformThread::CurrentHandle().platform_handle(),
- PlatformThread::CurrentId());
- #if !BUILDFLAG(IS_NACL) && BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
- partition_alloc::internal::PCScan::NotifyThreadDestroyed();
- #endif
- base::TerminateOnThread();
- return nullptr;
- }
- bool CreateThread(size_t stack_size,
- bool joinable,
- PlatformThread::Delegate* delegate,
- PlatformThreadHandle* thread_handle,
- ThreadType thread_type,
- MessagePumpType message_pump_type) {
- DCHECK(thread_handle);
- base::InitThreading();
- pthread_attr_t attributes;
- pthread_attr_init(&attributes);
- // Pthreads are joinable by default, so only specify the detached
- // attribute if the thread should be non-joinable.
- if (!joinable)
- pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED);
- // Get a better default if available.
- if (stack_size == 0)
- stack_size = base::GetDefaultThreadStackSize(attributes);
- if (stack_size > 0)
- pthread_attr_setstacksize(&attributes, stack_size);
- std::unique_ptr<ThreadParams> params(new ThreadParams);
- params->delegate = delegate;
- params->joinable = joinable;
- params->thread_type = thread_type;
- params->message_pump_type = message_pump_type;
- pthread_t handle;
- int err = pthread_create(&handle, &attributes, ThreadFunc, params.get());
- bool success = !err;
- if (success) {
- // ThreadParams should be deleted on the created thread after used.
- std::ignore = params.release();
- } else {
- // Value of |handle| is undefined if pthread_create fails.
- handle = 0;
- errno = err;
- PLOG(ERROR) << "pthread_create";
- }
- *thread_handle = PlatformThreadHandle(handle);
- pthread_attr_destroy(&attributes);
- return success;
- }
- #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
- // Store the thread ids in local storage since calling the SWI can be
- // expensive and PlatformThread::CurrentId is used liberally.
- thread_local pid_t g_thread_id = -1;
- // A boolean value that indicates that the value stored in |g_thread_id| on the
- // main thread is invalid, because it hasn't been updated since the process
- // forked.
- //
- // This used to work by setting |g_thread_id| to -1 in a pthread_atfork handler.
- // However, when a multithreaded process forks, it is only allowed to call
- // async-signal-safe functions until it calls an exec() syscall. However,
- // accessing TLS may allocate (see crbug.com/1275748), which is not
- // async-signal-safe and therefore causes deadlocks, corruption, and crashes.
- //
- // It's Atomic to placate TSAN.
- std::atomic<bool> g_main_thread_tid_cache_valid = false;
- // Tracks whether the current thread is the main thread, and therefore whether
- // |g_main_thread_tid_cache_valid| is relevant for the current thread. This is
- // also updated by PlatformThread::CurrentId().
- thread_local bool g_is_main_thread = true;
- class InitAtFork {
- public:
- InitAtFork() {
- pthread_atfork(nullptr, nullptr, internal::InvalidateTidCache);
- }
- };
- #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
- } // namespace
- #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
- namespace internal {
- void InvalidateTidCache() {
- g_main_thread_tid_cache_valid.store(false, std::memory_order_relaxed);
- }
- } // namespace internal
- #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
- // static
- PlatformThreadId PlatformThread::CurrentId() {
- // Pthreads doesn't have the concept of a thread ID, so we have to reach down
- // into the kernel.
- #if BUILDFLAG(IS_APPLE)
- return pthread_mach_thread_np(pthread_self());
- #elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
- static InitAtFork init_at_fork;
- if (g_thread_id == -1 ||
- (g_is_main_thread &&
- !g_main_thread_tid_cache_valid.load(std::memory_order_relaxed))) {
- // Update the cached tid.
- g_thread_id = static_cast<pid_t>(syscall(__NR_gettid));
- // If this is the main thread, we can mark the tid_cache as valid.
- // Otherwise, stop the current thread from always entering this slow path.
- if (g_thread_id == getpid()) {
- g_main_thread_tid_cache_valid.store(true, std::memory_order_relaxed);
- } else {
- g_is_main_thread = false;
- }
- } else {
- #if DCHECK_IS_ON()
- if (g_thread_id != syscall(__NR_gettid)) {
- RAW_LOG(
- FATAL,
- "Thread id stored in TLS is different from thread id returned by "
- "the system. It is likely that the process was forked without going "
- "through fork().");
- }
- #endif
- }
- return g_thread_id;
- #elif BUILDFLAG(IS_ANDROID)
- // Note: do not cache the return value inside a thread_local variable on
- // Android (as above). The reasons are:
- // - thread_local is slow on Android (goes through emutls)
- // - gettid() is fast, since its return value is cached in pthread (in the
- // thread control block of pthread). See gettid.c in bionic.
- return gettid();
- #elif BUILDFLAG(IS_FUCHSIA)
- return zx_thread_self();
- #elif BUILDFLAG(IS_SOLARIS) || BUILDFLAG(IS_QNX)
- return pthread_self();
- #elif BUILDFLAG(IS_NACL) && defined(__GLIBC__)
- return pthread_self();
- #elif BUILDFLAG(IS_NACL) && !defined(__GLIBC__)
- // Pointers are 32-bits in NaCl.
- return reinterpret_cast<int32_t>(pthread_self());
- #elif BUILDFLAG(IS_POSIX) && BUILDFLAG(IS_AIX)
- return pthread_self();
- #elif BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_AIX)
- return reinterpret_cast<int64_t>(pthread_self());
- #endif
- }
- // static
- PlatformThreadRef PlatformThread::CurrentRef() {
- return PlatformThreadRef(pthread_self());
- }
- // static
- PlatformThreadHandle PlatformThread::CurrentHandle() {
- return PlatformThreadHandle(pthread_self());
- }
- #if !BUILDFLAG(IS_APPLE)
- // static
- void PlatformThread::YieldCurrentThread() {
- sched_yield();
- }
- #endif // !BUILDFLAG(IS_APPLE)
- // static
- void PlatformThread::Sleep(TimeDelta duration) {
- struct timespec sleep_time, remaining;
- // Break the duration into seconds and nanoseconds.
- // NOTE: TimeDelta's microseconds are int64s while timespec's
- // nanoseconds are longs, so this unpacking must prevent overflow.
- sleep_time.tv_sec = static_cast<time_t>(duration.InSeconds());
- duration -= Seconds(sleep_time.tv_sec);
- sleep_time.tv_nsec = static_cast<long>(duration.InMicroseconds() * 1000);
- while (nanosleep(&sleep_time, &remaining) == -1 && errno == EINTR)
- sleep_time = remaining;
- }
- // static
- const char* PlatformThread::GetName() {
- return ThreadIdNameManager::GetInstance()->GetName(CurrentId());
- }
- // static
- bool PlatformThread::CreateWithType(size_t stack_size,
- Delegate* delegate,
- PlatformThreadHandle* thread_handle,
- ThreadType thread_type,
- MessagePumpType pump_type_hint) {
- return CreateThread(stack_size, true /* joinable thread */, delegate,
- thread_handle, thread_type, pump_type_hint);
- }
- // static
- bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) {
- return CreateNonJoinableWithType(stack_size, delegate, ThreadType::kDefault);
- }
- // static
- bool PlatformThread::CreateNonJoinableWithType(size_t stack_size,
- Delegate* delegate,
- ThreadType thread_type,
- MessagePumpType pump_type_hint) {
- PlatformThreadHandle unused;
- bool result = CreateThread(stack_size, false /* non-joinable thread */,
- delegate, &unused, thread_type, pump_type_hint);
- return result;
- }
- // static
- void PlatformThread::Join(PlatformThreadHandle thread_handle) {
- // Record the event that this thread is blocking upon (for hang diagnosis).
- base::debug::ScopedThreadJoinActivity thread_activity(&thread_handle);
- // Joining another thread may block the current thread for a long time, since
- // the thread referred to by |thread_handle| may still be running long-lived /
- // blocking tasks.
- base::internal::ScopedBlockingCallWithBaseSyncPrimitives scoped_blocking_call(
- FROM_HERE, base::BlockingType::MAY_BLOCK);
- CHECK_EQ(0, pthread_join(thread_handle.platform_handle(), nullptr));
- }
- // static
- void PlatformThread::Detach(PlatformThreadHandle thread_handle) {
- CHECK_EQ(0, pthread_detach(thread_handle.platform_handle()));
- }
- // Mac and Fuchsia have their own SetCurrentThreadType() and
- // GetCurrentThreadPriorityForTest() implementations.
- #if !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_FUCHSIA)
- // static
- bool PlatformThread::CanChangeThreadType(ThreadType from, ThreadType to) {
- #if BUILDFLAG(IS_NACL)
- return false;
- #else
- if (from >= to) {
- // Decreasing thread priority on POSIX is always allowed.
- return true;
- }
- if (to == ThreadType::kRealtimeAudio) {
- return internal::CanSetThreadTypeToRealtimeAudio();
- }
- return internal::CanLowerNiceTo(internal::ThreadTypeToNiceValue(to));
- #endif // BUILDFLAG(IS_NACL)
- }
- namespace internal {
- void SetCurrentThreadTypeImpl(ThreadType thread_type,
- MessagePumpType pump_type_hint) {
- #if BUILDFLAG(IS_NACL)
- NOTIMPLEMENTED();
- #else
- if (internal::SetCurrentThreadTypeForPlatform(thread_type, pump_type_hint))
- return;
- // setpriority(2) should change the whole thread group's (i.e. process)
- // priority. However, as stated in the bugs section of
- // http://man7.org/linux/man-pages/man2/getpriority.2.html: "under the current
- // Linux/NPTL implementation of POSIX threads, the nice value is a per-thread
- // attribute". Also, 0 is prefered to the current thread id since it is
- // equivalent but makes sandboxing easier (https://crbug.com/399473).
- const int nice_setting = internal::ThreadTypeToNiceValue(thread_type);
- if (setpriority(PRIO_PROCESS, 0, nice_setting)) {
- DVPLOG(1) << "Failed to set nice value of thread ("
- << PlatformThread::CurrentId() << ") to " << nice_setting;
- }
- #endif // BUILDFLAG(IS_NACL)
- }
- } // namespace internal
- // static
- ThreadPriorityForTest PlatformThread::GetCurrentThreadPriorityForTest() {
- #if BUILDFLAG(IS_NACL)
- NOTIMPLEMENTED();
- return ThreadPriorityForTest::kNormal;
- #else
- // Mirrors SetCurrentThreadPriority()'s implementation.
- auto platform_specific_priority =
- internal::GetCurrentThreadPriorityForPlatformForTest(); // IN-TEST
- if (platform_specific_priority)
- return platform_specific_priority.value();
- int nice_value = internal::GetCurrentThreadNiceValue();
- return internal::NiceValueToThreadPriorityForTest(nice_value); // IN-TEST
- #endif // !BUILDFLAG(IS_NACL)
- }
- #endif // !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_FUCHSIA)
- // static
- size_t PlatformThread::GetDefaultThreadStackSize() {
- pthread_attr_t attributes;
- pthread_attr_init(&attributes);
- return base::GetDefaultThreadStackSize(attributes);
- }
- } // namespace base
|