• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "partition_alloc/partition_alloc_base/threading/platform_thread.h"
6 
7 #include <errno.h>
8 #include <pthread.h>
9 #include <stddef.h>
10 #include <stdint.h>
11 #include <sys/time.h>
12 #include <sys/types.h>
13 #include <unistd.h>
14 
15 #include "build/build_config.h"
16 #include "partition_alloc/partition_alloc_base/debug/debugging_buildflags.h"
17 #include "partition_alloc/partition_alloc_base/logging.h"
18 #include "partition_alloc/partition_alloc_base/threading/platform_thread_internal_posix.h"
19 
20 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
21 #include <sys/syscall.h>
22 #include <atomic>
23 #endif
24 
25 #if BUILDFLAG(IS_FUCHSIA)
26 #include <zircon/process.h>
27 #endif
28 
29 namespace partition_alloc::internal::base {
30 
31 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
32 
33 namespace {
34 
35 // Store the thread ids in local storage since calling the SWI can be
36 // expensive and PlatformThread::CurrentId is used liberally.
37 thread_local pid_t g_thread_id = -1;
38 
39 // A boolean value that indicates that the value stored in |g_thread_id| on the
40 // main thread is invalid, because it hasn't been updated since the process
41 // forked.
42 //
43 // This used to work by setting |g_thread_id| to -1 in a pthread_atfork handler.
44 // However, when a multithreaded process forks, it is only allowed to call
45 // async-signal-safe functions until it calls an exec() syscall. However,
46 // accessing TLS may allocate (see crbug.com/1275748), which is not
47 // async-signal-safe and therefore causes deadlocks, corruption, and crashes.
48 //
49 // It's Atomic to placate TSAN.
50 std::atomic<bool> g_main_thread_tid_cache_valid = false;
51 
52 // Tracks whether the current thread is the main thread, and therefore whether
53 // |g_main_thread_tid_cache_valid| is relevant for the current thread. This is
54 // also updated by PlatformThread::CurrentId().
55 thread_local bool g_is_main_thread = true;
56 
57 class InitAtFork {
58  public:
InitAtFork()59   InitAtFork() {
60     pthread_atfork(nullptr, nullptr, internal::InvalidateTidCache);
61   }
62 };
63 
64 }  // namespace
65 
66 namespace internal {
67 
InvalidateTidCache()68 void InvalidateTidCache() {
69   g_main_thread_tid_cache_valid.store(false, std::memory_order_relaxed);
70 }
71 
72 }  // namespace internal
73 
74 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
75 
76 // static
CurrentId()77 PlatformThreadId PlatformThread::CurrentId() {
78   // Pthreads doesn't have the concept of a thread ID, so we have to reach down
79   // into the kernel.
80 #if BUILDFLAG(IS_APPLE)
81   return pthread_mach_thread_np(pthread_self());
82 #elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
83   static InitAtFork init_at_fork;
84   if (g_thread_id == -1 ||
85       (g_is_main_thread &&
86        !g_main_thread_tid_cache_valid.load(std::memory_order_relaxed))) {
87     // Update the cached tid.
88     g_thread_id = syscall(__NR_gettid);
89     // If this is the main thread, we can mark the tid_cache as valid.
90     // Otherwise, stop the current thread from always entering this slow path.
91     if (g_thread_id == getpid()) {
92       g_main_thread_tid_cache_valid.store(true, std::memory_order_relaxed);
93     } else {
94       g_is_main_thread = false;
95     }
96   } else {
97 #if BUILDFLAG(PA_DCHECK_IS_ON)
98     if (g_thread_id != syscall(__NR_gettid)) {
99       PA_RAW_LOG(
100           FATAL,
101           "Thread id stored in TLS is different from thread id returned by "
102           "the system. It is likely that the process was forked without going "
103           "through fork().");
104     }
105 #endif
106   }
107   return g_thread_id;
108 #elif BUILDFLAG(IS_ANDROID)
109   // Note: do not cache the return value inside a thread_local variable on
110   // Android (as above). The reasons are:
111   // - thread_local is slow on Android (goes through emutls)
112   // - gettid() is fast, since its return value is cached in pthread (in the
113   //   thread control block of pthread). See gettid.c in bionic.
114   return gettid();
115 #elif BUILDFLAG(IS_FUCHSIA)
116   return zx_thread_self();
117 #elif BUILDFLAG(IS_SOLARIS) || BUILDFLAG(IS_QNX)
118   return pthread_self();
119 #elif BUILDFLAG(IS_POSIX) && BUILDFLAG(IS_AIX)
120   return pthread_self();
121 #elif BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_AIX)
122   return reinterpret_cast<int64_t>(pthread_self());
123 #endif
124 }
125 
126 // static
CurrentRef()127 PlatformThreadRef PlatformThread::CurrentRef() {
128   return PlatformThreadRef(pthread_self());
129 }
130 
131 // static
Sleep(TimeDelta duration)132 void PlatformThread::Sleep(TimeDelta duration) {
133   struct timespec sleep_time, remaining;
134 
135   // Break the duration into seconds and nanoseconds.
136   // NOTE: TimeDelta's microseconds are int64s while timespec's
137   // nanoseconds are longs, so this unpacking must prevent overflow.
138   sleep_time.tv_sec = duration.InSeconds();
139   duration -= Seconds(sleep_time.tv_sec);
140   sleep_time.tv_nsec = duration.InMicroseconds() * 1000;  // nanoseconds
141 
142   while (nanosleep(&sleep_time, &remaining) == -1 && errno == EINTR) {
143     sleep_time = remaining;
144   }
145 }
146 
147 }  // namespace partition_alloc::internal::base
148