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