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