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