1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Platform-specific code for POSIX goes here. This is not a platform on its
6 // own, but contains the parts which are the same across the POSIX platforms
7 // Linux, MacOS, FreeBSD, OpenBSD, NetBSD and QNX.
8
9 #include <errno.h>
10 #include <limits.h>
11 #include <pthread.h>
12 #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
13 #include <pthread_np.h> // for pthread_set_name_np
14 #endif
15 #include <sched.h> // for sched_yield
16 #include <stdio.h>
17 #include <time.h>
18 #include <unistd.h>
19
20 #include <sys/mman.h>
21 #include <sys/resource.h>
22 #include <sys/stat.h>
23 #include <sys/time.h>
24 #include <sys/types.h>
25 #if defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__) || \
26 defined(__NetBSD__) || defined(__OpenBSD__)
27 #include <sys/sysctl.h> // NOLINT, for sysctl
28 #endif
29
30 #undef MAP_TYPE
31
32 #if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
33 #define LOG_TAG "v8"
34 #include <android/log.h> // NOLINT
35 #endif
36
37 #include <cmath>
38 #include <cstdlib>
39
40 #include "src/base/lazy-instance.h"
41 #include "src/base/macros.h"
42 #include "src/base/platform/platform.h"
43 #include "src/base/platform/time.h"
44 #include "src/base/utils/random-number-generator.h"
45
46 #ifdef V8_FAST_TLS_SUPPORTED
47 #include "src/base/atomicops.h"
48 #endif
49
50 #if V8_OS_MACOSX
51 #include <dlfcn.h>
52 #endif
53
54 #if V8_OS_LINUX
55 #include <sys/prctl.h> // NOLINT, for prctl
56 #endif
57
58 #ifndef _AIX
59 #include <sys/syscall.h>
60 #endif
61
62 namespace v8 {
63 namespace base {
64
65 namespace {
66
67 // 0 is never a valid thread id.
68 const pthread_t kNoThread = (pthread_t) 0;
69
70 bool g_hard_abort = false;
71
72 const char* g_gc_fake_mmap = NULL;
73
74 } // namespace
75
76
ActivationFrameAlignment()77 int OS::ActivationFrameAlignment() {
78 #if V8_TARGET_ARCH_ARM
79 // On EABI ARM targets this is required for fp correctness in the
80 // runtime system.
81 return 8;
82 #elif V8_TARGET_ARCH_MIPS
83 return 8;
84 #elif V8_TARGET_ARCH_S390
85 return 8;
86 #else
87 // Otherwise we just assume 16 byte alignment, i.e.:
88 // - With gcc 4.4 the tree vectorization optimizer can generate code
89 // that requires 16 byte alignment such as movdqa on x86.
90 // - Mac OS X, PPC and Solaris (64-bit) activation frames must
91 // be 16 byte-aligned; see "Mac OS X ABI Function Call Guide"
92 return 16;
93 #endif
94 }
95
96
CommitPageSize()97 intptr_t OS::CommitPageSize() {
98 static intptr_t page_size = getpagesize();
99 return page_size;
100 }
101
AllocateGuarded(const size_t requested)102 void* OS::AllocateGuarded(const size_t requested) {
103 size_t allocated = 0;
104 const bool is_executable = false;
105 void* mbase = OS::Allocate(requested, &allocated, is_executable);
106 if (allocated != requested) {
107 OS::Free(mbase, allocated);
108 return nullptr;
109 }
110 if (mbase == nullptr) {
111 return nullptr;
112 }
113 OS::Guard(mbase, requested);
114 return mbase;
115 }
116
Free(void * address,const size_t size)117 void OS::Free(void* address, const size_t size) {
118 // TODO(1240712): munmap has a return value which is ignored here.
119 int result = munmap(address, size);
120 USE(result);
121 DCHECK(result == 0);
122 }
123
124
125 // Get rid of writable permission on code allocations.
ProtectCode(void * address,const size_t size)126 void OS::ProtectCode(void* address, const size_t size) {
127 #if V8_OS_CYGWIN
128 DWORD old_protect;
129 VirtualProtect(address, size, PAGE_EXECUTE_READ, &old_protect);
130 #else
131 mprotect(address, size, PROT_READ | PROT_EXEC);
132 #endif
133 }
134
135
136 // Create guard pages.
Guard(void * address,const size_t size)137 void OS::Guard(void* address, const size_t size) {
138 #if V8_OS_CYGWIN
139 DWORD oldprotect;
140 VirtualProtect(address, size, PAGE_NOACCESS, &oldprotect);
141 #else
142 mprotect(address, size, PROT_NONE);
143 #endif
144 }
145
146 // Make a region of memory readable and writable.
Unprotect(void * address,const size_t size)147 void OS::Unprotect(void* address, const size_t size) {
148 #if V8_OS_CYGWIN
149 DWORD oldprotect;
150 VirtualProtect(address, size, PAGE_READWRITE, &oldprotect);
151 #else
152 mprotect(address, size, PROT_READ | PROT_WRITE);
153 #endif
154 }
155
156 static LazyInstance<RandomNumberGenerator>::type
157 platform_random_number_generator = LAZY_INSTANCE_INITIALIZER;
158
159
Initialize(int64_t random_seed,bool hard_abort,const char * const gc_fake_mmap)160 void OS::Initialize(int64_t random_seed, bool hard_abort,
161 const char* const gc_fake_mmap) {
162 if (random_seed) {
163 platform_random_number_generator.Pointer()->SetSeed(random_seed);
164 }
165 g_hard_abort = hard_abort;
166 g_gc_fake_mmap = gc_fake_mmap;
167 }
168
169
GetGCFakeMMapFile()170 const char* OS::GetGCFakeMMapFile() {
171 return g_gc_fake_mmap;
172 }
173
174
GetRandomMmapAddr()175 void* OS::GetRandomMmapAddr() {
176 #if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
177 defined(THREAD_SANITIZER)
178 // Dynamic tools do not support custom mmap addresses.
179 return NULL;
180 #endif
181 uintptr_t raw_addr;
182 platform_random_number_generator.Pointer()->NextBytes(&raw_addr,
183 sizeof(raw_addr));
184 #if V8_TARGET_ARCH_X64
185 // Currently available CPUs have 48 bits of virtual addressing. Truncate
186 // the hint address to 46 bits to give the kernel a fighting chance of
187 // fulfilling our placement request.
188 raw_addr &= V8_UINT64_C(0x3ffffffff000);
189 #elif V8_TARGET_ARCH_PPC64
190 #if V8_OS_AIX
191 // AIX: 64 bits of virtual addressing, but we limit address range to:
192 // a) minimize Segment Lookaside Buffer (SLB) misses and
193 raw_addr &= V8_UINT64_C(0x3ffff000);
194 // Use extra address space to isolate the mmap regions.
195 raw_addr += V8_UINT64_C(0x400000000000);
196 #elif V8_TARGET_BIG_ENDIAN
197 // Big-endian Linux: 44 bits of virtual addressing.
198 raw_addr &= V8_UINT64_C(0x03fffffff000);
199 #else
200 // Little-endian Linux: 48 bits of virtual addressing.
201 raw_addr &= V8_UINT64_C(0x3ffffffff000);
202 #endif
203 #elif V8_TARGET_ARCH_S390X
204 // Linux on Z uses bits 22-32 for Region Indexing, which translates to 42 bits
205 // of virtual addressing. Truncate to 40 bits to allow kernel chance to
206 // fulfill request.
207 raw_addr &= V8_UINT64_C(0xfffffff000);
208 #elif V8_TARGET_ARCH_S390
209 // 31 bits of virtual addressing. Truncate to 29 bits to allow kernel chance
210 // to fulfill request.
211 raw_addr &= 0x1ffff000;
212 #else
213 raw_addr &= 0x3ffff000;
214
215 # ifdef __sun
216 // For our Solaris/illumos mmap hint, we pick a random address in the bottom
217 // half of the top half of the address space (that is, the third quarter).
218 // Because we do not MAP_FIXED, this will be treated only as a hint -- the
219 // system will not fail to mmap() because something else happens to already
220 // be mapped at our random address. We deliberately set the hint high enough
221 // to get well above the system's break (that is, the heap); Solaris and
222 // illumos will try the hint and if that fails allocate as if there were
223 // no hint at all. The high hint prevents the break from getting hemmed in
224 // at low values, ceding half of the address space to the system heap.
225 raw_addr += 0x80000000;
226 #elif V8_OS_AIX
227 // The range 0x30000000 - 0xD0000000 is available on AIX;
228 // choose the upper range.
229 raw_addr += 0x90000000;
230 # else
231 // The range 0x20000000 - 0x60000000 is relatively unpopulated across a
232 // variety of ASLR modes (PAE kernel, NX compat mode, etc) and on macos
233 // 10.6 and 10.7.
234 raw_addr += 0x20000000;
235 # endif
236 #endif
237 return reinterpret_cast<void*>(raw_addr);
238 }
239
240
AllocateAlignment()241 size_t OS::AllocateAlignment() {
242 return static_cast<size_t>(sysconf(_SC_PAGESIZE));
243 }
244
245
Sleep(TimeDelta interval)246 void OS::Sleep(TimeDelta interval) {
247 usleep(static_cast<useconds_t>(interval.InMicroseconds()));
248 }
249
250
Abort()251 void OS::Abort() {
252 if (g_hard_abort) {
253 V8_IMMEDIATE_CRASH();
254 }
255 // Redirect to std abort to signal abnormal program termination.
256 abort();
257 }
258
259
DebugBreak()260 void OS::DebugBreak() {
261 #if V8_HOST_ARCH_ARM
262 asm("bkpt 0");
263 #elif V8_HOST_ARCH_ARM64
264 asm("brk 0");
265 #elif V8_HOST_ARCH_MIPS
266 asm("break");
267 #elif V8_HOST_ARCH_MIPS64
268 asm("break");
269 #elif V8_HOST_ARCH_PPC
270 asm("twge 2,2");
271 #elif V8_HOST_ARCH_IA32
272 asm("int $3");
273 #elif V8_HOST_ARCH_X64
274 asm("int $3");
275 #elif V8_HOST_ARCH_S390
276 // Software breakpoint instruction is 0x0001
277 asm volatile(".word 0x0001");
278 #else
279 #error Unsupported host architecture.
280 #endif
281 }
282
283
284 class PosixMemoryMappedFile final : public OS::MemoryMappedFile {
285 public:
PosixMemoryMappedFile(FILE * file,void * memory,size_t size)286 PosixMemoryMappedFile(FILE* file, void* memory, size_t size)
287 : file_(file), memory_(memory), size_(size) {}
288 ~PosixMemoryMappedFile() final;
memory() const289 void* memory() const final { return memory_; }
size() const290 size_t size() const final { return size_; }
291
292 private:
293 FILE* const file_;
294 void* const memory_;
295 size_t const size_;
296 };
297
298
299 // static
open(const char * name)300 OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
301 if (FILE* file = fopen(name, "r+")) {
302 if (fseek(file, 0, SEEK_END) == 0) {
303 long size = ftell(file); // NOLINT(runtime/int)
304 if (size >= 0) {
305 void* const memory =
306 mmap(OS::GetRandomMmapAddr(), size, PROT_READ | PROT_WRITE,
307 MAP_SHARED, fileno(file), 0);
308 if (memory != MAP_FAILED) {
309 return new PosixMemoryMappedFile(file, memory, size);
310 }
311 }
312 }
313 fclose(file);
314 }
315 return nullptr;
316 }
317
318
319 // static
create(const char * name,size_t size,void * initial)320 OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name,
321 size_t size, void* initial) {
322 if (FILE* file = fopen(name, "w+")) {
323 size_t result = fwrite(initial, 1, size, file);
324 if (result == size && !ferror(file)) {
325 void* memory = mmap(OS::GetRandomMmapAddr(), result,
326 PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
327 if (memory != MAP_FAILED) {
328 return new PosixMemoryMappedFile(file, memory, result);
329 }
330 }
331 fclose(file);
332 }
333 return nullptr;
334 }
335
336
~PosixMemoryMappedFile()337 PosixMemoryMappedFile::~PosixMemoryMappedFile() {
338 if (memory_) OS::Free(memory_, size_);
339 fclose(file_);
340 }
341
342
GetCurrentProcessId()343 int OS::GetCurrentProcessId() {
344 return static_cast<int>(getpid());
345 }
346
347
GetCurrentThreadId()348 int OS::GetCurrentThreadId() {
349 #if V8_OS_MACOSX || (V8_OS_ANDROID && defined(__APPLE__))
350 return static_cast<int>(pthread_mach_thread_np(pthread_self()));
351 #elif V8_OS_LINUX
352 return static_cast<int>(syscall(__NR_gettid));
353 #elif V8_OS_ANDROID
354 return static_cast<int>(gettid());
355 #elif V8_OS_AIX
356 return static_cast<int>(thread_self());
357 #elif V8_OS_SOLARIS
358 return static_cast<int>(pthread_self());
359 #else
360 return static_cast<int>(reinterpret_cast<intptr_t>(pthread_self()));
361 #endif
362 }
363
364
365 // ----------------------------------------------------------------------------
366 // POSIX date/time support.
367 //
368
GetUserTime(uint32_t * secs,uint32_t * usecs)369 int OS::GetUserTime(uint32_t* secs, uint32_t* usecs) {
370 struct rusage usage;
371
372 if (getrusage(RUSAGE_SELF, &usage) < 0) return -1;
373 *secs = static_cast<uint32_t>(usage.ru_utime.tv_sec);
374 *usecs = static_cast<uint32_t>(usage.ru_utime.tv_usec);
375 return 0;
376 }
377
378
TimeCurrentMillis()379 double OS::TimeCurrentMillis() {
380 return Time::Now().ToJsTime();
381 }
382
383
384 class TimezoneCache {};
385
386
CreateTimezoneCache()387 TimezoneCache* OS::CreateTimezoneCache() {
388 return NULL;
389 }
390
391
DisposeTimezoneCache(TimezoneCache * cache)392 void OS::DisposeTimezoneCache(TimezoneCache* cache) {
393 DCHECK(cache == NULL);
394 }
395
396
ClearTimezoneCache(TimezoneCache * cache)397 void OS::ClearTimezoneCache(TimezoneCache* cache) {
398 DCHECK(cache == NULL);
399 }
400
401
DaylightSavingsOffset(double time,TimezoneCache *)402 double OS::DaylightSavingsOffset(double time, TimezoneCache*) {
403 if (std::isnan(time)) return std::numeric_limits<double>::quiet_NaN();
404 time_t tv = static_cast<time_t>(std::floor(time/msPerSecond));
405 struct tm tm;
406 struct tm* t = localtime_r(&tv, &tm);
407 if (NULL == t) return std::numeric_limits<double>::quiet_NaN();
408 return t->tm_isdst > 0 ? 3600 * msPerSecond : 0;
409 }
410
411
GetLastError()412 int OS::GetLastError() {
413 return errno;
414 }
415
416
417 // ----------------------------------------------------------------------------
418 // POSIX stdio support.
419 //
420
FOpen(const char * path,const char * mode)421 FILE* OS::FOpen(const char* path, const char* mode) {
422 FILE* file = fopen(path, mode);
423 if (file == NULL) return NULL;
424 struct stat file_stat;
425 if (fstat(fileno(file), &file_stat) != 0) return NULL;
426 bool is_regular_file = ((file_stat.st_mode & S_IFREG) != 0);
427 if (is_regular_file) return file;
428 fclose(file);
429 return NULL;
430 }
431
432
Remove(const char * path)433 bool OS::Remove(const char* path) {
434 return (remove(path) == 0);
435 }
436
DirectorySeparator()437 char OS::DirectorySeparator() { return '/'; }
438
isDirectorySeparator(const char ch)439 bool OS::isDirectorySeparator(const char ch) {
440 return ch == DirectorySeparator();
441 }
442
443
OpenTemporaryFile()444 FILE* OS::OpenTemporaryFile() {
445 return tmpfile();
446 }
447
448
449 const char* const OS::LogFileOpenMode = "w";
450
451
Print(const char * format,...)452 void OS::Print(const char* format, ...) {
453 va_list args;
454 va_start(args, format);
455 VPrint(format, args);
456 va_end(args);
457 }
458
459
VPrint(const char * format,va_list args)460 void OS::VPrint(const char* format, va_list args) {
461 #if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
462 __android_log_vprint(ANDROID_LOG_INFO, LOG_TAG, format, args);
463 #else
464 vprintf(format, args);
465 #endif
466 }
467
468
FPrint(FILE * out,const char * format,...)469 void OS::FPrint(FILE* out, const char* format, ...) {
470 va_list args;
471 va_start(args, format);
472 VFPrint(out, format, args);
473 va_end(args);
474 }
475
476
VFPrint(FILE * out,const char * format,va_list args)477 void OS::VFPrint(FILE* out, const char* format, va_list args) {
478 #if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
479 __android_log_vprint(ANDROID_LOG_INFO, LOG_TAG, format, args);
480 #else
481 vfprintf(out, format, args);
482 #endif
483 }
484
485
PrintError(const char * format,...)486 void OS::PrintError(const char* format, ...) {
487 va_list args;
488 va_start(args, format);
489 VPrintError(format, args);
490 va_end(args);
491 }
492
493
VPrintError(const char * format,va_list args)494 void OS::VPrintError(const char* format, va_list args) {
495 #if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
496 __android_log_vprint(ANDROID_LOG_ERROR, LOG_TAG, format, args);
497 #else
498 vfprintf(stderr, format, args);
499 #endif
500 }
501
502
SNPrintF(char * str,int length,const char * format,...)503 int OS::SNPrintF(char* str, int length, const char* format, ...) {
504 va_list args;
505 va_start(args, format);
506 int result = VSNPrintF(str, length, format, args);
507 va_end(args);
508 return result;
509 }
510
511
VSNPrintF(char * str,int length,const char * format,va_list args)512 int OS::VSNPrintF(char* str,
513 int length,
514 const char* format,
515 va_list args) {
516 int n = vsnprintf(str, length, format, args);
517 if (n < 0 || n >= length) {
518 // If the length is zero, the assignment fails.
519 if (length > 0)
520 str[length - 1] = '\0';
521 return -1;
522 } else {
523 return n;
524 }
525 }
526
527
528 // ----------------------------------------------------------------------------
529 // POSIX string support.
530 //
531
StrChr(char * str,int c)532 char* OS::StrChr(char* str, int c) {
533 return strchr(str, c);
534 }
535
536
StrNCpy(char * dest,int length,const char * src,size_t n)537 void OS::StrNCpy(char* dest, int length, const char* src, size_t n) {
538 strncpy(dest, src, n);
539 }
540
541
542 // ----------------------------------------------------------------------------
543 // POSIX thread support.
544 //
545
546 class Thread::PlatformData {
547 public:
PlatformData()548 PlatformData() : thread_(kNoThread) {}
549 pthread_t thread_; // Thread handle for pthread.
550 // Synchronizes thread creation
551 Mutex thread_creation_mutex_;
552 };
553
Thread(const Options & options)554 Thread::Thread(const Options& options)
555 : data_(new PlatformData),
556 stack_size_(options.stack_size()),
557 start_semaphore_(NULL) {
558 if (stack_size_ > 0 && static_cast<size_t>(stack_size_) < PTHREAD_STACK_MIN) {
559 stack_size_ = PTHREAD_STACK_MIN;
560 }
561 set_name(options.name());
562 }
563
564
~Thread()565 Thread::~Thread() {
566 delete data_;
567 }
568
569
SetThreadName(const char * name)570 static void SetThreadName(const char* name) {
571 #if V8_OS_DRAGONFLYBSD || V8_OS_FREEBSD || V8_OS_OPENBSD
572 pthread_set_name_np(pthread_self(), name);
573 #elif V8_OS_NETBSD
574 STATIC_ASSERT(Thread::kMaxThreadNameLength <= PTHREAD_MAX_NAMELEN_NP);
575 pthread_setname_np(pthread_self(), "%s", name);
576 #elif V8_OS_MACOSX
577 // pthread_setname_np is only available in 10.6 or later, so test
578 // for it at runtime.
579 int (*dynamic_pthread_setname_np)(const char*);
580 *reinterpret_cast<void**>(&dynamic_pthread_setname_np) =
581 dlsym(RTLD_DEFAULT, "pthread_setname_np");
582 if (dynamic_pthread_setname_np == NULL)
583 return;
584
585 // Mac OS X does not expose the length limit of the name, so hardcode it.
586 static const int kMaxNameLength = 63;
587 STATIC_ASSERT(Thread::kMaxThreadNameLength <= kMaxNameLength);
588 dynamic_pthread_setname_np(name);
589 #elif defined(PR_SET_NAME)
590 prctl(PR_SET_NAME,
591 reinterpret_cast<unsigned long>(name), // NOLINT
592 0, 0, 0);
593 #endif
594 }
595
596
ThreadEntry(void * arg)597 static void* ThreadEntry(void* arg) {
598 Thread* thread = reinterpret_cast<Thread*>(arg);
599 // We take the lock here to make sure that pthread_create finished first since
600 // we don't know which thread will run first (the original thread or the new
601 // one).
602 { LockGuard<Mutex> lock_guard(&thread->data()->thread_creation_mutex_); }
603 SetThreadName(thread->name());
604 DCHECK(thread->data()->thread_ != kNoThread);
605 thread->NotifyStartedAndRun();
606 return NULL;
607 }
608
609
set_name(const char * name)610 void Thread::set_name(const char* name) {
611 strncpy(name_, name, sizeof(name_));
612 name_[sizeof(name_) - 1] = '\0';
613 }
614
615
Start()616 void Thread::Start() {
617 int result;
618 pthread_attr_t attr;
619 memset(&attr, 0, sizeof(attr));
620 result = pthread_attr_init(&attr);
621 DCHECK_EQ(0, result);
622 size_t stack_size = stack_size_;
623 if (stack_size == 0) {
624 #if V8_OS_MACOSX
625 // Default on Mac OS X is 512kB -- bump up to 1MB
626 stack_size = 1 * 1024 * 1024;
627 #elif V8_OS_AIX
628 // Default on AIX is 96kB -- bump up to 2MB
629 stack_size = 2 * 1024 * 1024;
630 #endif
631 }
632 if (stack_size > 0) {
633 result = pthread_attr_setstacksize(&attr, stack_size);
634 DCHECK_EQ(0, result);
635 }
636 {
637 LockGuard<Mutex> lock_guard(&data_->thread_creation_mutex_);
638 result = pthread_create(&data_->thread_, &attr, ThreadEntry, this);
639 }
640 DCHECK_EQ(0, result);
641 result = pthread_attr_destroy(&attr);
642 DCHECK_EQ(0, result);
643 DCHECK(data_->thread_ != kNoThread);
644 USE(result);
645 }
646
647
Join()648 void Thread::Join() {
649 pthread_join(data_->thread_, NULL);
650 }
651
652
PthreadKeyToLocalKey(pthread_key_t pthread_key)653 static Thread::LocalStorageKey PthreadKeyToLocalKey(pthread_key_t pthread_key) {
654 #if V8_OS_CYGWIN
655 // We need to cast pthread_key_t to Thread::LocalStorageKey in two steps
656 // because pthread_key_t is a pointer type on Cygwin. This will probably not
657 // work on 64-bit platforms, but Cygwin doesn't support 64-bit anyway.
658 STATIC_ASSERT(sizeof(Thread::LocalStorageKey) == sizeof(pthread_key_t));
659 intptr_t ptr_key = reinterpret_cast<intptr_t>(pthread_key);
660 return static_cast<Thread::LocalStorageKey>(ptr_key);
661 #else
662 return static_cast<Thread::LocalStorageKey>(pthread_key);
663 #endif
664 }
665
666
LocalKeyToPthreadKey(Thread::LocalStorageKey local_key)667 static pthread_key_t LocalKeyToPthreadKey(Thread::LocalStorageKey local_key) {
668 #if V8_OS_CYGWIN
669 STATIC_ASSERT(sizeof(Thread::LocalStorageKey) == sizeof(pthread_key_t));
670 intptr_t ptr_key = static_cast<intptr_t>(local_key);
671 return reinterpret_cast<pthread_key_t>(ptr_key);
672 #else
673 return static_cast<pthread_key_t>(local_key);
674 #endif
675 }
676
677
678 #ifdef V8_FAST_TLS_SUPPORTED
679
680 static Atomic32 tls_base_offset_initialized = 0;
681 intptr_t kMacTlsBaseOffset = 0;
682
683 // It's safe to do the initialization more that once, but it has to be
684 // done at least once.
InitializeTlsBaseOffset()685 static void InitializeTlsBaseOffset() {
686 const size_t kBufferSize = 128;
687 char buffer[kBufferSize];
688 size_t buffer_size = kBufferSize;
689 int ctl_name[] = { CTL_KERN , KERN_OSRELEASE };
690 if (sysctl(ctl_name, 2, buffer, &buffer_size, NULL, 0) != 0) {
691 V8_Fatal(__FILE__, __LINE__, "V8 failed to get kernel version");
692 }
693 // The buffer now contains a string of the form XX.YY.ZZ, where
694 // XX is the major kernel version component.
695 // Make sure the buffer is 0-terminated.
696 buffer[kBufferSize - 1] = '\0';
697 char* period_pos = strchr(buffer, '.');
698 *period_pos = '\0';
699 int kernel_version_major =
700 static_cast<int>(strtol(buffer, NULL, 10)); // NOLINT
701 // The constants below are taken from pthreads.s from the XNU kernel
702 // sources archive at www.opensource.apple.com.
703 if (kernel_version_major < 11) {
704 // 8.x.x (Tiger), 9.x.x (Leopard), 10.x.x (Snow Leopard) have the
705 // same offsets.
706 #if V8_HOST_ARCH_IA32
707 kMacTlsBaseOffset = 0x48;
708 #else
709 kMacTlsBaseOffset = 0x60;
710 #endif
711 } else {
712 // 11.x.x (Lion) changed the offset.
713 kMacTlsBaseOffset = 0;
714 }
715
716 Release_Store(&tls_base_offset_initialized, 1);
717 }
718
719
CheckFastTls(Thread::LocalStorageKey key)720 static void CheckFastTls(Thread::LocalStorageKey key) {
721 void* expected = reinterpret_cast<void*>(0x1234CAFE);
722 Thread::SetThreadLocal(key, expected);
723 void* actual = Thread::GetExistingThreadLocal(key);
724 if (expected != actual) {
725 V8_Fatal(__FILE__, __LINE__,
726 "V8 failed to initialize fast TLS on current kernel");
727 }
728 Thread::SetThreadLocal(key, NULL);
729 }
730
731 #endif // V8_FAST_TLS_SUPPORTED
732
733
CreateThreadLocalKey()734 Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
735 #ifdef V8_FAST_TLS_SUPPORTED
736 bool check_fast_tls = false;
737 if (tls_base_offset_initialized == 0) {
738 check_fast_tls = true;
739 InitializeTlsBaseOffset();
740 }
741 #endif
742 pthread_key_t key;
743 int result = pthread_key_create(&key, NULL);
744 DCHECK_EQ(0, result);
745 USE(result);
746 LocalStorageKey local_key = PthreadKeyToLocalKey(key);
747 #ifdef V8_FAST_TLS_SUPPORTED
748 // If we just initialized fast TLS support, make sure it works.
749 if (check_fast_tls) CheckFastTls(local_key);
750 #endif
751 return local_key;
752 }
753
754
DeleteThreadLocalKey(LocalStorageKey key)755 void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
756 pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
757 int result = pthread_key_delete(pthread_key);
758 DCHECK_EQ(0, result);
759 USE(result);
760 }
761
762
GetThreadLocal(LocalStorageKey key)763 void* Thread::GetThreadLocal(LocalStorageKey key) {
764 pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
765 return pthread_getspecific(pthread_key);
766 }
767
768
SetThreadLocal(LocalStorageKey key,void * value)769 void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
770 pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
771 int result = pthread_setspecific(pthread_key, value);
772 DCHECK_EQ(0, result);
773 USE(result);
774 }
775
776 } // namespace base
777 } // namespace v8
778