| // 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/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 "base/debug/activity_tracker.h" | 
 | #include "base/lazy_instance.h" | 
 | #include "base/logging.h" | 
 | #include "base/threading/platform_thread_internal_posix.h" | 
 | #include "base/threading/thread_id_name_manager.h" | 
 | #include "base/threading/thread_restrictions.h" | 
 | #include "build_config.h" | 
 |  | 
 | #if defined(OS_LINUX) | 
 | #include <sys/syscall.h> | 
 | #endif | 
 |  | 
 | #if defined(OS_FUCHSIA) | 
 | #include <zircon/process.h> | 
 | #else | 
 | #include <sys/resource.h> | 
 | #endif | 
 |  | 
 | namespace base { | 
 |  | 
 | void InitThreading(); | 
 | void TerminateOnThread(); | 
 | size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes); | 
 |  | 
 | namespace { | 
 |  | 
 | struct ThreadParams { | 
 |   ThreadParams() | 
 |       : delegate(nullptr), joinable(false), priority(ThreadPriority::NORMAL) {} | 
 |  | 
 |   PlatformThread::Delegate* delegate; | 
 |   bool joinable; | 
 |   ThreadPriority priority; | 
 | }; | 
 |  | 
 | 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::ThreadRestrictions::SetSingletonAllowed(false); | 
 |  | 
 | #if !defined(OS_NACL) | 
 |     // 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::SetCurrentThreadPriority(thread_params->priority); | 
 | #endif | 
 |   } | 
 |  | 
 |   ThreadIdNameManager::GetInstance()->RegisterThread( | 
 |       PlatformThread::CurrentHandle().platform_handle(), | 
 |       PlatformThread::CurrentId()); | 
 |  | 
 |   delegate->ThreadMain(); | 
 |  | 
 |   ThreadIdNameManager::GetInstance()->RemoveName( | 
 |       PlatformThread::CurrentHandle().platform_handle(), | 
 |       PlatformThread::CurrentId()); | 
 |  | 
 |   base::TerminateOnThread(); | 
 |   return nullptr; | 
 | } | 
 |  | 
 | bool CreateThread(size_t stack_size, | 
 |                   bool joinable, | 
 |                   PlatformThread::Delegate* delegate, | 
 |                   PlatformThreadHandle* thread_handle, | 
 |                   ThreadPriority priority) { | 
 |   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->priority = priority; | 
 |  | 
 |   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. | 
 |     ignore_result(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; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | // static | 
 | PlatformThreadId PlatformThread::CurrentId() { | 
 |   // Pthreads doesn't have the concept of a thread ID, so we have to reach down | 
 |   // into the kernel. | 
 | #if defined(OS_MACOSX) | 
 |   return pthread_mach_thread_np(pthread_self()); | 
 | #elif defined(OS_LINUX) | 
 |   return syscall(__NR_gettid); | 
 | #elif defined(OS_ANDROID) | 
 |   return gettid(); | 
 | #elif defined(OS_FUCHSIA) | 
 |   return zx_thread_self(); | 
 | #elif defined(OS_SOLARIS) || defined(OS_QNX) | 
 |   return pthread_self(); | 
 | #elif defined(OS_NACL) && defined(__GLIBC__) | 
 |   return pthread_self(); | 
 | #elif defined(OS_NACL) && !defined(__GLIBC__) | 
 |   // Pointers are 32-bits in NaCl. | 
 |   return reinterpret_cast<int32_t>(pthread_self()); | 
 | #elif defined(OS_POSIX) && defined(OS_AIX) | 
 |   return pthread_self(); | 
 | #elif defined(OS_POSIX) && !defined(OS_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()); | 
 | } | 
 |  | 
 | // static | 
 | void PlatformThread::YieldCurrentThread() { | 
 |   sched_yield(); | 
 | } | 
 |  | 
 | // 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 = duration.InSeconds(); | 
 |   duration -= TimeDelta::FromSeconds(sleep_time.tv_sec); | 
 |   sleep_time.tv_nsec = duration.InMicroseconds() * 1000;  // nanoseconds | 
 |  | 
 |   while (nanosleep(&sleep_time, &remaining) == -1 && errno == EINTR) | 
 |     sleep_time = remaining; | 
 | } | 
 |  | 
 | // static | 
 | const char* PlatformThread::GetName() { | 
 |   return ThreadIdNameManager::GetInstance()->GetName(CurrentId()); | 
 | } | 
 |  | 
 | // static | 
 | bool PlatformThread::CreateWithPriority(size_t stack_size, Delegate* delegate, | 
 |                                         PlatformThreadHandle* thread_handle, | 
 |                                         ThreadPriority priority) { | 
 |   return CreateThread(stack_size, true /* joinable thread */, delegate, | 
 |                       thread_handle, priority); | 
 | } | 
 |  | 
 | // static | 
 | bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) { | 
 |   return CreateNonJoinableWithPriority(stack_size, delegate, | 
 |                                        ThreadPriority::NORMAL); | 
 | } | 
 |  | 
 | // static | 
 | bool PlatformThread::CreateNonJoinableWithPriority(size_t stack_size, | 
 |                                                    Delegate* delegate, | 
 |                                                    ThreadPriority priority) { | 
 |   PlatformThreadHandle unused; | 
 |  | 
 |   bool result = CreateThread(stack_size, false /* non-joinable thread */, | 
 |                              delegate, &unused, priority); | 
 |   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. | 
 |   AssertBlockingAllowed(); | 
 |   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 Set/GetCurrentThreadPriority() | 
 | // implementations. | 
 | #if !defined(OS_MACOSX) && !defined(OS_FUCHSIA) | 
 |  | 
 | // static | 
 | bool PlatformThread::CanIncreaseCurrentThreadPriority() { | 
 | #if defined(OS_NACL) | 
 |   return false; | 
 | #else | 
 |   // Only root can raise thread priority on POSIX environment. On Linux, users | 
 |   // who have CAP_SYS_NICE permission also can raise the thread priority, but | 
 |   // libcap.so would be needed to check the capability. | 
 |   return geteuid() == 0; | 
 | #endif  // defined(OS_NACL) | 
 | } | 
 |  | 
 | // static | 
 | void PlatformThread::SetCurrentThreadPriority(ThreadPriority priority) { | 
 | #if defined(OS_NACL) | 
 |   NOTIMPLEMENTED(); | 
 | #else | 
 |   if (internal::SetCurrentThreadPriorityForPlatform(priority)) | 
 |     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::ThreadPriorityToNiceValue(priority); | 
 |   if (setpriority(PRIO_PROCESS, 0, nice_setting)) { | 
 |     DVPLOG(1) << "Failed to set nice value of thread (" | 
 |               << PlatformThread::CurrentId() << ") to " << nice_setting; | 
 |   } | 
 | #endif  // defined(OS_NACL) | 
 | } | 
 |  | 
 | // static | 
 | ThreadPriority PlatformThread::GetCurrentThreadPriority() { | 
 | #if defined(OS_NACL) | 
 |   NOTIMPLEMENTED(); | 
 |   return ThreadPriority::NORMAL; | 
 | #else | 
 |   // Mirrors SetCurrentThreadPriority()'s implementation. | 
 |   ThreadPriority platform_specific_priority; | 
 |   if (internal::GetCurrentThreadPriorityForPlatform( | 
 |           &platform_specific_priority)) { | 
 |     return platform_specific_priority; | 
 |   } | 
 |  | 
 |   // Need to clear errno before calling getpriority(): | 
 |   // http://man7.org/linux/man-pages/man2/getpriority.2.html | 
 |   errno = 0; | 
 |   int nice_value = getpriority(PRIO_PROCESS, 0); | 
 |   if (errno != 0) { | 
 |     DVPLOG(1) << "Failed to get nice value of thread (" | 
 |               << PlatformThread::CurrentId() << ")"; | 
 |     return ThreadPriority::NORMAL; | 
 |   } | 
 |  | 
 |   return internal::NiceValueToThreadPriority(nice_value); | 
 | #endif  // !defined(OS_NACL) | 
 | } | 
 |  | 
 | #endif  // !defined(OS_MACOSX) && !defined(OS_FUCHSIA) | 
 |  | 
 | }  // namespace base |