v8/src/platform-posix.cc
bmeurer@chromium.org e9fcf8fc98 Revert the latest set of platform changes.
Revert "Fix NaCl build."
Revert "Revert target arch detection."
Revert "Fix typo."
Revert "Simplify implementation of Mutex."
Revert "Fix for older clang releases that lack __has_extension."
Revert "Reland initial bits of "Implement correct OS and CC detection.""

TBR=danno@chromium.org,svenpanne@chromium.org

Review URL: https://codereview.chromium.org/21095008

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@15976 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2013-07-31 07:51:46 +00:00

1000 lines
26 KiB
C++

// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Platform specific code for POSIX goes here. This is not a platform on its
// own but contains the parts which are the same across POSIX platforms Linux,
// Mac OS, FreeBSD and OpenBSD.
#include "platform-posix.h"
#include <dlfcn.h>
#include <pthread.h>
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
#include <pthread_np.h> // for pthread_set_name_np
#endif
#include <sched.h> // for sched_yield
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#if defined(__linux__)
#include <sys/prctl.h> // for prctl
#endif
#if defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__) || \
defined(__NetBSD__) || defined(__OpenBSD__)
#include <sys/sysctl.h> // for sysctl
#endif
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#undef MAP_TYPE
#if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
#define LOG_TAG "v8"
#include <android/log.h>
#endif
#include "v8.h"
#include "codegen.h"
#include "platform.h"
namespace v8 {
namespace internal {
// 0 is never a valid thread id.
static const pthread_t kNoThread = (pthread_t) 0;
uint64_t OS::CpuFeaturesImpliedByPlatform() {
#if defined(__APPLE__)
// Mac OS X requires all these to install so we can assume they are present.
// These constants are defined by the CPUid instructions.
const uint64_t one = 1;
return (one << SSE2) | (one << CMOV) | (one << RDTSC) | (one << CPUID);
#else
return 0; // Nothing special about the other systems.
#endif
}
// Maximum size of the virtual memory. 0 means there is no artificial
// limit.
intptr_t OS::MaxVirtualMemory() {
struct rlimit limit;
int result = getrlimit(RLIMIT_DATA, &limit);
if (result != 0) return 0;
return limit.rlim_cur;
}
int OS::ActivationFrameAlignment() {
#if V8_TARGET_ARCH_ARM
// On EABI ARM targets this is required for fp correctness in the
// runtime system.
return 8;
#elif V8_TARGET_ARCH_MIPS
return 8;
#else
// Otherwise we just assume 16 byte alignment, i.e.:
// - With gcc 4.4 the tree vectorization optimizer can generate code
// that requires 16 byte alignment such as movdqa on x86.
// - Mac OS X and Solaris (64-bit) activation frames must be 16 byte-aligned;
// see "Mac OS X ABI Function Call Guide"
return 16;
#endif
}
intptr_t OS::CommitPageSize() {
static intptr_t page_size = getpagesize();
return page_size;
}
void OS::Free(void* address, const size_t size) {
// TODO(1240712): munmap has a return value which is ignored here.
int result = munmap(address, size);
USE(result);
ASSERT(result == 0);
}
// Get rid of writable permission on code allocations.
void OS::ProtectCode(void* address, const size_t size) {
#if defined(__CYGWIN__)
DWORD old_protect;
VirtualProtect(address, size, PAGE_EXECUTE_READ, &old_protect);
#elif defined(__native_client__)
// The Native Client port of V8 uses an interpreter, so
// code pages don't need PROT_EXEC.
mprotect(address, size, PROT_READ);
#else
mprotect(address, size, PROT_READ | PROT_EXEC);
#endif
}
// Create guard pages.
void OS::Guard(void* address, const size_t size) {
#if defined(__CYGWIN__)
DWORD oldprotect;
VirtualProtect(address, size, PAGE_READONLY | PAGE_GUARD, &oldprotect);
#else
mprotect(address, size, PROT_NONE);
#endif
}
void* OS::GetRandomMmapAddr() {
#if defined(__native_client__)
// TODO(bradchen): restore randomization once Native Client gets
// smarter about using mmap address hints.
// See http://code.google.com/p/nativeclient/issues/3341
return NULL;
#endif
Isolate* isolate = Isolate::UncheckedCurrent();
// Note that the current isolate isn't set up in a call path via
// CpuFeatures::Probe. We don't care about randomization in this case because
// the code page is immediately freed.
if (isolate != NULL) {
#if V8_TARGET_ARCH_X64
uint64_t rnd1 = V8::RandomPrivate(isolate);
uint64_t rnd2 = V8::RandomPrivate(isolate);
uint64_t raw_addr = (rnd1 << 32) ^ rnd2;
// Currently available CPUs have 48 bits of virtual addressing. Truncate
// the hint address to 46 bits to give the kernel a fighting chance of
// fulfilling our placement request.
raw_addr &= V8_UINT64_C(0x3ffffffff000);
#else
uint32_t raw_addr = V8::RandomPrivate(isolate);
raw_addr &= 0x3ffff000;
# ifdef __sun
// For our Solaris/illumos mmap hint, we pick a random address in the bottom
// half of the top half of the address space (that is, the third quarter).
// Because we do not MAP_FIXED, this will be treated only as a hint -- the
// system will not fail to mmap() because something else happens to already
// be mapped at our random address. We deliberately set the hint high enough
// to get well above the system's break (that is, the heap); Solaris and
// illumos will try the hint and if that fails allocate as if there were
// no hint at all. The high hint prevents the break from getting hemmed in
// at low values, ceding half of the address space to the system heap.
raw_addr += 0x80000000;
# else
// The range 0x20000000 - 0x60000000 is relatively unpopulated across a
// variety of ASLR modes (PAE kernel, NX compat mode, etc) and on macos
// 10.6 and 10.7.
raw_addr += 0x20000000;
# endif
#endif
return reinterpret_cast<void*>(raw_addr);
}
return NULL;
}
size_t OS::AllocateAlignment() {
return getpagesize();
}
void OS::Sleep(int milliseconds) {
useconds_t ms = static_cast<useconds_t>(milliseconds);
usleep(1000 * ms);
}
int OS::NumberOfCores() {
return sysconf(_SC_NPROCESSORS_ONLN);
}
void OS::Abort() {
// Redirect to std abort to signal abnormal program termination.
if (FLAG_break_on_abort) {
DebugBreak();
}
abort();
}
void OS::DebugBreak() {
#if V8_HOST_ARCH_ARM
asm("bkpt 0");
#elif V8_HOST_ARCH_MIPS
asm("break");
#elif V8_HOST_ARCH_IA32
#if defined(__native_client__)
asm("hlt");
#else
asm("int $3");
#endif // __native_client__
#elif V8_HOST_ARCH_X64
asm("int $3");
#else
#error Unsupported host architecture.
#endif
}
// ----------------------------------------------------------------------------
// Math functions
double ceiling(double x) {
// Correct buggy 'ceil' on some systems (i.e. FreeBSD, OS X 10.5)
return (-1.0 < x && x < 0.0) ? -0.0 : ceil(x);
}
double modulo(double x, double y) {
return fmod(x, y);
}
#define UNARY_MATH_FUNCTION(name, generator) \
static UnaryMathFunction fast_##name##_function = NULL; \
void init_fast_##name##_function() { \
fast_##name##_function = generator; \
} \
double fast_##name(double x) { \
return (*fast_##name##_function)(x); \
}
UNARY_MATH_FUNCTION(sin, CreateTranscendentalFunction(TranscendentalCache::SIN))
UNARY_MATH_FUNCTION(cos, CreateTranscendentalFunction(TranscendentalCache::COS))
UNARY_MATH_FUNCTION(tan, CreateTranscendentalFunction(TranscendentalCache::TAN))
UNARY_MATH_FUNCTION(log, CreateTranscendentalFunction(TranscendentalCache::LOG))
UNARY_MATH_FUNCTION(exp, CreateExpFunction())
UNARY_MATH_FUNCTION(sqrt, CreateSqrtFunction())
#undef UNARY_MATH_FUNCTION
void lazily_initialize_fast_exp() {
if (fast_exp_function == NULL) {
init_fast_exp_function();
}
}
double OS::nan_value() {
// NAN from math.h is defined in C99 and not in POSIX.
return NAN;
}
int OS::GetCurrentProcessId() {
return static_cast<int>(getpid());
}
// ----------------------------------------------------------------------------
// POSIX date/time support.
//
int OS::GetUserTime(uint32_t* secs, uint32_t* usecs) {
struct rusage usage;
if (getrusage(RUSAGE_SELF, &usage) < 0) return -1;
*secs = usage.ru_utime.tv_sec;
*usecs = usage.ru_utime.tv_usec;
return 0;
}
double OS::TimeCurrentMillis() {
struct timeval tv;
if (gettimeofday(&tv, NULL) < 0) return 0.0;
return (static_cast<double>(tv.tv_sec) * 1000) +
(static_cast<double>(tv.tv_usec) / 1000);
}
int64_t OS::Ticks() {
// gettimeofday has microsecond resolution.
struct timeval tv;
if (gettimeofday(&tv, NULL) < 0)
return 0;
return (static_cast<int64_t>(tv.tv_sec) * 1000000) + tv.tv_usec;
}
double OS::DaylightSavingsOffset(double time) {
if (std::isnan(time)) return nan_value();
time_t tv = static_cast<time_t>(floor(time/msPerSecond));
struct tm* t = localtime(&tv);
if (NULL == t) return nan_value();
return t->tm_isdst > 0 ? 3600 * msPerSecond : 0;
}
int OS::GetLastError() {
return errno;
}
// ----------------------------------------------------------------------------
// POSIX stdio support.
//
FILE* OS::FOpen(const char* path, const char* mode) {
FILE* file = fopen(path, mode);
if (file == NULL) return NULL;
struct stat file_stat;
if (fstat(fileno(file), &file_stat) != 0) return NULL;
bool is_regular_file = ((file_stat.st_mode & S_IFREG) != 0);
if (is_regular_file) return file;
fclose(file);
return NULL;
}
bool OS::Remove(const char* path) {
return (remove(path) == 0);
}
FILE* OS::OpenTemporaryFile() {
return tmpfile();
}
const char* const OS::LogFileOpenMode = "w";
void OS::Print(const char* format, ...) {
va_list args;
va_start(args, format);
VPrint(format, args);
va_end(args);
}
void OS::VPrint(const char* format, va_list args) {
#if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
__android_log_vprint(ANDROID_LOG_INFO, LOG_TAG, format, args);
#else
vprintf(format, args);
#endif
}
void OS::FPrint(FILE* out, const char* format, ...) {
va_list args;
va_start(args, format);
VFPrint(out, format, args);
va_end(args);
}
void OS::VFPrint(FILE* out, const char* format, va_list args) {
#if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
__android_log_vprint(ANDROID_LOG_INFO, LOG_TAG, format, args);
#else
vfprintf(out, format, args);
#endif
}
void OS::PrintError(const char* format, ...) {
va_list args;
va_start(args, format);
VPrintError(format, args);
va_end(args);
}
void OS::VPrintError(const char* format, va_list args) {
#if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
__android_log_vprint(ANDROID_LOG_ERROR, LOG_TAG, format, args);
#else
vfprintf(stderr, format, args);
#endif
}
int OS::SNPrintF(Vector<char> str, const char* format, ...) {
va_list args;
va_start(args, format);
int result = VSNPrintF(str, format, args);
va_end(args);
return result;
}
int OS::VSNPrintF(Vector<char> str,
const char* format,
va_list args) {
int n = vsnprintf(str.start(), str.length(), format, args);
if (n < 0 || n >= str.length()) {
// If the length is zero, the assignment fails.
if (str.length() > 0)
str[str.length() - 1] = '\0';
return -1;
} else {
return n;
}
}
#if V8_TARGET_ARCH_IA32
static void MemMoveWrapper(void* dest, const void* src, size_t size) {
memmove(dest, src, size);
}
// Initialize to library version so we can call this at any time during startup.
static OS::MemMoveFunction memmove_function = &MemMoveWrapper;
// Defined in codegen-ia32.cc.
OS::MemMoveFunction CreateMemMoveFunction();
// Copy memory area. No restrictions.
void OS::MemMove(void* dest, const void* src, size_t size) {
if (size == 0) return;
// Note: here we rely on dependent reads being ordered. This is true
// on all architectures we currently support.
(*memmove_function)(dest, src, size);
}
#elif defined(V8_HOST_ARCH_ARM)
void OS::MemCopyUint16Uint8Wrapper(uint16_t* dest,
const uint8_t* src,
size_t chars) {
uint16_t *limit = dest + chars;
while (dest < limit) {
*dest++ = static_cast<uint16_t>(*src++);
}
}
OS::MemCopyUint8Function OS::memcopy_uint8_function = &OS::MemCopyUint8Wrapper;
OS::MemCopyUint16Uint8Function OS::memcopy_uint16_uint8_function =
&OS::MemCopyUint16Uint8Wrapper;
// Defined in codegen-arm.cc.
OS::MemCopyUint8Function CreateMemCopyUint8Function(
OS::MemCopyUint8Function stub);
OS::MemCopyUint16Uint8Function CreateMemCopyUint16Uint8Function(
OS::MemCopyUint16Uint8Function stub);
#endif
void OS::PostSetUp() {
#if V8_TARGET_ARCH_IA32
OS::MemMoveFunction generated_memmove = CreateMemMoveFunction();
if (generated_memmove != NULL) {
memmove_function = generated_memmove;
}
#elif defined(V8_HOST_ARCH_ARM)
OS::memcopy_uint8_function =
CreateMemCopyUint8Function(&OS::MemCopyUint8Wrapper);
OS::memcopy_uint16_uint8_function =
CreateMemCopyUint16Uint8Function(&OS::MemCopyUint16Uint8Wrapper);
#endif
init_fast_sin_function();
init_fast_cos_function();
init_fast_tan_function();
init_fast_log_function();
// fast_exp is initialized lazily.
init_fast_sqrt_function();
}
// ----------------------------------------------------------------------------
// POSIX string support.
//
char* OS::StrChr(char* str, int c) {
return strchr(str, c);
}
void OS::StrNCpy(Vector<char> dest, const char* src, size_t n) {
strncpy(dest.start(), src, n);
}
// ----------------------------------------------------------------------------
// POSIX thread support.
//
class Thread::PlatformData : public Malloced {
public:
PlatformData() : thread_(kNoThread) {}
pthread_t thread_; // Thread handle for pthread.
};
Thread::Thread(const Options& options)
: data_(new PlatformData),
stack_size_(options.stack_size()),
start_semaphore_(NULL) {
set_name(options.name());
}
Thread::~Thread() {
delete data_;
}
static void SetThreadName(const char* name) {
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
pthread_set_name_np(pthread_self(), name);
#elif defined(__NetBSD__)
STATIC_ASSERT(Thread::kMaxThreadNameLength <= PTHREAD_MAX_NAMELEN_NP);
pthread_setname_np(pthread_self(), "%s", name);
#elif defined(__APPLE__)
// pthread_setname_np is only available in 10.6 or later, so test
// for it at runtime.
int (*dynamic_pthread_setname_np)(const char*);
*reinterpret_cast<void**>(&dynamic_pthread_setname_np) =
dlsym(RTLD_DEFAULT, "pthread_setname_np");
if (dynamic_pthread_setname_np == NULL)
return;
// Mac OS X does not expose the length limit of the name, so hardcode it.
static const int kMaxNameLength = 63;
STATIC_ASSERT(Thread::kMaxThreadNameLength <= kMaxNameLength);
dynamic_pthread_setname_np(name);
#elif defined(PR_SET_NAME)
prctl(PR_SET_NAME,
reinterpret_cast<unsigned long>(name), // NOLINT
0, 0, 0);
#endif
}
static void* ThreadEntry(void* arg) {
Thread* thread = reinterpret_cast<Thread*>(arg);
// This is also initialized by the first argument to pthread_create() but we
// don't know which thread will run first (the original thread or the new
// one) so we initialize it here too.
thread->data()->thread_ = pthread_self();
SetThreadName(thread->name());
ASSERT(thread->data()->thread_ != kNoThread);
thread->NotifyStartedAndRun();
return NULL;
}
void Thread::set_name(const char* name) {
strncpy(name_, name, sizeof(name_));
name_[sizeof(name_) - 1] = '\0';
}
void Thread::Start() {
int result;
pthread_attr_t attr;
memset(&attr, 0, sizeof(attr));
result = pthread_attr_init(&attr);
ASSERT_EQ(0, result);
// Native client uses default stack size.
#if !defined(__native_client__)
if (stack_size_ > 0) {
result = pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
ASSERT_EQ(0, result);
}
#endif
result = pthread_create(&data_->thread_, &attr, ThreadEntry, this);
ASSERT_EQ(0, result);
result = pthread_attr_destroy(&attr);
ASSERT_EQ(0, result);
ASSERT(data_->thread_ != kNoThread);
USE(result);
}
void Thread::Join() {
pthread_join(data_->thread_, NULL);
}
void Thread::YieldCPU() {
int result = sched_yield();
ASSERT_EQ(0, result);
USE(result);
}
static Thread::LocalStorageKey PthreadKeyToLocalKey(pthread_key_t pthread_key) {
#if defined(__CYGWIN__)
// We need to cast pthread_key_t to Thread::LocalStorageKey in two steps
// because pthread_key_t is a pointer type on Cygwin. This will probably not
// work on 64-bit platforms, but Cygwin doesn't support 64-bit anyway.
STATIC_ASSERT(sizeof(Thread::LocalStorageKey) == sizeof(pthread_key_t));
intptr_t ptr_key = reinterpret_cast<intptr_t>(pthread_key);
return static_cast<Thread::LocalStorageKey>(ptr_key);
#else
return static_cast<Thread::LocalStorageKey>(pthread_key);
#endif
}
static pthread_key_t LocalKeyToPthreadKey(Thread::LocalStorageKey local_key) {
#if defined(__CYGWIN__)
STATIC_ASSERT(sizeof(Thread::LocalStorageKey) == sizeof(pthread_key_t));
intptr_t ptr_key = static_cast<intptr_t>(local_key);
return reinterpret_cast<pthread_key_t>(ptr_key);
#else
return static_cast<pthread_key_t>(local_key);
#endif
}
#ifdef V8_FAST_TLS_SUPPORTED
static Atomic32 tls_base_offset_initialized = 0;
intptr_t kMacTlsBaseOffset = 0;
// It's safe to do the initialization more that once, but it has to be
// done at least once.
static void InitializeTlsBaseOffset() {
const size_t kBufferSize = 128;
char buffer[kBufferSize];
size_t buffer_size = kBufferSize;
int ctl_name[] = { CTL_KERN , KERN_OSRELEASE };
if (sysctl(ctl_name, 2, buffer, &buffer_size, NULL, 0) != 0) {
V8_Fatal(__FILE__, __LINE__, "V8 failed to get kernel version");
}
// The buffer now contains a string of the form XX.YY.ZZ, where
// XX is the major kernel version component.
// Make sure the buffer is 0-terminated.
buffer[kBufferSize - 1] = '\0';
char* period_pos = strchr(buffer, '.');
*period_pos = '\0';
int kernel_version_major =
static_cast<int>(strtol(buffer, NULL, 10)); // NOLINT
// The constants below are taken from pthreads.s from the XNU kernel
// sources archive at www.opensource.apple.com.
if (kernel_version_major < 11) {
// 8.x.x (Tiger), 9.x.x (Leopard), 10.x.x (Snow Leopard) have the
// same offsets.
#if V8_HOST_ARCH_IA32
kMacTlsBaseOffset = 0x48;
#else
kMacTlsBaseOffset = 0x60;
#endif
} else {
// 11.x.x (Lion) changed the offset.
kMacTlsBaseOffset = 0;
}
Release_Store(&tls_base_offset_initialized, 1);
}
static void CheckFastTls(Thread::LocalStorageKey key) {
void* expected = reinterpret_cast<void*>(0x1234CAFE);
Thread::SetThreadLocal(key, expected);
void* actual = Thread::GetExistingThreadLocal(key);
if (expected != actual) {
V8_Fatal(__FILE__, __LINE__,
"V8 failed to initialize fast TLS on current kernel");
}
Thread::SetThreadLocal(key, NULL);
}
#endif // V8_FAST_TLS_SUPPORTED
Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
#ifdef V8_FAST_TLS_SUPPORTED
bool check_fast_tls = false;
if (tls_base_offset_initialized == 0) {
check_fast_tls = true;
InitializeTlsBaseOffset();
}
#endif
pthread_key_t key;
int result = pthread_key_create(&key, NULL);
ASSERT_EQ(0, result);
USE(result);
LocalStorageKey local_key = PthreadKeyToLocalKey(key);
#ifdef V8_FAST_TLS_SUPPORTED
// If we just initialized fast TLS support, make sure it works.
if (check_fast_tls) CheckFastTls(local_key);
#endif
return local_key;
}
void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
int result = pthread_key_delete(pthread_key);
ASSERT_EQ(0, result);
USE(result);
}
void* Thread::GetThreadLocal(LocalStorageKey key) {
pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
return pthread_getspecific(pthread_key);
}
void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
int result = pthread_setspecific(pthread_key, value);
ASSERT_EQ(0, result);
USE(result);
}
class POSIXMutex : public Mutex {
public:
POSIXMutex() {
pthread_mutexattr_t attr;
memset(&attr, 0, sizeof(attr));
int result = pthread_mutexattr_init(&attr);
ASSERT(result == 0);
result = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
ASSERT(result == 0);
result = pthread_mutex_init(&mutex_, &attr);
ASSERT(result == 0);
result = pthread_mutexattr_destroy(&attr);
ASSERT(result == 0);
USE(result);
}
virtual ~POSIXMutex() { pthread_mutex_destroy(&mutex_); }
virtual int Lock() { return pthread_mutex_lock(&mutex_); }
virtual int Unlock() { return pthread_mutex_unlock(&mutex_); }
virtual bool TryLock() {
int result = pthread_mutex_trylock(&mutex_);
// Return false if the lock is busy and locking failed.
if (result == EBUSY) {
return false;
}
ASSERT(result == 0); // Verify no other errors.
return true;
}
private:
pthread_mutex_t mutex_; // Pthread mutex for POSIX platforms.
};
Mutex* OS::CreateMutex() {
return new POSIXMutex();
}
// ----------------------------------------------------------------------------
// POSIX socket support.
//
class POSIXSocket : public Socket {
public:
explicit POSIXSocket() {
// Create the socket.
socket_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (IsValid()) {
// Allow rapid reuse.
static const int kOn = 1;
int ret = setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR,
&kOn, sizeof(kOn));
ASSERT(ret == 0);
USE(ret);
}
}
explicit POSIXSocket(int socket): socket_(socket) { }
virtual ~POSIXSocket() { Shutdown(); }
// Server initialization.
bool Bind(const int port);
bool Listen(int backlog) const;
Socket* Accept() const;
// Client initialization.
bool Connect(const char* host, const char* port);
// Shutdown socket for both read and write.
bool Shutdown();
// Data Transimission
int Send(const char* data, int len) const;
int Receive(char* data, int len) const;
bool SetReuseAddress(bool reuse_address);
bool IsValid() const { return socket_ != -1; }
private:
int socket_;
};
bool POSIXSocket::Bind(const int port) {
if (!IsValid()) {
return false;
}
sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
addr.sin_port = htons(port);
int status = bind(socket_,
BitCast<struct sockaddr *>(&addr),
sizeof(addr));
return status == 0;
}
bool POSIXSocket::Listen(int backlog) const {
if (!IsValid()) {
return false;
}
int status = listen(socket_, backlog);
return status == 0;
}
Socket* POSIXSocket::Accept() const {
if (!IsValid()) {
return NULL;
}
int socket;
do {
socket = accept(socket_, NULL, NULL);
} while (socket == -1 && errno == EINTR);
if (socket == -1) {
return NULL;
} else {
return new POSIXSocket(socket);
}
}
bool POSIXSocket::Connect(const char* host, const char* port) {
if (!IsValid()) {
return false;
}
// Lookup host and port.
struct addrinfo *result = NULL;
struct addrinfo hints;
memset(&hints, 0, sizeof(addrinfo));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
int status = getaddrinfo(host, port, &hints, &result);
if (status != 0) {
return false;
}
// Connect.
do {
status = connect(socket_, result->ai_addr, result->ai_addrlen);
} while (status == -1 && errno == EINTR);
freeaddrinfo(result);
return status == 0;
}
bool POSIXSocket::Shutdown() {
if (IsValid()) {
// Shutdown socket for both read and write.
int status = shutdown(socket_, SHUT_RDWR);
close(socket_);
socket_ = -1;
return status == 0;
}
return true;
}
int POSIXSocket::Send(const char* data, int len) const {
if (len <= 0) return 0;
int written = 0;
while (written < len) {
int status = send(socket_, data + written, len - written, 0);
if (status == 0) {
break;
} else if (status > 0) {
written += status;
} else if (errno != EINTR) {
return 0;
}
}
return written;
}
int POSIXSocket::Receive(char* data, int len) const {
if (len <= 0) return 0;
int status;
do {
status = recv(socket_, data, len, 0);
} while (status == -1 && errno == EINTR);
return (status < 0) ? 0 : status;
}
bool POSIXSocket::SetReuseAddress(bool reuse_address) {
int on = reuse_address ? 1 : 0;
int status = setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
return status == 0;
}
bool Socket::SetUp() {
// Nothing to do on POSIX.
return true;
}
int Socket::LastError() {
return errno;
}
uint16_t Socket::HToN(uint16_t value) {
return htons(value);
}
uint16_t Socket::NToH(uint16_t value) {
return ntohs(value);
}
uint32_t Socket::HToN(uint32_t value) {
return htonl(value);
}
uint32_t Socket::NToH(uint32_t value) {
return ntohl(value);
}
Socket* OS::CreateSocket() {
return new POSIXSocket();
}
} } // namespace v8::internal