/src/rocksdb/port/port_posix.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
2 | | // This source code is licensed under both the GPLv2 (found in the |
3 | | // COPYING file in the root directory) and Apache 2.0 License |
4 | | // (found in the LICENSE.Apache file in the root directory). |
5 | | // |
6 | | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. |
7 | | // Use of this source code is governed by a BSD-style license that can be |
8 | | // found in the LICENSE file. See the AUTHORS file for names of contributors. |
9 | | |
10 | | #if !defined(OS_WIN) |
11 | | |
12 | | #include "port/port_posix.h" |
13 | | |
14 | | #include <cassert> |
15 | | #if defined(__i386__) || defined(__x86_64__) |
16 | | #include <cpuid.h> |
17 | | #endif |
18 | | #include <sched.h> |
19 | | #include <sys/resource.h> |
20 | | #include <sys/time.h> |
21 | | #include <unistd.h> |
22 | | |
23 | | #include <cerrno> |
24 | | #include <csignal> |
25 | | #include <cstdio> |
26 | | #include <cstdlib> |
27 | | #include <cstring> |
28 | | #include <fstream> |
29 | | #include <string> |
30 | | |
31 | | #include "util/string_util.h" |
32 | | |
33 | | namespace ROCKSDB_NAMESPACE { |
34 | | |
35 | | // We want to give users opportunity to default all the mutexes to adaptive if |
36 | | // not specified otherwise. This enables a quick way to conduct various |
37 | | // performance related experiements. |
38 | | // |
39 | | // NB! Support for adaptive mutexes is turned on by definining |
40 | | // ROCKSDB_PTHREAD_ADAPTIVE_MUTEX during the compilation. If you use RocksDB |
41 | | // build environment then this happens automatically; otherwise it's up to the |
42 | | // consumer to define the identifier. |
43 | | #ifdef ROCKSDB_DEFAULT_TO_ADAPTIVE_MUTEX |
44 | | const bool kDefaultToAdaptiveMutex = true; |
45 | | #else |
46 | | const bool kDefaultToAdaptiveMutex = false; |
47 | | #endif |
48 | | |
49 | | namespace port { |
50 | | |
51 | 479M | static int PthreadCall(const char* label, int result) { |
52 | 479M | if (result != 0 && result != ETIMEDOUT && result != EBUSY) { |
53 | 0 | fprintf(stderr, "pthread %s: %s\n", label, errnoStr(result).c_str()); |
54 | 0 | abort(); |
55 | 0 | } |
56 | 479M | return result; |
57 | 479M | } |
58 | | |
59 | 115M | Mutex::Mutex(bool adaptive) { |
60 | 115M | (void)adaptive; |
61 | 115M | #ifdef ROCKSDB_PTHREAD_ADAPTIVE_MUTEX |
62 | 115M | if (!adaptive) { |
63 | 115M | PthreadCall("init mutex", pthread_mutex_init(&mu_, nullptr)); |
64 | 115M | } else { |
65 | 0 | pthread_mutexattr_t mutex_attr; |
66 | 0 | PthreadCall("init mutex attr", pthread_mutexattr_init(&mutex_attr)); |
67 | 0 | PthreadCall("set mutex attr", pthread_mutexattr_settype( |
68 | 0 | &mutex_attr, PTHREAD_MUTEX_ADAPTIVE_NP)); |
69 | 0 | PthreadCall("init mutex", pthread_mutex_init(&mu_, &mutex_attr)); |
70 | 0 | PthreadCall("destroy mutex attr", pthread_mutexattr_destroy(&mutex_attr)); |
71 | 0 | } |
72 | | #else |
73 | | PthreadCall("init mutex", pthread_mutex_init(&mu_, nullptr)); |
74 | | #endif // ROCKSDB_PTHREAD_ADAPTIVE_MUTEX |
75 | 115M | } |
76 | | |
77 | 115M | Mutex::~Mutex() { PthreadCall("destroy mutex", pthread_mutex_destroy(&mu_)); } |
78 | | |
79 | 123M | void Mutex::Lock() { |
80 | 123M | PthreadCall("lock", pthread_mutex_lock(&mu_)); |
81 | | #ifndef NDEBUG |
82 | | locked_ = true; |
83 | | #endif |
84 | 123M | } |
85 | | |
86 | 123M | void Mutex::Unlock() { |
87 | | #ifndef NDEBUG |
88 | | locked_ = false; |
89 | | #endif |
90 | 123M | PthreadCall("unlock", pthread_mutex_unlock(&mu_)); |
91 | 123M | } |
92 | | |
93 | 0 | bool Mutex::TryLock() { |
94 | 0 | bool ret = PthreadCall("trylock", pthread_mutex_trylock(&mu_)) == 0; |
95 | | #ifndef NDEBUG |
96 | | if (ret) { |
97 | | locked_ = true; |
98 | | } |
99 | | #endif |
100 | 0 | return ret; |
101 | 0 | } |
102 | | |
103 | 3.62M | void Mutex::AssertHeld() const { |
104 | | #ifndef NDEBUG |
105 | | assert(locked_); |
106 | | #endif |
107 | 3.62M | } |
108 | | |
109 | 737k | CondVar::CondVar(Mutex* mu) : mu_(mu) { |
110 | 737k | PthreadCall("init cv", pthread_cond_init(&cv_, nullptr)); |
111 | 737k | } |
112 | | |
113 | 737k | CondVar::~CondVar() { PthreadCall("destroy cv", pthread_cond_destroy(&cv_)); } |
114 | | |
115 | 11.0k | void CondVar::Wait() { |
116 | | #ifndef NDEBUG |
117 | | mu_->locked_ = false; |
118 | | #endif |
119 | 11.0k | PthreadCall("wait", pthread_cond_wait(&cv_, &mu_->mu_)); |
120 | | #ifndef NDEBUG |
121 | | mu_->locked_ = true; |
122 | | #endif |
123 | 11.0k | } |
124 | | |
125 | 56.0k | bool CondVar::TimedWait(uint64_t abs_time_us) { |
126 | 56.0k | struct timespec ts; |
127 | 56.0k | ts.tv_sec = static_cast<time_t>(abs_time_us / 1000000); |
128 | 56.0k | ts.tv_nsec = static_cast<suseconds_t>((abs_time_us % 1000000) * 1000); |
129 | | |
130 | | #ifndef NDEBUG |
131 | | mu_->locked_ = false; |
132 | | #endif |
133 | 56.0k | int err = pthread_cond_timedwait(&cv_, &mu_->mu_, &ts); |
134 | | #ifndef NDEBUG |
135 | | mu_->locked_ = true; |
136 | | #endif |
137 | 56.0k | if (err == ETIMEDOUT) { |
138 | 0 | return true; |
139 | 0 | } |
140 | 56.0k | if (err != 0) { |
141 | 0 | PthreadCall("timedwait", err); |
142 | 0 | } |
143 | 56.0k | return false; |
144 | 56.0k | } |
145 | | |
146 | 59.4k | void CondVar::Signal() { PthreadCall("signal", pthread_cond_signal(&cv_)); } |
147 | | |
148 | 664k | void CondVar::SignalAll() { |
149 | 664k | PthreadCall("broadcast", pthread_cond_broadcast(&cv_)); |
150 | 664k | } |
151 | | |
152 | 4 | RWMutex::RWMutex() { |
153 | 4 | PthreadCall("init mutex", pthread_rwlock_init(&mu_, nullptr)); |
154 | 4 | } |
155 | | |
156 | 0 | RWMutex::~RWMutex() { |
157 | 0 | PthreadCall("destroy mutex", pthread_rwlock_destroy(&mu_)); |
158 | 0 | } |
159 | | |
160 | 118k | void RWMutex::ReadLock() { |
161 | 118k | PthreadCall("read lock", pthread_rwlock_rdlock(&mu_)); |
162 | 118k | } |
163 | | |
164 | 237k | void RWMutex::WriteLock() { |
165 | 237k | PthreadCall("write lock", pthread_rwlock_wrlock(&mu_)); |
166 | 237k | } |
167 | | |
168 | 118k | void RWMutex::ReadUnlock() { |
169 | 118k | PthreadCall("read unlock", pthread_rwlock_unlock(&mu_)); |
170 | 118k | } |
171 | | |
172 | 237k | void RWMutex::WriteUnlock() { |
173 | 237k | PthreadCall("write unlock", pthread_rwlock_unlock(&mu_)); |
174 | 237k | } |
175 | | |
176 | 3.72k | int PhysicalCoreID() { |
177 | 3.72k | #if defined(ROCKSDB_SCHED_GETCPU_PRESENT) && defined(__x86_64__) && \ |
178 | 3.72k | (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 22)) |
179 | | // sched_getcpu uses VDSO getcpu() syscall since 2.22. I believe Linux offers |
180 | | // VDSO support only on x86_64. This is the fastest/preferred method if |
181 | | // available. |
182 | 3.72k | int cpuno = sched_getcpu(); |
183 | 3.72k | if (cpuno < 0) { |
184 | 0 | return -1; |
185 | 0 | } |
186 | 3.72k | return cpuno; |
187 | | #elif defined(__x86_64__) || defined(__i386__) |
188 | | // clang/gcc both provide cpuid.h, which defines __get_cpuid(), for x86_64 and |
189 | | // i386. |
190 | | unsigned eax, ebx = 0, ecx, edx; |
191 | | if (!__get_cpuid(1, &eax, &ebx, &ecx, &edx)) { |
192 | | return -1; |
193 | | } |
194 | | return ebx >> 24; |
195 | | #else |
196 | | // give up, the caller can generate a random number or something. |
197 | | return -1; |
198 | | #endif |
199 | 3.72k | } |
200 | | |
201 | 0 | void InitOnce(OnceType* once, void (*initializer)()) { |
202 | 0 | PthreadCall("once", pthread_once(once, initializer)); |
203 | 0 | } |
204 | | |
205 | 0 | void Crash(const std::string& srcfile, int srcline) { |
206 | 0 | fprintf(stdout, "Crashing at %s:%d\n", srcfile.c_str(), srcline); |
207 | 0 | fflush(stdout); |
208 | 0 | kill(getpid(), SIGTERM); |
209 | 0 | } |
210 | | |
211 | 0 | int GetMaxOpenFiles() { |
212 | 0 | #if defined(RLIMIT_NOFILE) |
213 | 0 | struct rlimit no_files_limit; |
214 | 0 | if (getrlimit(RLIMIT_NOFILE, &no_files_limit) != 0) { |
215 | 0 | return -1; |
216 | 0 | } |
217 | | // protect against overflow |
218 | 0 | if (static_cast<uintmax_t>(no_files_limit.rlim_cur) >= |
219 | 0 | static_cast<uintmax_t>(std::numeric_limits<int>::max())) { |
220 | 0 | return std::numeric_limits<int>::max(); |
221 | 0 | } |
222 | 0 | return static_cast<int>(no_files_limit.rlim_cur); |
223 | | #else |
224 | | return -1; |
225 | | #endif |
226 | 0 | } |
227 | | |
228 | 1.26M | void* cacheline_aligned_alloc(size_t size) { |
229 | | #if __GNUC__ < 5 && defined(__SANITIZE_ADDRESS__) |
230 | | return malloc(size); |
231 | | #elif (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600 || defined(__APPLE__)) |
232 | | void* m; |
233 | 1.26M | errno = posix_memalign(&m, CACHE_LINE_SIZE, size); |
234 | 1.26M | return errno ? nullptr : m; |
235 | | #else |
236 | | return malloc(size); |
237 | | #endif |
238 | 1.26M | } |
239 | | |
240 | 1.26M | void cacheline_aligned_free(void* memblock) { free(memblock); } |
241 | | |
242 | 4 | static size_t GetPageSize() { |
243 | 4 | #if defined(OS_LINUX) || defined(_SC_PAGESIZE) |
244 | 4 | long v = sysconf(_SC_PAGESIZE); |
245 | 4 | if (v >= 1024) { |
246 | 4 | return static_cast<size_t>(v); |
247 | 4 | } |
248 | 0 | #endif |
249 | | // Default assume 4KB |
250 | 0 | return 4U * 1024U; |
251 | 4 | } |
252 | | |
253 | | const size_t kPageSize = GetPageSize(); |
254 | | |
255 | 0 | void SetCpuPriority(ThreadId id, CpuPriority priority) { |
256 | 0 | #ifdef OS_LINUX |
257 | 0 | sched_param param; |
258 | 0 | param.sched_priority = 0; |
259 | 0 | switch (priority) { |
260 | 0 | case CpuPriority::kHigh: |
261 | 0 | sched_setscheduler(id, SCHED_OTHER, ¶m); |
262 | 0 | setpriority(PRIO_PROCESS, id, -20); |
263 | 0 | break; |
264 | 0 | case CpuPriority::kNormal: |
265 | 0 | sched_setscheduler(id, SCHED_OTHER, ¶m); |
266 | 0 | setpriority(PRIO_PROCESS, id, 0); |
267 | 0 | break; |
268 | 0 | case CpuPriority::kLow: |
269 | 0 | sched_setscheduler(id, SCHED_OTHER, ¶m); |
270 | 0 | setpriority(PRIO_PROCESS, id, 19); |
271 | 0 | break; |
272 | 0 | case CpuPriority::kIdle: |
273 | 0 | sched_setscheduler(id, SCHED_IDLE, ¶m); |
274 | 0 | break; |
275 | 0 | default: |
276 | 0 | assert(false); |
277 | 0 | } |
278 | | #else |
279 | | (void)id; |
280 | | (void)priority; |
281 | | #endif |
282 | 0 | } |
283 | | |
284 | 58.8k | int64_t GetProcessID() { return getpid(); } |
285 | | |
286 | 10.2k | bool GenerateRfcUuid(std::string* output) { |
287 | 10.2k | output->clear(); |
288 | 10.2k | std::ifstream f("/proc/sys/kernel/random/uuid"); |
289 | 10.2k | std::getline(f, /*&*/ *output); |
290 | 10.2k | if (output->size() == 36) { |
291 | 10.2k | return true; |
292 | 10.2k | } else { |
293 | 0 | output->clear(); |
294 | 0 | return false; |
295 | 0 | } |
296 | 10.2k | } |
297 | | |
298 | | } // namespace port |
299 | | } // namespace ROCKSDB_NAMESPACE |
300 | | |
301 | | #endif |