Upstream version 6.35.121.0
[platform/framework/web/crosswalk.git] / src / v8 / src / platform-posix.cc
1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 //     * Redistributions of source code must retain the above copyright
7 //       notice, this list of conditions and the following disclaimer.
8 //     * Redistributions in binary form must reproduce the above
9 //       copyright notice, this list of conditions and the following
10 //       disclaimer in the documentation and/or other materials provided
11 //       with the distribution.
12 //     * Neither the name of Google Inc. nor the names of its
13 //       contributors may be used to endorse or promote products derived
14 //       from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28 // Platform-specific code for POSIX goes here. This is not a platform on its
29 // own, but contains the parts which are the same across the POSIX platforms
30 // Linux, MacOS, FreeBSD, OpenBSD, NetBSD and QNX.
31
32 #include <dlfcn.h>
33 #include <pthread.h>
34 #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
35 #include <pthread_np.h>  // for pthread_set_name_np
36 #endif
37 #include <sched.h>  // for sched_yield
38 #include <unistd.h>
39 #include <errno.h>
40 #include <time.h>
41
42 #include <sys/mman.h>
43 #include <sys/socket.h>
44 #include <sys/resource.h>
45 #include <sys/time.h>
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #if defined(__linux__)
49 #include <sys/prctl.h>  // for prctl
50 #endif
51 #if defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__) || \
52     defined(__NetBSD__) || defined(__OpenBSD__)
53 #include <sys/sysctl.h>  // for sysctl
54 #endif
55
56 #include <arpa/inet.h>
57 #include <netinet/in.h>
58 #include <netdb.h>
59
60 #undef MAP_TYPE
61
62 #if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
63 #define LOG_TAG "v8"
64 #include <android/log.h>
65 #endif
66
67 #include "v8.h"
68
69 #include "codegen.h"
70 #include "isolate-inl.h"
71 #include "platform.h"
72
73 namespace v8 {
74 namespace internal {
75
76 // 0 is never a valid thread id.
77 static const pthread_t kNoThread = (pthread_t) 0;
78
79
80 uint64_t OS::CpuFeaturesImpliedByPlatform() {
81 #if V8_OS_MACOSX
82   // Mac OS X requires all these to install so we can assume they are present.
83   // These constants are defined by the CPUid instructions.
84   const uint64_t one = 1;
85   return (one << SSE2) | (one << CMOV);
86 #else
87   return 0;  // Nothing special about the other systems.
88 #endif
89 }
90
91
92 // Maximum size of the virtual memory.  0 means there is no artificial
93 // limit.
94
95 intptr_t OS::MaxVirtualMemory() {
96   struct rlimit limit;
97   int result = getrlimit(RLIMIT_DATA, &limit);
98   if (result != 0) return 0;
99   return limit.rlim_cur;
100 }
101
102
103 uint64_t OS::TotalPhysicalMemory() {
104 #if V8_OS_MACOSX
105   int mib[2];
106   mib[0] = CTL_HW;
107   mib[1] = HW_MEMSIZE;
108   int64_t size = 0;
109   size_t len = sizeof(size);
110   if (sysctl(mib, 2, &size, &len, NULL, 0) != 0) {
111     UNREACHABLE();
112     return 0;
113   }
114   return static_cast<uint64_t>(size);
115 #elif V8_OS_FREEBSD
116   int pages, page_size;
117   size_t size = sizeof(pages);
118   sysctlbyname("vm.stats.vm.v_page_count", &pages, &size, NULL, 0);
119   sysctlbyname("vm.stats.vm.v_page_size", &page_size, &size, NULL, 0);
120   if (pages == -1 || page_size == -1) {
121     UNREACHABLE();
122     return 0;
123   }
124   return static_cast<uint64_t>(pages) * page_size;
125 #elif V8_OS_CYGWIN
126   MEMORYSTATUS memory_info;
127   memory_info.dwLength = sizeof(memory_info);
128   if (!GlobalMemoryStatus(&memory_info)) {
129     UNREACHABLE();
130     return 0;
131   }
132   return static_cast<uint64_t>(memory_info.dwTotalPhys);
133 #elif V8_OS_QNX
134   struct stat stat_buf;
135   if (stat("/proc", &stat_buf) != 0) {
136     UNREACHABLE();
137     return 0;
138   }
139   return static_cast<uint64_t>(stat_buf.st_size);
140 #else
141   intptr_t pages = sysconf(_SC_PHYS_PAGES);
142   intptr_t page_size = sysconf(_SC_PAGESIZE);
143   if (pages == -1 || page_size == -1) {
144     UNREACHABLE();
145     return 0;
146   }
147   return static_cast<uint64_t>(pages) * page_size;
148 #endif
149 }
150
151
152 int OS::ActivationFrameAlignment() {
153 #if V8_TARGET_ARCH_ARM
154   // On EABI ARM targets this is required for fp correctness in the
155   // runtime system.
156   return 8;
157 #elif V8_TARGET_ARCH_MIPS
158   return 8;
159 #else
160   // Otherwise we just assume 16 byte alignment, i.e.:
161   // - With gcc 4.4 the tree vectorization optimizer can generate code
162   //   that requires 16 byte alignment such as movdqa on x86.
163   // - Mac OS X and Solaris (64-bit) activation frames must be 16 byte-aligned;
164   //   see "Mac OS X ABI Function Call Guide"
165   return 16;
166 #endif
167 }
168
169
170 intptr_t OS::CommitPageSize() {
171   static intptr_t page_size = getpagesize();
172   return page_size;
173 }
174
175
176 void OS::Free(void* address, const size_t size) {
177   // TODO(1240712): munmap has a return value which is ignored here.
178   int result = munmap(address, size);
179   USE(result);
180   ASSERT(result == 0);
181 }
182
183
184 // Get rid of writable permission on code allocations.
185 void OS::ProtectCode(void* address, const size_t size) {
186 #if V8_OS_CYGWIN
187   DWORD old_protect;
188   VirtualProtect(address, size, PAGE_EXECUTE_READ, &old_protect);
189 #elif V8_OS_NACL
190   // The Native Client port of V8 uses an interpreter, so
191   // code pages don't need PROT_EXEC.
192   mprotect(address, size, PROT_READ);
193 #else
194   mprotect(address, size, PROT_READ | PROT_EXEC);
195 #endif
196 }
197
198
199 // Create guard pages.
200 void OS::Guard(void* address, const size_t size) {
201 #if V8_OS_CYGWIN
202   DWORD oldprotect;
203   VirtualProtect(address, size, PAGE_NOACCESS, &oldprotect);
204 #else
205   mprotect(address, size, PROT_NONE);
206 #endif
207 }
208
209
210 void* OS::GetRandomMmapAddr() {
211 #if V8_OS_NACL
212   // TODO(bradchen): restore randomization once Native Client gets
213   // smarter about using mmap address hints.
214   // See http://code.google.com/p/nativeclient/issues/3341
215   return NULL;
216 #endif
217   Isolate* isolate = Isolate::UncheckedCurrent();
218   // Note that the current isolate isn't set up in a call path via
219   // CpuFeatures::Probe. We don't care about randomization in this case because
220   // the code page is immediately freed.
221   if (isolate != NULL) {
222     uintptr_t raw_addr;
223     isolate->random_number_generator()->NextBytes(&raw_addr, sizeof(raw_addr));
224 #if V8_TARGET_ARCH_X64
225     // Currently available CPUs have 48 bits of virtual addressing.  Truncate
226     // the hint address to 46 bits to give the kernel a fighting chance of
227     // fulfilling our placement request.
228     raw_addr &= V8_UINT64_C(0x3ffffffff000);
229 #else
230     raw_addr &= 0x3ffff000;
231
232 # ifdef __sun
233     // For our Solaris/illumos mmap hint, we pick a random address in the bottom
234     // half of the top half of the address space (that is, the third quarter).
235     // Because we do not MAP_FIXED, this will be treated only as a hint -- the
236     // system will not fail to mmap() because something else happens to already
237     // be mapped at our random address. We deliberately set the hint high enough
238     // to get well above the system's break (that is, the heap); Solaris and
239     // illumos will try the hint and if that fails allocate as if there were
240     // no hint at all. The high hint prevents the break from getting hemmed in
241     // at low values, ceding half of the address space to the system heap.
242     raw_addr += 0x80000000;
243 # else
244     // The range 0x20000000 - 0x60000000 is relatively unpopulated across a
245     // variety of ASLR modes (PAE kernel, NX compat mode, etc) and on macos
246     // 10.6 and 10.7.
247     raw_addr += 0x20000000;
248 # endif
249 #endif
250     return reinterpret_cast<void*>(raw_addr);
251   }
252   return NULL;
253 }
254
255
256 size_t OS::AllocateAlignment() {
257   return static_cast<size_t>(sysconf(_SC_PAGESIZE));
258 }
259
260
261 void OS::Sleep(int milliseconds) {
262   useconds_t ms = static_cast<useconds_t>(milliseconds);
263   usleep(1000 * ms);
264 }
265
266
267 void OS::Abort() {
268   if (FLAG_hard_abort) {
269     V8_IMMEDIATE_CRASH();
270   }
271   // Redirect to std abort to signal abnormal program termination.
272   abort();
273 }
274
275
276 void OS::DebugBreak() {
277 #if V8_HOST_ARCH_ARM
278   asm("bkpt 0");
279 #elif V8_HOST_ARCH_ARM64
280   asm("brk 0");
281 #elif V8_HOST_ARCH_MIPS
282   asm("break");
283 #elif V8_HOST_ARCH_IA32
284 #if defined(__native_client__)
285   asm("hlt");
286 #else
287   asm("int $3");
288 #endif  // __native_client__
289 #elif V8_HOST_ARCH_X64
290   asm("int $3");
291 #else
292 #error Unsupported host architecture.
293 #endif
294 }
295
296
297 // ----------------------------------------------------------------------------
298 // Math functions
299
300 double modulo(double x, double y) {
301   return std::fmod(x, y);
302 }
303
304
305 #define UNARY_MATH_FUNCTION(name, generator)             \
306 static UnaryMathFunction fast_##name##_function = NULL;  \
307 void init_fast_##name##_function() {                     \
308   fast_##name##_function = generator;                    \
309 }                                                        \
310 double fast_##name(double x) {                           \
311   return (*fast_##name##_function)(x);                   \
312 }
313
314 UNARY_MATH_FUNCTION(exp, CreateExpFunction())
315 UNARY_MATH_FUNCTION(sqrt, CreateSqrtFunction())
316
317 #undef UNARY_MATH_FUNCTION
318
319
320 void lazily_initialize_fast_exp() {
321   if (fast_exp_function == NULL) {
322     init_fast_exp_function();
323   }
324 }
325
326
327 double OS::nan_value() {
328   // NAN from math.h is defined in C99 and not in POSIX.
329   return NAN;
330 }
331
332
333 int OS::GetCurrentProcessId() {
334   return static_cast<int>(getpid());
335 }
336
337
338 // ----------------------------------------------------------------------------
339 // POSIX date/time support.
340 //
341
342 int OS::GetUserTime(uint32_t* secs,  uint32_t* usecs) {
343   struct rusage usage;
344
345   if (getrusage(RUSAGE_SELF, &usage) < 0) return -1;
346   *secs = usage.ru_utime.tv_sec;
347   *usecs = usage.ru_utime.tv_usec;
348   return 0;
349 }
350
351
352 double OS::TimeCurrentMillis() {
353   return Time::Now().ToJsTime();
354 }
355
356
357 class TimezoneCache {};
358
359
360 TimezoneCache* OS::CreateTimezoneCache() {
361   return NULL;
362 }
363
364
365 void OS::DisposeTimezoneCache(TimezoneCache* cache) {
366   ASSERT(cache == NULL);
367 }
368
369
370 void OS::ClearTimezoneCache(TimezoneCache* cache) {
371   ASSERT(cache == NULL);
372 }
373
374
375 double OS::DaylightSavingsOffset(double time, TimezoneCache*) {
376   if (std::isnan(time)) return nan_value();
377   time_t tv = static_cast<time_t>(std::floor(time/msPerSecond));
378   struct tm* t = localtime(&tv);
379   if (NULL == t) return nan_value();
380   return t->tm_isdst > 0 ? 3600 * msPerSecond : 0;
381 }
382
383
384 int OS::GetLastError() {
385   return errno;
386 }
387
388
389 // ----------------------------------------------------------------------------
390 // POSIX stdio support.
391 //
392
393 FILE* OS::FOpen(const char* path, const char* mode) {
394   FILE* file = fopen(path, mode);
395   if (file == NULL) return NULL;
396   struct stat file_stat;
397   if (fstat(fileno(file), &file_stat) != 0) return NULL;
398   bool is_regular_file = ((file_stat.st_mode & S_IFREG) != 0);
399   if (is_regular_file) return file;
400   fclose(file);
401   return NULL;
402 }
403
404
405 bool OS::Remove(const char* path) {
406   return (remove(path) == 0);
407 }
408
409
410 FILE* OS::OpenTemporaryFile() {
411   return tmpfile();
412 }
413
414
415 const char* const OS::LogFileOpenMode = "w";
416
417
418 void OS::Print(const char* format, ...) {
419   va_list args;
420   va_start(args, format);
421   VPrint(format, args);
422   va_end(args);
423 }
424
425
426 void OS::VPrint(const char* format, va_list args) {
427 #if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
428   __android_log_vprint(ANDROID_LOG_INFO, LOG_TAG, format, args);
429 #else
430   vprintf(format, args);
431 #endif
432 }
433
434
435 void OS::FPrint(FILE* out, const char* format, ...) {
436   va_list args;
437   va_start(args, format);
438   VFPrint(out, format, args);
439   va_end(args);
440 }
441
442
443 void OS::VFPrint(FILE* out, const char* format, va_list args) {
444 #if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
445   __android_log_vprint(ANDROID_LOG_INFO, LOG_TAG, format, args);
446 #else
447   vfprintf(out, format, args);
448 #endif
449 }
450
451
452 void OS::PrintError(const char* format, ...) {
453   va_list args;
454   va_start(args, format);
455   VPrintError(format, args);
456   va_end(args);
457 }
458
459
460 void OS::VPrintError(const char* format, va_list args) {
461 #if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
462   __android_log_vprint(ANDROID_LOG_ERROR, LOG_TAG, format, args);
463 #else
464   vfprintf(stderr, format, args);
465 #endif
466 }
467
468
469 int OS::SNPrintF(Vector<char> str, const char* format, ...) {
470   va_list args;
471   va_start(args, format);
472   int result = VSNPrintF(str, format, args);
473   va_end(args);
474   return result;
475 }
476
477
478 int OS::VSNPrintF(Vector<char> str,
479                   const char* format,
480                   va_list args) {
481   int n = vsnprintf(str.start(), str.length(), format, args);
482   if (n < 0 || n >= str.length()) {
483     // If the length is zero, the assignment fails.
484     if (str.length() > 0)
485       str[str.length() - 1] = '\0';
486     return -1;
487   } else {
488     return n;
489   }
490 }
491
492
493 #if V8_TARGET_ARCH_IA32
494 static void MemMoveWrapper(void* dest, const void* src, size_t size) {
495   memmove(dest, src, size);
496 }
497
498
499 // Initialize to library version so we can call this at any time during startup.
500 static OS::MemMoveFunction memmove_function = &MemMoveWrapper;
501
502 // Defined in codegen-ia32.cc.
503 OS::MemMoveFunction CreateMemMoveFunction();
504
505 // Copy memory area. No restrictions.
506 void OS::MemMove(void* dest, const void* src, size_t size) {
507   if (size == 0) return;
508   // Note: here we rely on dependent reads being ordered. This is true
509   // on all architectures we currently support.
510   (*memmove_function)(dest, src, size);
511 }
512
513 #elif defined(V8_HOST_ARCH_ARM)
514 void OS::MemCopyUint16Uint8Wrapper(uint16_t* dest,
515                                    const uint8_t* src,
516                                    size_t chars) {
517   uint16_t *limit = dest + chars;
518   while (dest < limit) {
519     *dest++ = static_cast<uint16_t>(*src++);
520   }
521 }
522
523
524 OS::MemCopyUint8Function OS::memcopy_uint8_function = &OS::MemCopyUint8Wrapper;
525 OS::MemCopyUint16Uint8Function OS::memcopy_uint16_uint8_function =
526     &OS::MemCopyUint16Uint8Wrapper;
527 // Defined in codegen-arm.cc.
528 OS::MemCopyUint8Function CreateMemCopyUint8Function(
529     OS::MemCopyUint8Function stub);
530 OS::MemCopyUint16Uint8Function CreateMemCopyUint16Uint8Function(
531     OS::MemCopyUint16Uint8Function stub);
532
533 #elif defined(V8_HOST_ARCH_MIPS)
534 OS::MemCopyUint8Function OS::memcopy_uint8_function = &OS::MemCopyUint8Wrapper;
535 // Defined in codegen-mips.cc.
536 OS::MemCopyUint8Function CreateMemCopyUint8Function(
537     OS::MemCopyUint8Function stub);
538 #endif
539
540
541 void OS::PostSetUp() {
542 #if V8_TARGET_ARCH_IA32
543   OS::MemMoveFunction generated_memmove = CreateMemMoveFunction();
544   if (generated_memmove != NULL) {
545     memmove_function = generated_memmove;
546   }
547 #elif defined(V8_HOST_ARCH_ARM)
548   OS::memcopy_uint8_function =
549       CreateMemCopyUint8Function(&OS::MemCopyUint8Wrapper);
550   OS::memcopy_uint16_uint8_function =
551       CreateMemCopyUint16Uint8Function(&OS::MemCopyUint16Uint8Wrapper);
552 #elif defined(V8_HOST_ARCH_MIPS)
553   OS::memcopy_uint8_function =
554       CreateMemCopyUint8Function(&OS::MemCopyUint8Wrapper);
555 #endif
556   // fast_exp is initialized lazily.
557   init_fast_sqrt_function();
558 }
559
560
561 // ----------------------------------------------------------------------------
562 // POSIX string support.
563 //
564
565 char* OS::StrChr(char* str, int c) {
566   return strchr(str, c);
567 }
568
569
570 void OS::StrNCpy(Vector<char> dest, const char* src, size_t n) {
571   strncpy(dest.start(), src, n);
572 }
573
574
575 // ----------------------------------------------------------------------------
576 // POSIX thread support.
577 //
578
579 class Thread::PlatformData : public Malloced {
580  public:
581   PlatformData() : thread_(kNoThread) {}
582   pthread_t thread_;  // Thread handle for pthread.
583   // Synchronizes thread creation
584   Mutex thread_creation_mutex_;
585 };
586
587 Thread::Thread(const Options& options)
588     : data_(new PlatformData),
589       stack_size_(options.stack_size()),
590       start_semaphore_(NULL) {
591   if (stack_size_ > 0 && stack_size_ < PTHREAD_STACK_MIN) {
592     stack_size_ = PTHREAD_STACK_MIN;
593   }
594   set_name(options.name());
595 }
596
597
598 Thread::~Thread() {
599   delete data_;
600 }
601
602
603 static void SetThreadName(const char* name) {
604 #if V8_OS_DRAGONFLYBSD || V8_OS_FREEBSD || V8_OS_OPENBSD
605   pthread_set_name_np(pthread_self(), name);
606 #elif V8_OS_NETBSD
607   STATIC_ASSERT(Thread::kMaxThreadNameLength <= PTHREAD_MAX_NAMELEN_NP);
608   pthread_setname_np(pthread_self(), "%s", name);
609 #elif V8_OS_MACOSX
610   // pthread_setname_np is only available in 10.6 or later, so test
611   // for it at runtime.
612   int (*dynamic_pthread_setname_np)(const char*);
613   *reinterpret_cast<void**>(&dynamic_pthread_setname_np) =
614     dlsym(RTLD_DEFAULT, "pthread_setname_np");
615   if (dynamic_pthread_setname_np == NULL)
616     return;
617
618   // Mac OS X does not expose the length limit of the name, so hardcode it.
619   static const int kMaxNameLength = 63;
620   STATIC_ASSERT(Thread::kMaxThreadNameLength <= kMaxNameLength);
621   dynamic_pthread_setname_np(name);
622 #elif defined(PR_SET_NAME)
623   prctl(PR_SET_NAME,
624         reinterpret_cast<unsigned long>(name),  // NOLINT
625         0, 0, 0);
626 #endif
627 }
628
629
630 static void* ThreadEntry(void* arg) {
631   Thread* thread = reinterpret_cast<Thread*>(arg);
632   // We take the lock here to make sure that pthread_create finished first since
633   // we don't know which thread will run first (the original thread or the new
634   // one).
635   { LockGuard<Mutex> lock_guard(&thread->data()->thread_creation_mutex_); }
636   SetThreadName(thread->name());
637   ASSERT(thread->data()->thread_ != kNoThread);
638   thread->NotifyStartedAndRun();
639   return NULL;
640 }
641
642
643 void Thread::set_name(const char* name) {
644   strncpy(name_, name, sizeof(name_));
645   name_[sizeof(name_) - 1] = '\0';
646 }
647
648
649 void Thread::Start() {
650   int result;
651   pthread_attr_t attr;
652   memset(&attr, 0, sizeof(attr));
653   result = pthread_attr_init(&attr);
654   ASSERT_EQ(0, result);
655   // Native client uses default stack size.
656 #if !V8_OS_NACL
657   if (stack_size_ > 0) {
658     result = pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
659     ASSERT_EQ(0, result);
660   }
661 #endif
662   {
663     LockGuard<Mutex> lock_guard(&data_->thread_creation_mutex_);
664     result = pthread_create(&data_->thread_, &attr, ThreadEntry, this);
665   }
666   ASSERT_EQ(0, result);
667   result = pthread_attr_destroy(&attr);
668   ASSERT_EQ(0, result);
669   ASSERT(data_->thread_ != kNoThread);
670   USE(result);
671 }
672
673
674 void Thread::Join() {
675   pthread_join(data_->thread_, NULL);
676 }
677
678
679 void Thread::YieldCPU() {
680   int result = sched_yield();
681   ASSERT_EQ(0, result);
682   USE(result);
683 }
684
685
686 static Thread::LocalStorageKey PthreadKeyToLocalKey(pthread_key_t pthread_key) {
687 #if V8_OS_CYGWIN
688   // We need to cast pthread_key_t to Thread::LocalStorageKey in two steps
689   // because pthread_key_t is a pointer type on Cygwin. This will probably not
690   // work on 64-bit platforms, but Cygwin doesn't support 64-bit anyway.
691   STATIC_ASSERT(sizeof(Thread::LocalStorageKey) == sizeof(pthread_key_t));
692   intptr_t ptr_key = reinterpret_cast<intptr_t>(pthread_key);
693   return static_cast<Thread::LocalStorageKey>(ptr_key);
694 #else
695   return static_cast<Thread::LocalStorageKey>(pthread_key);
696 #endif
697 }
698
699
700 static pthread_key_t LocalKeyToPthreadKey(Thread::LocalStorageKey local_key) {
701 #if V8_OS_CYGWIN
702   STATIC_ASSERT(sizeof(Thread::LocalStorageKey) == sizeof(pthread_key_t));
703   intptr_t ptr_key = static_cast<intptr_t>(local_key);
704   return reinterpret_cast<pthread_key_t>(ptr_key);
705 #else
706   return static_cast<pthread_key_t>(local_key);
707 #endif
708 }
709
710
711 #ifdef V8_FAST_TLS_SUPPORTED
712
713 static Atomic32 tls_base_offset_initialized = 0;
714 intptr_t kMacTlsBaseOffset = 0;
715
716 // It's safe to do the initialization more that once, but it has to be
717 // done at least once.
718 static void InitializeTlsBaseOffset() {
719   const size_t kBufferSize = 128;
720   char buffer[kBufferSize];
721   size_t buffer_size = kBufferSize;
722   int ctl_name[] = { CTL_KERN , KERN_OSRELEASE };
723   if (sysctl(ctl_name, 2, buffer, &buffer_size, NULL, 0) != 0) {
724     V8_Fatal(__FILE__, __LINE__, "V8 failed to get kernel version");
725   }
726   // The buffer now contains a string of the form XX.YY.ZZ, where
727   // XX is the major kernel version component.
728   // Make sure the buffer is 0-terminated.
729   buffer[kBufferSize - 1] = '\0';
730   char* period_pos = strchr(buffer, '.');
731   *period_pos = '\0';
732   int kernel_version_major =
733       static_cast<int>(strtol(buffer, NULL, 10));  // NOLINT
734   // The constants below are taken from pthreads.s from the XNU kernel
735   // sources archive at www.opensource.apple.com.
736   if (kernel_version_major < 11) {
737     // 8.x.x (Tiger), 9.x.x (Leopard), 10.x.x (Snow Leopard) have the
738     // same offsets.
739 #if V8_HOST_ARCH_IA32
740     kMacTlsBaseOffset = 0x48;
741 #else
742     kMacTlsBaseOffset = 0x60;
743 #endif
744   } else {
745     // 11.x.x (Lion) changed the offset.
746     kMacTlsBaseOffset = 0;
747   }
748
749   Release_Store(&tls_base_offset_initialized, 1);
750 }
751
752
753 static void CheckFastTls(Thread::LocalStorageKey key) {
754   void* expected = reinterpret_cast<void*>(0x1234CAFE);
755   Thread::SetThreadLocal(key, expected);
756   void* actual = Thread::GetExistingThreadLocal(key);
757   if (expected != actual) {
758     V8_Fatal(__FILE__, __LINE__,
759              "V8 failed to initialize fast TLS on current kernel");
760   }
761   Thread::SetThreadLocal(key, NULL);
762 }
763
764 #endif  // V8_FAST_TLS_SUPPORTED
765
766
767 Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
768 #ifdef V8_FAST_TLS_SUPPORTED
769   bool check_fast_tls = false;
770   if (tls_base_offset_initialized == 0) {
771     check_fast_tls = true;
772     InitializeTlsBaseOffset();
773   }
774 #endif
775   pthread_key_t key;
776   int result = pthread_key_create(&key, NULL);
777   ASSERT_EQ(0, result);
778   USE(result);
779   LocalStorageKey local_key = PthreadKeyToLocalKey(key);
780 #ifdef V8_FAST_TLS_SUPPORTED
781   // If we just initialized fast TLS support, make sure it works.
782   if (check_fast_tls) CheckFastTls(local_key);
783 #endif
784   return local_key;
785 }
786
787
788 void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
789   pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
790   int result = pthread_key_delete(pthread_key);
791   ASSERT_EQ(0, result);
792   USE(result);
793 }
794
795
796 void* Thread::GetThreadLocal(LocalStorageKey key) {
797   pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
798   return pthread_getspecific(pthread_key);
799 }
800
801
802 void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
803   pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
804   int result = pthread_setspecific(pthread_key, value);
805   ASSERT_EQ(0, result);
806   USE(result);
807 }
808
809
810 } }  // namespace v8::internal