• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <time.h>
17 #include <unistd.h>
18 
19 #include <sys/mman.h>
20 #include <sys/resource.h>
21 #include <sys/stat.h>
22 #include <sys/time.h>
23 #include <sys/types.h>
24 #if defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__) || \
25     defined(__NetBSD__) || defined(__OpenBSD__)
26 #include <sys/sysctl.h>  // NOLINT, for sysctl
27 #endif
28 
29 #undef MAP_TYPE
30 
31 #if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
32 #define LOG_TAG "v8"
33 #include <android/log.h>  // NOLINT
34 #endif
35 
36 #include <cmath>
37 #include <cstdlib>
38 
39 #include "src/base/lazy-instance.h"
40 #include "src/base/macros.h"
41 #include "src/base/platform/platform.h"
42 #include "src/base/platform/time.h"
43 #include "src/base/utils/random-number-generator.h"
44 
45 #ifdef V8_FAST_TLS_SUPPORTED
46 #include "src/base/atomicops.h"
47 #endif
48 
49 #if V8_OS_MACOSX
50 #include <dlfcn.h>
51 #endif
52 
53 #if V8_OS_LINUX
54 #include <sys/prctl.h>  // NOLINT, for prctl
55 #endif
56 
57 #if !V8_OS_NACL
58 #include <sys/syscall.h>
59 #endif
60 
61 namespace v8 {
62 namespace base {
63 
64 namespace {
65 
66 // 0 is never a valid thread id.
67 const pthread_t kNoThread = (pthread_t) 0;
68 
69 bool g_hard_abort = false;
70 
71 const char* g_gc_fake_mmap = NULL;
72 
73 }  // namespace
74 
75 
ActivationFrameAlignment()76 int OS::ActivationFrameAlignment() {
77 #if V8_TARGET_ARCH_ARM
78   // On EABI ARM targets this is required for fp correctness in the
79   // runtime system.
80   return 8;
81 #elif V8_TARGET_ARCH_MIPS
82   return 8;
83 #else
84   // Otherwise we just assume 16 byte alignment, i.e.:
85   // - With gcc 4.4 the tree vectorization optimizer can generate code
86   //   that requires 16 byte alignment such as movdqa on x86.
87   // - Mac OS X and Solaris (64-bit) activation frames must be 16 byte-aligned;
88   //   see "Mac OS X ABI Function Call Guide"
89   return 16;
90 #endif
91 }
92 
93 
CommitPageSize()94 intptr_t OS::CommitPageSize() {
95   static intptr_t page_size = getpagesize();
96   return page_size;
97 }
98 
99 
Free(void * address,const size_t size)100 void OS::Free(void* address, const size_t size) {
101   // TODO(1240712): munmap has a return value which is ignored here.
102   int result = munmap(address, size);
103   USE(result);
104   DCHECK(result == 0);
105 }
106 
107 
108 // Get rid of writable permission on code allocations.
ProtectCode(void * address,const size_t size)109 void OS::ProtectCode(void* address, const size_t size) {
110 #if V8_OS_CYGWIN
111   DWORD old_protect;
112   VirtualProtect(address, size, PAGE_EXECUTE_READ, &old_protect);
113 #elif V8_OS_NACL
114   // The Native Client port of V8 uses an interpreter, so
115   // code pages don't need PROT_EXEC.
116   mprotect(address, size, PROT_READ);
117 #else
118   mprotect(address, size, PROT_READ | PROT_EXEC);
119 #endif
120 }
121 
122 
123 // Create guard pages.
Guard(void * address,const size_t size)124 void OS::Guard(void* address, const size_t size) {
125 #if V8_OS_CYGWIN
126   DWORD oldprotect;
127   VirtualProtect(address, size, PAGE_NOACCESS, &oldprotect);
128 #else
129   mprotect(address, size, PROT_NONE);
130 #endif
131 }
132 
133 
134 static LazyInstance<RandomNumberGenerator>::type
135     platform_random_number_generator = LAZY_INSTANCE_INITIALIZER;
136 
137 
Initialize(int64_t random_seed,bool hard_abort,const char * const gc_fake_mmap)138 void OS::Initialize(int64_t random_seed, bool hard_abort,
139                     const char* const gc_fake_mmap) {
140   if (random_seed) {
141     platform_random_number_generator.Pointer()->SetSeed(random_seed);
142   }
143   g_hard_abort = hard_abort;
144   g_gc_fake_mmap = gc_fake_mmap;
145 }
146 
147 
GetGCFakeMMapFile()148 const char* OS::GetGCFakeMMapFile() {
149   return g_gc_fake_mmap;
150 }
151 
152 
GetRandomMmapAddr()153 void* OS::GetRandomMmapAddr() {
154 #if V8_OS_NACL
155   // TODO(bradchen): restore randomization once Native Client gets
156   // smarter about using mmap address hints.
157   // See http://code.google.com/p/nativeclient/issues/3341
158   return NULL;
159 #endif
160 #if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
161     defined(THREAD_SANITIZER)
162   // Dynamic tools do not support custom mmap addresses.
163   return NULL;
164 #endif
165   uintptr_t raw_addr;
166   platform_random_number_generator.Pointer()->NextBytes(&raw_addr,
167                                                         sizeof(raw_addr));
168 #if V8_TARGET_ARCH_X64
169   // Currently available CPUs have 48 bits of virtual addressing.  Truncate
170   // the hint address to 46 bits to give the kernel a fighting chance of
171   // fulfilling our placement request.
172   raw_addr &= V8_UINT64_C(0x3ffffffff000);
173 #else
174   raw_addr &= 0x3ffff000;
175 
176 # ifdef __sun
177   // For our Solaris/illumos mmap hint, we pick a random address in the bottom
178   // half of the top half of the address space (that is, the third quarter).
179   // Because we do not MAP_FIXED, this will be treated only as a hint -- the
180   // system will not fail to mmap() because something else happens to already
181   // be mapped at our random address. We deliberately set the hint high enough
182   // to get well above the system's break (that is, the heap); Solaris and
183   // illumos will try the hint and if that fails allocate as if there were
184   // no hint at all. The high hint prevents the break from getting hemmed in
185   // at low values, ceding half of the address space to the system heap.
186   raw_addr += 0x80000000;
187 # else
188   // The range 0x20000000 - 0x60000000 is relatively unpopulated across a
189   // variety of ASLR modes (PAE kernel, NX compat mode, etc) and on macos
190   // 10.6 and 10.7.
191   raw_addr += 0x20000000;
192 # endif
193 #endif
194   return reinterpret_cast<void*>(raw_addr);
195 }
196 
197 
AllocateAlignment()198 size_t OS::AllocateAlignment() {
199   return static_cast<size_t>(sysconf(_SC_PAGESIZE));
200 }
201 
202 
Sleep(int milliseconds)203 void OS::Sleep(int milliseconds) {
204   useconds_t ms = static_cast<useconds_t>(milliseconds);
205   usleep(1000 * ms);
206 }
207 
208 
Abort()209 void OS::Abort() {
210   if (g_hard_abort) {
211     V8_IMMEDIATE_CRASH();
212   }
213   // Redirect to std abort to signal abnormal program termination.
214   abort();
215 }
216 
217 
DebugBreak()218 void OS::DebugBreak() {
219 #if V8_HOST_ARCH_ARM
220   asm("bkpt 0");
221 #elif V8_HOST_ARCH_ARM64
222   asm("brk 0");
223 #elif V8_HOST_ARCH_MIPS
224   asm("break");
225 #elif V8_HOST_ARCH_MIPS64
226   asm("break");
227 #elif V8_HOST_ARCH_IA32
228 #if V8_OS_NACL
229   asm("hlt");
230 #else
231   asm("int $3");
232 #endif  // V8_OS_NACL
233 #elif V8_HOST_ARCH_X64
234   asm("int $3");
235 #else
236 #error Unsupported host architecture.
237 #endif
238 }
239 
240 
241 // ----------------------------------------------------------------------------
242 // Math functions
243 
nan_value()244 double OS::nan_value() {
245   // NAN from math.h is defined in C99 and not in POSIX.
246   return NAN;
247 }
248 
249 
GetCurrentProcessId()250 int OS::GetCurrentProcessId() {
251   return static_cast<int>(getpid());
252 }
253 
254 
GetCurrentThreadId()255 int OS::GetCurrentThreadId() {
256 #if defined(ANDROID)
257   return static_cast<int>(syscall(__NR_gettid));
258 #elif defined(SYS_gettid)
259   return static_cast<int>(syscall(SYS_gettid));
260 #else
261   // PNaCL doesn't have a way to get an integral thread ID, but it doesn't
262   // really matter, because we only need it in PerfJitLogger::LogRecordedBuffer.
263   return 0;
264 #endif
265 }
266 
267 
268 // ----------------------------------------------------------------------------
269 // POSIX date/time support.
270 //
271 
GetUserTime(uint32_t * secs,uint32_t * usecs)272 int OS::GetUserTime(uint32_t* secs,  uint32_t* usecs) {
273 #if V8_OS_NACL
274   // Optionally used in Logger::ResourceEvent.
275   return -1;
276 #else
277   struct rusage usage;
278 
279   if (getrusage(RUSAGE_SELF, &usage) < 0) return -1;
280   *secs = usage.ru_utime.tv_sec;
281   *usecs = usage.ru_utime.tv_usec;
282   return 0;
283 #endif
284 }
285 
286 
TimeCurrentMillis()287 double OS::TimeCurrentMillis() {
288   return Time::Now().ToJsTime();
289 }
290 
291 
292 class TimezoneCache {};
293 
294 
CreateTimezoneCache()295 TimezoneCache* OS::CreateTimezoneCache() {
296   return NULL;
297 }
298 
299 
DisposeTimezoneCache(TimezoneCache * cache)300 void OS::DisposeTimezoneCache(TimezoneCache* cache) {
301   DCHECK(cache == NULL);
302 }
303 
304 
ClearTimezoneCache(TimezoneCache * cache)305 void OS::ClearTimezoneCache(TimezoneCache* cache) {
306   DCHECK(cache == NULL);
307 }
308 
309 
DaylightSavingsOffset(double time,TimezoneCache *)310 double OS::DaylightSavingsOffset(double time, TimezoneCache*) {
311   if (std::isnan(time)) return nan_value();
312   time_t tv = static_cast<time_t>(std::floor(time/msPerSecond));
313   struct tm* t = localtime(&tv);
314   if (NULL == t) return nan_value();
315   return t->tm_isdst > 0 ? 3600 * msPerSecond : 0;
316 }
317 
318 
GetLastError()319 int OS::GetLastError() {
320   return errno;
321 }
322 
323 
324 // ----------------------------------------------------------------------------
325 // POSIX stdio support.
326 //
327 
FOpen(const char * path,const char * mode)328 FILE* OS::FOpen(const char* path, const char* mode) {
329   FILE* file = fopen(path, mode);
330   if (file == NULL) return NULL;
331   struct stat file_stat;
332   if (fstat(fileno(file), &file_stat) != 0) return NULL;
333   bool is_regular_file = ((file_stat.st_mode & S_IFREG) != 0);
334   if (is_regular_file) return file;
335   fclose(file);
336   return NULL;
337 }
338 
339 
Remove(const char * path)340 bool OS::Remove(const char* path) {
341   return (remove(path) == 0);
342 }
343 
344 
OpenTemporaryFile()345 FILE* OS::OpenTemporaryFile() {
346   return tmpfile();
347 }
348 
349 
350 const char* const OS::LogFileOpenMode = "w";
351 
352 
Print(const char * format,...)353 void OS::Print(const char* format, ...) {
354   va_list args;
355   va_start(args, format);
356   VPrint(format, args);
357   va_end(args);
358 }
359 
360 
VPrint(const char * format,va_list args)361 void OS::VPrint(const char* format, va_list args) {
362 #if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
363   __android_log_vprint(ANDROID_LOG_INFO, LOG_TAG, format, args);
364 #else
365   vprintf(format, args);
366 #endif
367 }
368 
369 
FPrint(FILE * out,const char * format,...)370 void OS::FPrint(FILE* out, const char* format, ...) {
371   va_list args;
372   va_start(args, format);
373   VFPrint(out, format, args);
374   va_end(args);
375 }
376 
377 
VFPrint(FILE * out,const char * format,va_list args)378 void OS::VFPrint(FILE* out, const char* format, va_list args) {
379 #if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
380   __android_log_vprint(ANDROID_LOG_INFO, LOG_TAG, format, args);
381 #else
382   vfprintf(out, format, args);
383 #endif
384 }
385 
386 
PrintError(const char * format,...)387 void OS::PrintError(const char* format, ...) {
388   va_list args;
389   va_start(args, format);
390   VPrintError(format, args);
391   va_end(args);
392 }
393 
394 
VPrintError(const char * format,va_list args)395 void OS::VPrintError(const char* format, va_list args) {
396 #if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
397   __android_log_vprint(ANDROID_LOG_ERROR, LOG_TAG, format, args);
398 #else
399   vfprintf(stderr, format, args);
400 #endif
401 }
402 
403 
SNPrintF(char * str,int length,const char * format,...)404 int OS::SNPrintF(char* str, int length, const char* format, ...) {
405   va_list args;
406   va_start(args, format);
407   int result = VSNPrintF(str, length, format, args);
408   va_end(args);
409   return result;
410 }
411 
412 
VSNPrintF(char * str,int length,const char * format,va_list args)413 int OS::VSNPrintF(char* str,
414                   int length,
415                   const char* format,
416                   va_list args) {
417   int n = vsnprintf(str, length, format, args);
418   if (n < 0 || n >= length) {
419     // If the length is zero, the assignment fails.
420     if (length > 0)
421       str[length - 1] = '\0';
422     return -1;
423   } else {
424     return n;
425   }
426 }
427 
428 
429 // ----------------------------------------------------------------------------
430 // POSIX string support.
431 //
432 
StrChr(char * str,int c)433 char* OS::StrChr(char* str, int c) {
434   return strchr(str, c);
435 }
436 
437 
StrNCpy(char * dest,int length,const char * src,size_t n)438 void OS::StrNCpy(char* dest, int length, const char* src, size_t n) {
439   strncpy(dest, src, n);
440 }
441 
442 
443 // ----------------------------------------------------------------------------
444 // POSIX thread support.
445 //
446 
447 class Thread::PlatformData {
448  public:
PlatformData()449   PlatformData() : thread_(kNoThread) {}
450   pthread_t thread_;  // Thread handle for pthread.
451   // Synchronizes thread creation
452   Mutex thread_creation_mutex_;
453 };
454 
Thread(const Options & options)455 Thread::Thread(const Options& options)
456     : data_(new PlatformData),
457       stack_size_(options.stack_size()),
458       start_semaphore_(NULL) {
459   if (stack_size_ > 0 && static_cast<size_t>(stack_size_) < PTHREAD_STACK_MIN) {
460     stack_size_ = PTHREAD_STACK_MIN;
461   }
462   set_name(options.name());
463 }
464 
465 
~Thread()466 Thread::~Thread() {
467   delete data_;
468 }
469 
470 
SetThreadName(const char * name)471 static void SetThreadName(const char* name) {
472 #if V8_OS_DRAGONFLYBSD || V8_OS_FREEBSD || V8_OS_OPENBSD
473   pthread_set_name_np(pthread_self(), name);
474 #elif V8_OS_NETBSD
475   STATIC_ASSERT(Thread::kMaxThreadNameLength <= PTHREAD_MAX_NAMELEN_NP);
476   pthread_setname_np(pthread_self(), "%s", name);
477 #elif V8_OS_MACOSX
478   // pthread_setname_np is only available in 10.6 or later, so test
479   // for it at runtime.
480   int (*dynamic_pthread_setname_np)(const char*);
481   *reinterpret_cast<void**>(&dynamic_pthread_setname_np) =
482     dlsym(RTLD_DEFAULT, "pthread_setname_np");
483   if (dynamic_pthread_setname_np == NULL)
484     return;
485 
486   // Mac OS X does not expose the length limit of the name, so hardcode it.
487   static const int kMaxNameLength = 63;
488   STATIC_ASSERT(Thread::kMaxThreadNameLength <= kMaxNameLength);
489   dynamic_pthread_setname_np(name);
490 #elif defined(PR_SET_NAME)
491   prctl(PR_SET_NAME,
492         reinterpret_cast<unsigned long>(name),  // NOLINT
493         0, 0, 0);
494 #endif
495 }
496 
497 
ThreadEntry(void * arg)498 static void* ThreadEntry(void* arg) {
499   Thread* thread = reinterpret_cast<Thread*>(arg);
500   // We take the lock here to make sure that pthread_create finished first since
501   // we don't know which thread will run first (the original thread or the new
502   // one).
503   { LockGuard<Mutex> lock_guard(&thread->data()->thread_creation_mutex_); }
504   SetThreadName(thread->name());
505   DCHECK(thread->data()->thread_ != kNoThread);
506   thread->NotifyStartedAndRun();
507   return NULL;
508 }
509 
510 
set_name(const char * name)511 void Thread::set_name(const char* name) {
512   strncpy(name_, name, sizeof(name_));
513   name_[sizeof(name_) - 1] = '\0';
514 }
515 
516 
Start()517 void Thread::Start() {
518   int result;
519   pthread_attr_t attr;
520   memset(&attr, 0, sizeof(attr));
521   result = pthread_attr_init(&attr);
522   DCHECK_EQ(0, result);
523   // Native client uses default stack size.
524 #if !V8_OS_NACL
525   if (stack_size_ > 0) {
526     result = pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
527     DCHECK_EQ(0, result);
528   }
529 #endif
530   {
531     LockGuard<Mutex> lock_guard(&data_->thread_creation_mutex_);
532     result = pthread_create(&data_->thread_, &attr, ThreadEntry, this);
533   }
534   DCHECK_EQ(0, result);
535   result = pthread_attr_destroy(&attr);
536   DCHECK_EQ(0, result);
537   DCHECK(data_->thread_ != kNoThread);
538   USE(result);
539 }
540 
541 
Join()542 void Thread::Join() {
543   pthread_join(data_->thread_, NULL);
544 }
545 
546 
YieldCPU()547 void Thread::YieldCPU() {
548   int result = sched_yield();
549   DCHECK_EQ(0, result);
550   USE(result);
551 }
552 
553 
PthreadKeyToLocalKey(pthread_key_t pthread_key)554 static Thread::LocalStorageKey PthreadKeyToLocalKey(pthread_key_t pthread_key) {
555 #if V8_OS_CYGWIN
556   // We need to cast pthread_key_t to Thread::LocalStorageKey in two steps
557   // because pthread_key_t is a pointer type on Cygwin. This will probably not
558   // work on 64-bit platforms, but Cygwin doesn't support 64-bit anyway.
559   STATIC_ASSERT(sizeof(Thread::LocalStorageKey) == sizeof(pthread_key_t));
560   intptr_t ptr_key = reinterpret_cast<intptr_t>(pthread_key);
561   return static_cast<Thread::LocalStorageKey>(ptr_key);
562 #else
563   return static_cast<Thread::LocalStorageKey>(pthread_key);
564 #endif
565 }
566 
567 
LocalKeyToPthreadKey(Thread::LocalStorageKey local_key)568 static pthread_key_t LocalKeyToPthreadKey(Thread::LocalStorageKey local_key) {
569 #if V8_OS_CYGWIN
570   STATIC_ASSERT(sizeof(Thread::LocalStorageKey) == sizeof(pthread_key_t));
571   intptr_t ptr_key = static_cast<intptr_t>(local_key);
572   return reinterpret_cast<pthread_key_t>(ptr_key);
573 #else
574   return static_cast<pthread_key_t>(local_key);
575 #endif
576 }
577 
578 
579 #ifdef V8_FAST_TLS_SUPPORTED
580 
581 static Atomic32 tls_base_offset_initialized = 0;
582 intptr_t kMacTlsBaseOffset = 0;
583 
584 // It's safe to do the initialization more that once, but it has to be
585 // done at least once.
InitializeTlsBaseOffset()586 static void InitializeTlsBaseOffset() {
587   const size_t kBufferSize = 128;
588   char buffer[kBufferSize];
589   size_t buffer_size = kBufferSize;
590   int ctl_name[] = { CTL_KERN , KERN_OSRELEASE };
591   if (sysctl(ctl_name, 2, buffer, &buffer_size, NULL, 0) != 0) {
592     V8_Fatal(__FILE__, __LINE__, "V8 failed to get kernel version");
593   }
594   // The buffer now contains a string of the form XX.YY.ZZ, where
595   // XX is the major kernel version component.
596   // Make sure the buffer is 0-terminated.
597   buffer[kBufferSize - 1] = '\0';
598   char* period_pos = strchr(buffer, '.');
599   *period_pos = '\0';
600   int kernel_version_major =
601       static_cast<int>(strtol(buffer, NULL, 10));  // NOLINT
602   // The constants below are taken from pthreads.s from the XNU kernel
603   // sources archive at www.opensource.apple.com.
604   if (kernel_version_major < 11) {
605     // 8.x.x (Tiger), 9.x.x (Leopard), 10.x.x (Snow Leopard) have the
606     // same offsets.
607 #if V8_HOST_ARCH_IA32
608     kMacTlsBaseOffset = 0x48;
609 #else
610     kMacTlsBaseOffset = 0x60;
611 #endif
612   } else {
613     // 11.x.x (Lion) changed the offset.
614     kMacTlsBaseOffset = 0;
615   }
616 
617   Release_Store(&tls_base_offset_initialized, 1);
618 }
619 
620 
CheckFastTls(Thread::LocalStorageKey key)621 static void CheckFastTls(Thread::LocalStorageKey key) {
622   void* expected = reinterpret_cast<void*>(0x1234CAFE);
623   Thread::SetThreadLocal(key, expected);
624   void* actual = Thread::GetExistingThreadLocal(key);
625   if (expected != actual) {
626     V8_Fatal(__FILE__, __LINE__,
627              "V8 failed to initialize fast TLS on current kernel");
628   }
629   Thread::SetThreadLocal(key, NULL);
630 }
631 
632 #endif  // V8_FAST_TLS_SUPPORTED
633 
634 
CreateThreadLocalKey()635 Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
636 #ifdef V8_FAST_TLS_SUPPORTED
637   bool check_fast_tls = false;
638   if (tls_base_offset_initialized == 0) {
639     check_fast_tls = true;
640     InitializeTlsBaseOffset();
641   }
642 #endif
643   pthread_key_t key;
644   int result = pthread_key_create(&key, NULL);
645   DCHECK_EQ(0, result);
646   USE(result);
647   LocalStorageKey local_key = PthreadKeyToLocalKey(key);
648 #ifdef V8_FAST_TLS_SUPPORTED
649   // If we just initialized fast TLS support, make sure it works.
650   if (check_fast_tls) CheckFastTls(local_key);
651 #endif
652   return local_key;
653 }
654 
655 
DeleteThreadLocalKey(LocalStorageKey key)656 void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
657   pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
658   int result = pthread_key_delete(pthread_key);
659   DCHECK_EQ(0, result);
660   USE(result);
661 }
662 
663 
GetThreadLocal(LocalStorageKey key)664 void* Thread::GetThreadLocal(LocalStorageKey key) {
665   pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
666   return pthread_getspecific(pthread_key);
667 }
668 
669 
SetThreadLocal(LocalStorageKey key,void * value)670 void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
671   pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
672   int result = pthread_setspecific(pthread_key, value);
673   DCHECK_EQ(0, result);
674   USE(result);
675 }
676 
677 
678 } }  // namespace v8::base
679