Home | History | Annotate | Download | only in platform
      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 
     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 
     97 intptr_t OS::CommitPageSize() {
     98   static intptr_t page_size = getpagesize();
     99   return page_size;
    100 }
    101 
    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 
    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.
    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.
    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.
    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 
    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 
    170 const char* OS::GetGCFakeMMapFile() {
    171   return g_gc_fake_mmap;
    172 }
    173 
    174 
    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 
    241 size_t OS::AllocateAlignment() {
    242   return static_cast<size_t>(sysconf(_SC_PAGESIZE));
    243 }
    244 
    245 
    246 void OS::Sleep(TimeDelta interval) {
    247   usleep(static_cast<useconds_t>(interval.InMicroseconds()));
    248 }
    249 
    250 
    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 
    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:
    286   PosixMemoryMappedFile(FILE* file, void* memory, size_t size)
    287       : file_(file), memory_(memory), size_(size) {}
    288   ~PosixMemoryMappedFile() final;
    289   void* memory() const final { return memory_; }
    290   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
    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
    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 
    337 PosixMemoryMappedFile::~PosixMemoryMappedFile() {
    338   if (memory_) OS::Free(memory_, size_);
    339   fclose(file_);
    340 }
    341 
    342 
    343 int OS::GetCurrentProcessId() {
    344   return static_cast<int>(getpid());
    345 }
    346 
    347 
    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 
    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 
    379 double OS::TimeCurrentMillis() {
    380   return Time::Now().ToJsTime();
    381 }
    382 
    383 
    384 class TimezoneCache {};
    385 
    386 
    387 TimezoneCache* OS::CreateTimezoneCache() {
    388   return NULL;
    389 }
    390 
    391 
    392 void OS::DisposeTimezoneCache(TimezoneCache* cache) {
    393   DCHECK(cache == NULL);
    394 }
    395 
    396 
    397 void OS::ClearTimezoneCache(TimezoneCache* cache) {
    398   DCHECK(cache == NULL);
    399 }
    400 
    401 
    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 
    412 int OS::GetLastError() {
    413   return errno;
    414 }
    415 
    416 
    417 // ----------------------------------------------------------------------------
    418 // POSIX stdio support.
    419 //
    420 
    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 
    433 bool OS::Remove(const char* path) {
    434   return (remove(path) == 0);
    435 }
    436 
    437 char OS::DirectorySeparator() { return '/'; }
    438 
    439 bool OS::isDirectorySeparator(const char ch) {
    440   return ch == DirectorySeparator();
    441 }
    442 
    443 
    444 FILE* OS::OpenTemporaryFile() {
    445   return tmpfile();
    446 }
    447 
    448 
    449 const char* const OS::LogFileOpenMode = "w";
    450 
    451 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    532 char* OS::StrChr(char* str, int c) {
    533   return strchr(str, c);
    534 }
    535 
    536 
    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:
    548   PlatformData() : thread_(kNoThread) {}
    549   pthread_t thread_;  // Thread handle for pthread.
    550   // Synchronizes thread creation
    551   Mutex thread_creation_mutex_;
    552 };
    553 
    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 
    565 Thread::~Thread() {
    566   delete data_;
    567 }
    568 
    569 
    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 
    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 
    610 void Thread::set_name(const char* name) {
    611   strncpy(name_, name, sizeof(name_));
    612   name_[sizeof(name_) - 1] = '\0';
    613 }
    614 
    615 
    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 
    648 void Thread::Join() {
    649   pthread_join(data_->thread_, NULL);
    650 }
    651 
    652 
    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 
    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.
    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 
    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 
    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 
    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 
    763 void* Thread::GetThreadLocal(LocalStorageKey key) {
    764   pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
    765   return pthread_getspecific(pthread_key);
    766 }
    767 
    768 
    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