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