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 POSIX platforms Linux,
30// Mac OS, FreeBSD and OpenBSD.
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
73namespace v8 {
74namespace internal {
75
76// 0 is never a valid thread id.
77static const pthread_t kNoThread = (pthread_t) 0;
78
79
80uint64_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
95intptr_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
103uint64_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#else
134  intptr_t pages = sysconf(_SC_PHYS_PAGES);
135  intptr_t page_size = sysconf(_SC_PAGESIZE);
136  if (pages == -1 || page_size == -1) {
137    UNREACHABLE();
138    return 0;
139  }
140  return static_cast<uint64_t>(pages) * page_size;
141#endif
142}
143
144
145int OS::ActivationFrameAlignment() {
146#if V8_TARGET_ARCH_ARM
147  // On EABI ARM targets this is required for fp correctness in the
148  // runtime system.
149  return 8;
150#elif V8_TARGET_ARCH_MIPS
151  return 8;
152#else
153  // Otherwise we just assume 16 byte alignment, i.e.:
154  // - With gcc 4.4 the tree vectorization optimizer can generate code
155  //   that requires 16 byte alignment such as movdqa on x86.
156  // - Mac OS X and Solaris (64-bit) activation frames must be 16 byte-aligned;
157  //   see "Mac OS X ABI Function Call Guide"
158  return 16;
159#endif
160}
161
162
163intptr_t OS::CommitPageSize() {
164  static intptr_t page_size = getpagesize();
165  return page_size;
166}
167
168
169void OS::Free(void* address, const size_t size) {
170  // TODO(1240712): munmap has a return value which is ignored here.
171  int result = munmap(address, size);
172  USE(result);
173  ASSERT(result == 0);
174}
175
176
177// Get rid of writable permission on code allocations.
178void OS::ProtectCode(void* address, const size_t size) {
179#if defined(__CYGWIN__)
180  DWORD old_protect;
181  VirtualProtect(address, size, PAGE_EXECUTE_READ, &old_protect);
182#elif defined(__native_client__)
183  // The Native Client port of V8 uses an interpreter, so
184  // code pages don't need PROT_EXEC.
185  mprotect(address, size, PROT_READ);
186#else
187  mprotect(address, size, PROT_READ | PROT_EXEC);
188#endif
189}
190
191
192// Create guard pages.
193void OS::Guard(void* address, const size_t size) {
194#if defined(__CYGWIN__)
195  DWORD oldprotect;
196  VirtualProtect(address, size, PAGE_NOACCESS, &oldprotect);
197#else
198  mprotect(address, size, PROT_NONE);
199#endif
200}
201
202
203void* OS::GetRandomMmapAddr() {
204#if defined(__native_client__)
205  // TODO(bradchen): restore randomization once Native Client gets
206  // smarter about using mmap address hints.
207  // See http://code.google.com/p/nativeclient/issues/3341
208  return NULL;
209#endif
210  Isolate* isolate = Isolate::UncheckedCurrent();
211  // Note that the current isolate isn't set up in a call path via
212  // CpuFeatures::Probe. We don't care about randomization in this case because
213  // the code page is immediately freed.
214  if (isolate != NULL) {
215    uintptr_t raw_addr;
216    isolate->random_number_generator()->NextBytes(&raw_addr, sizeof(raw_addr));
217#if V8_TARGET_ARCH_X64
218    // Currently available CPUs have 48 bits of virtual addressing.  Truncate
219    // the hint address to 46 bits to give the kernel a fighting chance of
220    // fulfilling our placement request.
221    raw_addr &= V8_UINT64_C(0x3ffffffff000);
222#else
223    raw_addr &= 0x3ffff000;
224
225# ifdef __sun
226    // For our Solaris/illumos mmap hint, we pick a random address in the bottom
227    // half of the top half of the address space (that is, the third quarter).
228    // Because we do not MAP_FIXED, this will be treated only as a hint -- the
229    // system will not fail to mmap() because something else happens to already
230    // be mapped at our random address. We deliberately set the hint high enough
231    // to get well above the system's break (that is, the heap); Solaris and
232    // illumos will try the hint and if that fails allocate as if there were
233    // no hint at all. The high hint prevents the break from getting hemmed in
234    // at low values, ceding half of the address space to the system heap.
235    raw_addr += 0x80000000;
236# else
237    // The range 0x20000000 - 0x60000000 is relatively unpopulated across a
238    // variety of ASLR modes (PAE kernel, NX compat mode, etc) and on macos
239    // 10.6 and 10.7.
240    raw_addr += 0x20000000;
241# endif
242#endif
243    return reinterpret_cast<void*>(raw_addr);
244  }
245  return NULL;
246}
247
248
249size_t OS::AllocateAlignment() {
250  return getpagesize();
251}
252
253
254void OS::Sleep(int milliseconds) {
255  useconds_t ms = static_cast<useconds_t>(milliseconds);
256  usleep(1000 * ms);
257}
258
259
260void OS::Abort() {
261  // Redirect to std abort to signal abnormal program termination.
262  if (FLAG_break_on_abort) {
263    DebugBreak();
264  }
265  abort();
266}
267
268
269void OS::DebugBreak() {
270#if V8_HOST_ARCH_ARM
271  asm("bkpt 0");
272#elif V8_HOST_ARCH_MIPS
273  asm("break");
274#elif V8_HOST_ARCH_IA32
275#if defined(__native_client__)
276  asm("hlt");
277#else
278  asm("int $3");
279#endif  // __native_client__
280#elif V8_HOST_ARCH_X64
281  asm("int $3");
282#else
283#error Unsupported host architecture.
284#endif
285}
286
287
288// ----------------------------------------------------------------------------
289// Math functions
290
291double modulo(double x, double y) {
292  return fmod(x, y);
293}
294
295
296#define UNARY_MATH_FUNCTION(name, generator)             \
297static UnaryMathFunction fast_##name##_function = NULL;  \
298void init_fast_##name##_function() {                     \
299  fast_##name##_function = generator;                    \
300}                                                        \
301double fast_##name(double x) {                           \
302  return (*fast_##name##_function)(x);                   \
303}
304
305UNARY_MATH_FUNCTION(sin, CreateTranscendentalFunction(TranscendentalCache::SIN))
306UNARY_MATH_FUNCTION(cos, CreateTranscendentalFunction(TranscendentalCache::COS))
307UNARY_MATH_FUNCTION(tan, CreateTranscendentalFunction(TranscendentalCache::TAN))
308UNARY_MATH_FUNCTION(log, CreateTranscendentalFunction(TranscendentalCache::LOG))
309UNARY_MATH_FUNCTION(exp, CreateExpFunction())
310UNARY_MATH_FUNCTION(sqrt, CreateSqrtFunction())
311
312#undef UNARY_MATH_FUNCTION
313
314
315void lazily_initialize_fast_exp() {
316  if (fast_exp_function == NULL) {
317    init_fast_exp_function();
318  }
319}
320
321
322double OS::nan_value() {
323  // NAN from math.h is defined in C99 and not in POSIX.
324  return NAN;
325}
326
327
328int OS::GetCurrentProcessId() {
329  return static_cast<int>(getpid());
330}
331
332
333// ----------------------------------------------------------------------------
334// POSIX date/time support.
335//
336
337int OS::GetUserTime(uint32_t* secs,  uint32_t* usecs) {
338  struct rusage usage;
339
340  if (getrusage(RUSAGE_SELF, &usage) < 0) return -1;
341  *secs = usage.ru_utime.tv_sec;
342  *usecs = usage.ru_utime.tv_usec;
343  return 0;
344}
345
346
347double OS::TimeCurrentMillis() {
348  return Time::Now().ToJsTime();
349}
350
351
352double OS::DaylightSavingsOffset(double time) {
353  if (std::isnan(time)) return nan_value();
354  time_t tv = static_cast<time_t>(floor(time/msPerSecond));
355  struct tm* t = localtime(&tv);
356  if (NULL == t) return nan_value();
357  return t->tm_isdst > 0 ? 3600 * msPerSecond : 0;
358}
359
360
361int OS::GetLastError() {
362  return errno;
363}
364
365
366// ----------------------------------------------------------------------------
367// POSIX stdio support.
368//
369
370FILE* OS::FOpen(const char* path, const char* mode) {
371  FILE* file = fopen(path, mode);
372  if (file == NULL) return NULL;
373  struct stat file_stat;
374  if (fstat(fileno(file), &file_stat) != 0) return NULL;
375  bool is_regular_file = ((file_stat.st_mode & S_IFREG) != 0);
376  if (is_regular_file) return file;
377  fclose(file);
378  return NULL;
379}
380
381
382bool OS::Remove(const char* path) {
383  return (remove(path) == 0);
384}
385
386
387FILE* OS::OpenTemporaryFile() {
388  return tmpfile();
389}
390
391
392const char* const OS::LogFileOpenMode = "w";
393
394
395void OS::Print(const char* format, ...) {
396  va_list args;
397  va_start(args, format);
398  VPrint(format, args);
399  va_end(args);
400}
401
402
403void OS::VPrint(const char* format, va_list args) {
404#if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
405  __android_log_vprint(ANDROID_LOG_INFO, LOG_TAG, format, args);
406#else
407  vprintf(format, args);
408#endif
409}
410
411
412void OS::FPrint(FILE* out, const char* format, ...) {
413  va_list args;
414  va_start(args, format);
415  VFPrint(out, format, args);
416  va_end(args);
417}
418
419
420void OS::VFPrint(FILE* out, const char* format, va_list args) {
421#if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
422  __android_log_vprint(ANDROID_LOG_INFO, LOG_TAG, format, args);
423#else
424  vfprintf(out, format, args);
425#endif
426}
427
428
429void OS::PrintError(const char* format, ...) {
430  va_list args;
431  va_start(args, format);
432  VPrintError(format, args);
433  va_end(args);
434}
435
436
437void OS::VPrintError(const char* format, va_list args) {
438#if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
439  __android_log_vprint(ANDROID_LOG_ERROR, LOG_TAG, format, args);
440#else
441  vfprintf(stderr, format, args);
442#endif
443}
444
445
446int OS::SNPrintF(Vector<char> str, const char* format, ...) {
447  va_list args;
448  va_start(args, format);
449  int result = VSNPrintF(str, format, args);
450  va_end(args);
451  return result;
452}
453
454
455int OS::VSNPrintF(Vector<char> str,
456                  const char* format,
457                  va_list args) {
458  int n = vsnprintf(str.start(), str.length(), format, args);
459  if (n < 0 || n >= str.length()) {
460    // If the length is zero, the assignment fails.
461    if (str.length() > 0)
462      str[str.length() - 1] = '\0';
463    return -1;
464  } else {
465    return n;
466  }
467}
468
469
470#if V8_TARGET_ARCH_IA32
471static void MemMoveWrapper(void* dest, const void* src, size_t size) {
472  memmove(dest, src, size);
473}
474
475
476// Initialize to library version so we can call this at any time during startup.
477static OS::MemMoveFunction memmove_function = &MemMoveWrapper;
478
479// Defined in codegen-ia32.cc.
480OS::MemMoveFunction CreateMemMoveFunction();
481
482// Copy memory area. No restrictions.
483void OS::MemMove(void* dest, const void* src, size_t size) {
484  if (size == 0) return;
485  // Note: here we rely on dependent reads being ordered. This is true
486  // on all architectures we currently support.
487  (*memmove_function)(dest, src, size);
488}
489
490#elif defined(V8_HOST_ARCH_ARM)
491void OS::MemCopyUint16Uint8Wrapper(uint16_t* dest,
492                               const uint8_t* src,
493                               size_t chars) {
494  uint16_t *limit = dest + chars;
495  while (dest < limit) {
496    *dest++ = static_cast<uint16_t>(*src++);
497  }
498}
499
500
501OS::MemCopyUint8Function OS::memcopy_uint8_function = &OS::MemCopyUint8Wrapper;
502OS::MemCopyUint16Uint8Function OS::memcopy_uint16_uint8_function =
503    &OS::MemCopyUint16Uint8Wrapper;
504// Defined in codegen-arm.cc.
505OS::MemCopyUint8Function CreateMemCopyUint8Function(
506    OS::MemCopyUint8Function stub);
507OS::MemCopyUint16Uint8Function CreateMemCopyUint16Uint8Function(
508    OS::MemCopyUint16Uint8Function stub);
509#endif
510
511
512void OS::PostSetUp() {
513#if V8_TARGET_ARCH_IA32
514  OS::MemMoveFunction generated_memmove = CreateMemMoveFunction();
515  if (generated_memmove != NULL) {
516    memmove_function = generated_memmove;
517  }
518#elif defined(V8_HOST_ARCH_ARM)
519  OS::memcopy_uint8_function =
520      CreateMemCopyUint8Function(&OS::MemCopyUint8Wrapper);
521  OS::memcopy_uint16_uint8_function =
522      CreateMemCopyUint16Uint8Function(&OS::MemCopyUint16Uint8Wrapper);
523#endif
524  init_fast_sin_function();
525  init_fast_cos_function();
526  init_fast_tan_function();
527  init_fast_log_function();
528  // fast_exp is initialized lazily.
529  init_fast_sqrt_function();
530}
531
532
533// ----------------------------------------------------------------------------
534// POSIX string support.
535//
536
537char* OS::StrChr(char* str, int c) {
538  return strchr(str, c);
539}
540
541
542void OS::StrNCpy(Vector<char> dest, const char* src, size_t n) {
543  strncpy(dest.start(), src, n);
544}
545
546
547// ----------------------------------------------------------------------------
548// POSIX thread support.
549//
550
551class Thread::PlatformData : public Malloced {
552 public:
553  PlatformData() : thread_(kNoThread) {}
554  pthread_t thread_;  // Thread handle for pthread.
555};
556
557Thread::Thread(const Options& options)
558    : data_(new PlatformData),
559      stack_size_(options.stack_size()),
560      start_semaphore_(NULL) {
561  if (stack_size_ > 0 && stack_size_ < PTHREAD_STACK_MIN) {
562    stack_size_ = PTHREAD_STACK_MIN;
563  }
564  set_name(options.name());
565}
566
567
568Thread::~Thread() {
569  delete data_;
570}
571
572
573static void SetThreadName(const char* name) {
574#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
575  pthread_set_name_np(pthread_self(), name);
576#elif defined(__NetBSD__)
577  STATIC_ASSERT(Thread::kMaxThreadNameLength <= PTHREAD_MAX_NAMELEN_NP);
578  pthread_setname_np(pthread_self(), "%s", name);
579#elif defined(__APPLE__)
580  // pthread_setname_np is only available in 10.6 or later, so test
581  // for it at runtime.
582  int (*dynamic_pthread_setname_np)(const char*);
583  *reinterpret_cast<void**>(&dynamic_pthread_setname_np) =
584    dlsym(RTLD_DEFAULT, "pthread_setname_np");
585  if (dynamic_pthread_setname_np == NULL)
586    return;
587
588  // Mac OS X does not expose the length limit of the name, so hardcode it.
589  static const int kMaxNameLength = 63;
590  STATIC_ASSERT(Thread::kMaxThreadNameLength <= kMaxNameLength);
591  dynamic_pthread_setname_np(name);
592#elif defined(PR_SET_NAME)
593  prctl(PR_SET_NAME,
594        reinterpret_cast<unsigned long>(name),  // NOLINT
595        0, 0, 0);
596#endif
597}
598
599
600static void* ThreadEntry(void* arg) {
601  Thread* thread = reinterpret_cast<Thread*>(arg);
602  // This is also initialized by the first argument to pthread_create() but we
603  // don't know which thread will run first (the original thread or the new
604  // one) so we initialize it here too.
605  thread->data()->thread_ = pthread_self();
606  SetThreadName(thread->name());
607  ASSERT(thread->data()->thread_ != kNoThread);
608  thread->NotifyStartedAndRun();
609  return NULL;
610}
611
612
613void Thread::set_name(const char* name) {
614  strncpy(name_, name, sizeof(name_));
615  name_[sizeof(name_) - 1] = '\0';
616}
617
618
619void Thread::Start() {
620  int result;
621  pthread_attr_t attr;
622  memset(&attr, 0, sizeof(attr));
623  result = pthread_attr_init(&attr);
624  ASSERT_EQ(0, result);
625  // Native client uses default stack size.
626#if !defined(__native_client__)
627  if (stack_size_ > 0) {
628    result = pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
629    ASSERT_EQ(0, result);
630  }
631#endif
632  result = pthread_create(&data_->thread_, &attr, ThreadEntry, this);
633  ASSERT_EQ(0, result);
634  result = pthread_attr_destroy(&attr);
635  ASSERT_EQ(0, result);
636  ASSERT(data_->thread_ != kNoThread);
637  USE(result);
638}
639
640
641void Thread::Join() {
642  pthread_join(data_->thread_, NULL);
643}
644
645
646void Thread::YieldCPU() {
647  int result = sched_yield();
648  ASSERT_EQ(0, result);
649  USE(result);
650}
651
652
653static Thread::LocalStorageKey PthreadKeyToLocalKey(pthread_key_t pthread_key) {
654#if defined(__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
667static pthread_key_t LocalKeyToPthreadKey(Thread::LocalStorageKey local_key) {
668#if defined(__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
680static Atomic32 tls_base_offset_initialized = 0;
681intptr_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.
685static 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
720static 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
734Thread::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  ASSERT_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
755void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
756  pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
757  int result = pthread_key_delete(pthread_key);
758  ASSERT_EQ(0, result);
759  USE(result);
760}
761
762
763void* Thread::GetThreadLocal(LocalStorageKey key) {
764  pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
765  return pthread_getspecific(pthread_key);
766}
767
768
769void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
770  pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
771  int result = pthread_setspecific(pthread_key, value);
772  ASSERT_EQ(0, result);
773  USE(result);
774}
775
776
777} }  // namespace v8::internal
778