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