Import ConditionVariable class.
authorbmeurer@chromium.org <bmeurer@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 3 Sep 2013 07:30:01 +0000 (07:30 +0000)
committerbmeurer@chromium.org <bmeurer@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 3 Sep 2013 07:30:01 +0000 (07:30 +0000)
Condition variables are synchronization primitives that can be used
to block one or more threads while waiting for condition to become
true.

Right now we have only semaphores, mutexes and atomic operations for
synchronization, which results in quite complex solutions where an
implementation using condition variables and mutexes would be straight
forward.

There's also a performance benefit to condition variables and mutexes
vs semaphores, especially on Windows, where semaphores are kernel
objects, while mutexes are implemented as fast critical sections,
it CAN be beneficial performance-wise to use condition variables
instead of semaphores.

R=mstarzinger@chromium.org

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@16492 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/platform/condition-variable.cc [new file with mode: 0644]
src/platform/condition-variable.h [new file with mode: 0644]
src/platform/mutex.cc
src/platform/mutex.h
src/platform/time.cc
src/platform/time.h
src/x64/disasm-x64.cc
test/cctest/cctest.gyp
test/cctest/test-condition-variable.cc [new file with mode: 0644]
test/cctest/test-platform-linux.cc
tools/gyp/v8.gyp

diff --git a/src/platform/condition-variable.cc b/src/platform/condition-variable.cc
new file mode 100644 (file)
index 0000000..84df976
--- /dev/null
@@ -0,0 +1,345 @@
+// Copyright 2013 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.
+
+#include "platform/condition-variable.h"
+
+#include <cerrno>
+#include <ctime>
+
+#include "platform/time.h"
+
+namespace v8 {
+namespace internal {
+
+#if V8_OS_POSIX
+
+ConditionVariable::ConditionVariable() {
+  // TODO(bmeurer): The test for V8_LIBRT_NOT_AVAILABLE is a temporary
+  // hack to support cross-compiling Chrome for Android in AOSP. Remove
+  // this once AOSP is fixed.
+#if (V8_OS_FREEBSD || V8_OS_NETBSD || V8_OS_OPENBSD || V8_LIBC_GLIBC) && \
+    !V8_LIBRT_NOT_AVAILABLE
+  // On Free/Net/OpenBSD and Linux with glibc we can change the time
+  // source for pthread_cond_timedwait() to use the monotonic clock.
+  pthread_condattr_t attr;
+  int result = pthread_condattr_init(&attr);
+  ASSERT_EQ(0, result);
+  result = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
+  ASSERT_EQ(0, result);
+  result = pthread_cond_init(&native_handle_, &attr);
+  ASSERT_EQ(0, result);
+  result = pthread_condattr_destroy(&attr);
+#else
+  int result = pthread_cond_init(&native_handle_, NULL);
+#endif
+  ASSERT_EQ(0, result);
+  USE(result);
+}
+
+
+ConditionVariable::~ConditionVariable() {
+  int result = pthread_cond_destroy(&native_handle_);
+  ASSERT_EQ(0, result);
+  USE(result);
+}
+
+
+void ConditionVariable::NotifyOne() {
+  int result = pthread_cond_signal(&native_handle_);
+  ASSERT_EQ(0, result);
+  USE(result);
+}
+
+
+void ConditionVariable::NotifyAll() {
+  int result = pthread_cond_broadcast(&native_handle_);
+  ASSERT_EQ(0, result);
+  USE(result);
+}
+
+
+void ConditionVariable::Wait(Mutex* mutex) {
+  mutex->AssertHeldAndUnmark();
+  int result = pthread_cond_wait(&native_handle_, &mutex->native_handle());
+  ASSERT_EQ(0, result);
+  USE(result);
+  mutex->AssertUnheldAndMark();
+}
+
+
+bool ConditionVariable::WaitFor(Mutex* mutex, const TimeDelta& rel_time) {
+  struct timespec ts;
+  int result;
+  mutex->AssertHeldAndUnmark();
+#if V8_OS_MACOSX
+  // Mac OS X provides pthread_cond_timedwait_relative_np(), which does
+  // not depend on the real time clock, which is what you really WANT here!
+  ts = rel_time.ToTimespec();
+  ASSERT_GE(ts.tv_sec, 0);
+  ASSERT_GE(ts.tv_nsec, 0);
+  result = pthread_cond_timedwait_relative_np(
+      &native_handle_, &mutex->native_handle(), &ts);
+#else
+  // TODO(bmeurer): The test for V8_LIBRT_NOT_AVAILABLE is a temporary
+  // hack to support cross-compiling Chrome for Android in AOSP. Remove
+  // this once AOSP is fixed.
+#if (V8_OS_FREEBSD || V8_OS_NETBSD || V8_OS_OPENBSD || V8_LIBC_GLIBC) && \
+    !V8_LIBRT_NOT_AVAILABLE
+  // On Free/Net/OpenBSD and Linux with glibc we can change the time
+  // source for pthread_cond_timedwait() to use the monotonic clock.
+  result = clock_gettime(CLOCK_MONOTONIC, &ts);
+  ASSERT_EQ(0, result);
+  Time now = Time::FromTimespec(ts);
+#else
+  // The timeout argument to pthread_cond_timedwait() is in absolute time.
+  Time now = Time::NowFromSystemTime();
+#endif
+  Time end_time = now + rel_time;
+  ASSERT_GE(end_time, now);
+  ts = end_time.ToTimespec();
+  result = pthread_cond_timedwait(
+      &native_handle_, &mutex->native_handle(), &ts);
+#endif  // V8_OS_MACOSX
+  mutex->AssertUnheldAndMark();
+  if (result == ETIMEDOUT) {
+    return false;
+  }
+  ASSERT_EQ(0, result);
+  return true;
+}
+
+#elif V8_OS_WIN
+
+struct ConditionVariable::Event {
+  Event() : handle_(::CreateEventA(NULL, true, false, NULL)) {
+    ASSERT(handle_ != NULL);
+  }
+
+  ~Event() {
+    BOOL ok = ::CloseHandle(handle_);
+    ASSERT(ok);
+    USE(ok);
+  }
+
+  bool WaitFor(DWORD timeout_ms) {
+    DWORD result = ::WaitForSingleObject(handle_, timeout_ms);
+    if (result == WAIT_OBJECT_0) {
+      return true;
+    }
+    ASSERT(result == WAIT_TIMEOUT);
+    return false;
+  }
+
+  HANDLE handle_;
+  Event* next_;
+  HANDLE thread_;
+  volatile bool notified_;
+};
+
+
+ConditionVariable::NativeHandle::~NativeHandle() {
+  ASSERT(waitlist_ == NULL);
+
+  while (freelist_ != NULL) {
+    Event* event = freelist_;
+    freelist_ = event->next_;
+    delete event;
+  }
+}
+
+
+ConditionVariable::Event* ConditionVariable::NativeHandle::Pre() {
+  LockGuard<Mutex> lock_guard(&mutex_);
+
+  // Grab an event from the free list or create a new one.
+  Event* event = freelist_;
+  if (event != NULL) {
+    freelist_ = event->next_;
+  } else {
+    event = new Event;
+  }
+  event->thread_ = GetCurrentThread();
+  event->notified_ = false;
+
+#ifdef DEBUG
+  // The event must not be on the wait list.
+  for (Event* we = waitlist_; we != NULL; we = we->next_) {
+    ASSERT_NE(event, we);
+  }
+#endif
+
+  // Prepend the event to the wait list.
+  event->next_ = waitlist_;
+  waitlist_ = event;
+
+  return event;
+}
+
+
+void ConditionVariable::NativeHandle::Post(Event* event, bool result) {
+  LockGuard<Mutex> lock_guard(&mutex_);
+
+  // Remove the event from the wait list.
+  for (Event** wep = &waitlist_;; wep = &(*wep)->next_) {
+    ASSERT_NE(NULL, *wep);
+    if (*wep == event) {
+      *wep = event->next_;
+      break;
+    }
+  }
+
+#ifdef DEBUG
+  // The event must not be on the free list.
+  for (Event* fe = freelist_; fe != NULL; fe = fe->next_) {
+    ASSERT_NE(event, fe);
+  }
+#endif
+
+  // Reset the event.
+  BOOL ok = ::ResetEvent(event->handle_);
+  ASSERT(ok);
+  USE(ok);
+
+  // Insert the event into the free list.
+  event->next_ = freelist_;
+  freelist_ = event;
+
+  // Forward signals delivered after the timeout to the next waiting event.
+  if (!result && event->notified_ && waitlist_ != NULL) {
+    ok = ::SetEvent(waitlist_->handle_);
+    ASSERT(ok);
+    USE(ok);
+    waitlist_->notified_ = true;
+  }
+}
+
+
+ConditionVariable::ConditionVariable() {}
+
+
+ConditionVariable::~ConditionVariable() {}
+
+
+void ConditionVariable::NotifyOne() {
+  // Notify the thread with the highest priority in the waitlist
+  // that was not already signalled.
+  LockGuard<Mutex> lock_guard(native_handle_.mutex());
+  Event* highest_event = NULL;
+  int highest_priority = std::numeric_limits<int>::min();
+  for (Event* event = native_handle().waitlist();
+       event != NULL;
+       event = event->next_) {
+    if (event->notified_) {
+      continue;
+    }
+    int priority = GetThreadPriority(event->thread_);
+    ASSERT_NE(THREAD_PRIORITY_ERROR_RETURN, priority);
+    if (priority >= highest_priority) {
+      highest_priority = priority;
+      highest_event = event;
+    }
+  }
+  if (highest_event != NULL) {
+    ASSERT(!highest_event->notified_);
+    ::SetEvent(highest_event->handle_);
+    highest_event->notified_ = true;
+  }
+}
+
+
+void ConditionVariable::NotifyAll() {
+  // Notify all threads on the waitlist.
+  LockGuard<Mutex> lock_guard(native_handle_.mutex());
+  for (Event* event = native_handle().waitlist();
+       event != NULL;
+       event = event->next_) {
+    if (!event->notified_) {
+      ::SetEvent(event->handle_);
+      event->notified_ = true;
+    }
+  }
+}
+
+
+void ConditionVariable::Wait(Mutex* mutex) {
+  // Create and setup the wait event.
+  Event* event = native_handle_.Pre();
+
+  // Release the user mutex.
+  mutex->Unlock();
+
+  // Wait on the wait event.
+  while (!event->WaitFor(INFINITE))
+    ;
+
+  // Reaquire the user mutex.
+  mutex->Lock();
+
+  // Release the wait event (we must have been notified).
+  ASSERT(event->notified_);
+  native_handle_.Post(event, true);
+}
+
+
+bool ConditionVariable::WaitFor(Mutex* mutex, const TimeDelta& rel_time) {
+  // Create and setup the wait event.
+  Event* event = native_handle_.Pre();
+
+  // Release the user mutex.
+  mutex->Unlock();
+
+  // Wait on the wait event.
+  TimeTicks now = TimeTicks::Now();
+  TimeTicks end = now + rel_time;
+  bool result = false;
+  while (true) {
+    int64_t msec = (end - now).InMilliseconds();
+    if (msec >= static_cast<int64_t>(INFINITE)) {
+      result = event->WaitFor(INFINITE - 1);
+      if (result) {
+        break;
+      }
+      now = TimeTicks::Now();
+    } else {
+      result = event->WaitFor((msec < 0) ? 0 : static_cast<DWORD>(msec));
+      break;
+    }
+  }
+
+  // Reaquire the user mutex.
+  mutex->Lock();
+
+  // Release the wait event.
+  ASSERT(!result || event->notified_);
+  native_handle_.Post(event, result);
+
+  return result;
+}
+
+#endif  // V8_OS_POSIX
+
+} }  // namespace v8::internal
diff --git a/src/platform/condition-variable.h b/src/platform/condition-variable.h
new file mode 100644 (file)
index 0000000..43cc529
--- /dev/null
@@ -0,0 +1,140 @@
+// Copyright 2013 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.
+
+#ifndef V8_PLATFORM_CONDITION_VARIABLE_H_
+#define V8_PLATFORM_CONDITION_VARIABLE_H_
+
+#include "platform/mutex.h"
+
+namespace v8 {
+namespace internal {
+
+// Forward declarations.
+class ConditionVariableEvent;
+class TimeDelta;
+
+// -----------------------------------------------------------------------------
+// ConditionVariable
+//
+// This class is a synchronization primitive that can be used to block a thread,
+// or multiple threads at the same time, until:
+// - a notification is received from another thread,
+// - a timeout expires, or
+// - a spurious wakeup occurs
+// Any thread that intends to wait on a ConditionVariable has to acquire a lock
+// on a Mutex first. The |Wait()| and |WaitFor()| operations atomically release
+// the mutex and suspend the execution of the calling thread. When the condition
+// variable is notified, the thread is awakened, and the mutex is reacquired.
+
+class ConditionVariable V8_FINAL {
+ public:
+  ConditionVariable();
+  ~ConditionVariable();
+
+  // If any threads are waiting on this condition variable, calling
+  // |NotifyOne()| unblocks one of the waiting threads.
+  void NotifyOne();
+
+  // Unblocks all threads currently waiting for this condition variable.
+  void NotifyAll();
+
+  // |Wait()| causes the calling thread to block until the condition variable is
+  // notified or a spurious wakeup occurs. Atomically releases the mutex, blocks
+  // the current executing thread, and adds it to the list of threads waiting on
+  // this condition variable. The thread will be unblocked when |NotifyAll()| or
+  // |NotifyOne()| is executed. It may also be unblocked spuriously. When
+  // unblocked, regardless of the reason, the lock on the mutex is reacquired
+  // and |Wait()| exits.
+  void Wait(Mutex* mutex);
+
+  // Atomically releases the mutex, blocks the current executing thread, and
+  // adds it to the list of threads waiting on this condition variable. The
+  // thread will be unblocked when |NotifyAll()| or |NotifyOne()| is executed,
+  // or when the relative timeout |rel_time| expires. It may also be unblocked
+  // spuriously. When unblocked, regardless of the reason, the lock on the mutex
+  // is reacquired and |WaitFor()| exits. Returns true if the condition variable
+  // was notified prior to the timeout.
+  bool WaitFor(Mutex* mutex, const TimeDelta& rel_time) V8_WARN_UNUSED_RESULT;
+
+  // The implementation-defined native handle type.
+#if V8_OS_POSIX
+  typedef pthread_cond_t NativeHandle;
+#elif V8_OS_WIN
+  struct Event;
+  class NativeHandle V8_FINAL {
+   public:
+    NativeHandle() : waitlist_(NULL), freelist_(NULL) {}
+    ~NativeHandle();
+
+    Event* Pre() V8_WARN_UNUSED_RESULT;
+    void Post(Event* event, bool result);
+
+    Mutex* mutex() { return &mutex_; }
+    Event* waitlist() { return waitlist_; }
+
+   private:
+    Event* waitlist_;
+    Event* freelist_;
+    Mutex mutex_;
+
+    DISALLOW_COPY_AND_ASSIGN(NativeHandle);
+  };
+#endif
+
+  NativeHandle& native_handle() V8_WARN_UNUSED_RESULT {
+    return native_handle_;
+  }
+  const NativeHandle& native_handle() const V8_WARN_UNUSED_RESULT {
+    return native_handle_;
+  }
+
+ private:
+  NativeHandle native_handle_;
+
+  DISALLOW_COPY_AND_ASSIGN(ConditionVariable);
+};
+
+
+// POD ConditionVariable initialized lazily (i.e. the first time Pointer() is
+// called).
+// Usage:
+//   static LazyConditionVariable my_condvar =
+//       LAZY_CONDITION_VARIABLE_INITIALIZER;
+//
+//   void my_function() {
+//     LockGuard<Mutex> lock_guard(&my_mutex);
+//     my_condvar.Pointer()->Wait(&my_mutex);
+//   }
+typedef LazyStaticInstance<ConditionVariable,
+                           DefaultConstructTrait<ConditionVariable>,
+                           ThreadSafeInitOnceTrait>::type LazyConditionVariable;
+
+#define LAZY_CONDITION_VARIABLE_INITIALIZER LAZY_STATIC_INSTANCE_INITIALIZER
+
+} }  // namespace v8::internal
+
+#endif  // V8_PLATFORM_CONDITION_VARIABLE_H_
index c8d75c7..1a7c69a 100644 (file)
@@ -101,32 +101,32 @@ static V8_INLINE(bool TryLockNativeHandle(pthread_mutex_t* mutex)) {
 
 #elif V8_OS_WIN
 
-static V8_INLINE(void InitializeNativeHandle(CRITICAL_SECTION* cs)) {
+static V8_INLINE(void InitializeNativeHandle(PCRITICAL_SECTION cs)) {
   InitializeCriticalSection(cs);
 }
 
 
-static V8_INLINE(void InitializeRecursiveNativeHandle(CRITICAL_SECTION* cs)) {
+static V8_INLINE(void InitializeRecursiveNativeHandle(PCRITICAL_SECTION cs)) {
   InitializeCriticalSection(cs);
 }
 
 
-static V8_INLINE(void DestroyNativeHandle(CRITICAL_SECTION* cs)) {
+static V8_INLINE(void DestroyNativeHandle(PCRITICAL_SECTION cs)) {
   DeleteCriticalSection(cs);
 }
 
 
-static V8_INLINE(void LockNativeHandle(CRITICAL_SECTION* cs)) {
+static V8_INLINE(void LockNativeHandle(PCRITICAL_SECTION cs)) {
   EnterCriticalSection(cs);
 }
 
 
-static V8_INLINE(void UnlockNativeHandle(CRITICAL_SECTION* cs)) {
+static V8_INLINE(void UnlockNativeHandle(PCRITICAL_SECTION cs)) {
   LeaveCriticalSection(cs);
 }
 
 
-static V8_INLINE(bool TryLockNativeHandle(CRITICAL_SECTION* cs)) {
+static V8_INLINE(bool TryLockNativeHandle(PCRITICAL_SECTION cs)) {
   return TryEnterCriticalSection(cs);
 }
 
@@ -149,18 +149,12 @@ Mutex::~Mutex() {
 
 void Mutex::Lock() {
   LockNativeHandle(&native_handle_);
-#ifdef DEBUG
-  ASSERT_EQ(0, level_);
-  level_++;
-#endif
+  AssertUnheldAndMark();
 }
 
 
 void Mutex::Unlock() {
-#ifdef DEBUG
-  ASSERT_EQ(1, level_);
-  level_--;
-#endif
+  AssertHeldAndUnmark();
   UnlockNativeHandle(&native_handle_);
 }
 
@@ -169,10 +163,7 @@ bool Mutex::TryLock() {
   if (!TryLockNativeHandle(&native_handle_)) {
     return false;
   }
-#ifdef DEBUG
-  ASSERT_EQ(0, level_);
-  level_++;
-#endif
+  AssertUnheldAndMark();
   return true;
 }
 
index 1940542..c760c20 100644 (file)
@@ -94,6 +94,22 @@ class Mutex V8_FINAL {
   int level_;
 #endif
 
+  V8_INLINE(void AssertHeldAndUnmark()) {
+#ifdef DEBUG
+    ASSERT_EQ(1, level_);
+    level_--;
+#endif
+  }
+
+  V8_INLINE(void AssertUnheldAndMark()) {
+#ifdef DEBUG
+    ASSERT_EQ(0, level_);
+    level_++;
+#endif
+  }
+
+  friend class ConditionVariable;
+
   DISALLOW_COPY_AND_ASSIGN(Mutex);
 };
 
index 756be93..ea6dd2c 100644 (file)
@@ -126,7 +126,9 @@ int64_t TimeDelta::InNanoseconds() const {
 #if V8_OS_MACOSX
 
 TimeDelta TimeDelta::FromMachTimespec(struct mach_timespec ts) {
-  ASSERT(ts.tv_nsec >= 0);
+  ASSERT_GE(ts.tv_nsec, 0);
+  ASSERT_LT(ts.tv_nsec,
+            static_cast<long>(Time::kNanosecondsPerSecond));  // NOLINT
   return TimeDelta(ts.tv_sec * Time::kMicrosecondsPerSecond +
                    ts.tv_nsec / Time::kNanosecondsPerMicrosecond);
 }
@@ -144,6 +146,28 @@ struct mach_timespec TimeDelta::ToMachTimespec() const {
 #endif  // V8_OS_MACOSX
 
 
+#if V8_OS_POSIX
+
+TimeDelta TimeDelta::FromTimespec(struct timespec ts) {
+  ASSERT_GE(ts.tv_nsec, 0);
+  ASSERT_LT(ts.tv_nsec,
+            static_cast<long>(Time::kNanosecondsPerSecond));  // NOLINT
+  return TimeDelta(ts.tv_sec * Time::kMicrosecondsPerSecond +
+                   ts.tv_nsec / Time::kNanosecondsPerMicrosecond);
+}
+
+
+struct timespec TimeDelta::ToTimespec() const {
+  struct timespec ts;
+  ts.tv_sec = delta_ / Time::kMicrosecondsPerSecond;
+  ts.tv_nsec = (delta_ % Time::kMicrosecondsPerSecond) *
+      Time::kNanosecondsPerMicrosecond;
+  return ts;
+}
+
+#endif  // V8_OS_POSIX
+
+
 #if V8_OS_WIN
 
 // We implement time using the high-resolution timers so that we can get
index a0d5425..3fed628 100644 (file)
@@ -88,6 +88,10 @@ class TimeDelta V8_FINAL BASE_EMBEDDED {
   static TimeDelta FromMachTimespec(struct mach_timespec ts);
   struct mach_timespec ToMachTimespec() const;
 
+  // Converts to/from POSIX time specs.
+  static TimeDelta FromTimespec(struct timespec ts);
+  struct timespec ToTimespec() const;
+
   TimeDelta& operator=(const TimeDelta& other) {
     delta_ = other.delta_;
     return *this;
index 3509794..acf2dc1 100644 (file)
@@ -332,10 +332,10 @@ class DisassemblerX64 {
 
  private:
   enum OperandSize {
-    BYTE_SIZE = 0,
-    WORD_SIZE = 1,
-    DOUBLEWORD_SIZE = 2,
-    QUADWORD_SIZE = 3
+    OPERAND_BYTE_SIZE = 0,
+    OPERAND_WORD_SIZE = 1,
+    OPERAND_DOUBLEWORD_SIZE = 2,
+    OPERAND_QUADWORD_SIZE = 3
   };
 
   const NameConverter& converter_;
@@ -369,10 +369,10 @@ class DisassemblerX64 {
   bool rex_w() { return (rex_ & 0x08) != 0; }
 
   OperandSize operand_size() {
-    if (byte_size_operand_) return BYTE_SIZE;
-    if (rex_w()) return QUADWORD_SIZE;
-    if (operand_size_ != 0) return WORD_SIZE;
-    return DOUBLEWORD_SIZE;
+    if (byte_size_operand_) return OPERAND_BYTE_SIZE;
+    if (rex_w()) return OPERAND_QUADWORD_SIZE;
+    if (operand_size_ != 0) return OPERAND_WORD_SIZE;
+    return OPERAND_DOUBLEWORD_SIZE;
   }
 
   char operand_size_code() {
@@ -562,19 +562,19 @@ int DisassemblerX64::PrintImmediate(byte* data, OperandSize size) {
   int64_t value;
   int count;
   switch (size) {
-    case BYTE_SIZE:
+    case OPERAND_BYTE_SIZE:
       value = *data;
       count = 1;
       break;
-    case WORD_SIZE:
+    case OPERAND_WORD_SIZE:
       value = *reinterpret_cast<int16_t*>(data);
       count = 2;
       break;
-    case DOUBLEWORD_SIZE:
+    case OPERAND_DOUBLEWORD_SIZE:
       value = *reinterpret_cast<uint32_t*>(data);
       count = 4;
       break;
-    case QUADWORD_SIZE:
+    case OPERAND_QUADWORD_SIZE:
       value = *reinterpret_cast<int32_t*>(data);
       count = 4;
       break;
@@ -682,7 +682,8 @@ int DisassemblerX64::PrintImmediateOp(byte* data) {
   AppendToBuffer("%s%c ", mnem, operand_size_code());
   int count = PrintRightOperand(data + 1);
   AppendToBuffer(",0x");
-  OperandSize immediate_size = byte_size_immediate ? BYTE_SIZE : operand_size();
+  OperandSize immediate_size =
+      byte_size_immediate ? OPERAND_BYTE_SIZE : operand_size();
   count += PrintImmediate(data + 1 + count, immediate_size);
   return 1 + count;
 }
@@ -1415,15 +1416,15 @@ int DisassemblerX64::InstructionDecode(v8::internal::Vector<char> out_buffer,
     case MOVE_REG_INSTR: {
       byte* addr = NULL;
       switch (operand_size()) {
-        case WORD_SIZE:
+        case OPERAND_WORD_SIZE:
           addr = reinterpret_cast<byte*>(*reinterpret_cast<int16_t*>(data + 1));
           data += 3;
           break;
-        case DOUBLEWORD_SIZE:
+        case OPERAND_DOUBLEWORD_SIZE:
           addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data + 1));
           data += 5;
           break;
-        case QUADWORD_SIZE:
+        case OPERAND_QUADWORD_SIZE:
           addr = reinterpret_cast<byte*>(*reinterpret_cast<int64_t*>(data + 1));
           data += 9;
           break;
@@ -1628,11 +1629,11 @@ int DisassemblerX64::InstructionDecode(v8::internal::Vector<char> out_buffer,
           AppendToBuffer("mov%c %s, ",
                          operand_size_code(),
                          NameOfCPURegister(reg));
-          data += PrintImmediate(data, DOUBLEWORD_SIZE);
+          data += PrintImmediate(data, OPERAND_DOUBLEWORD_SIZE);
         } else {
           AppendToBuffer("movb %s, ",
                          NameOfByteCPURegister(reg));
-          data += PrintImmediate(data, BYTE_SIZE);
+          data += PrintImmediate(data, OPERAND_BYTE_SIZE);
         }
         break;
       }
@@ -1661,7 +1662,7 @@ int DisassemblerX64::InstructionDecode(v8::internal::Vector<char> out_buffer,
       case 0xA1:  // Fall through.
       case 0xA3:
         switch (operand_size()) {
-          case DOUBLEWORD_SIZE: {
+          case OPERAND_DOUBLEWORD_SIZE: {
             const char* memory_location = NameOfAddress(
                 reinterpret_cast<byte*>(
                     *reinterpret_cast<int32_t*>(data + 1)));
@@ -1673,7 +1674,7 @@ int DisassemblerX64::InstructionDecode(v8::internal::Vector<char> out_buffer,
             data += 5;
             break;
           }
-          case QUADWORD_SIZE: {
+          case OPERAND_QUADWORD_SIZE: {
             // New x64 instruction mov rax,(imm_64).
             const char* memory_location = NameOfAddress(
                 *reinterpret_cast<byte**>(data + 1));
@@ -1699,15 +1700,15 @@ int DisassemblerX64::InstructionDecode(v8::internal::Vector<char> out_buffer,
       case 0xA9: {
         int64_t value = 0;
         switch (operand_size()) {
-          case WORD_SIZE:
+          case OPERAND_WORD_SIZE:
             value = *reinterpret_cast<uint16_t*>(data + 1);
             data += 3;
             break;
-          case DOUBLEWORD_SIZE:
+          case OPERAND_DOUBLEWORD_SIZE:
             value = *reinterpret_cast<uint32_t*>(data + 1);
             data += 5;
             break;
-          case QUADWORD_SIZE:
+          case OPERAND_QUADWORD_SIZE:
             value = *reinterpret_cast<int32_t*>(data + 1);
             data += 5;
             break;
index d93ae2f..ce01e3d 100644 (file)
@@ -55,6 +55,7 @@
         'test-bignum-dtoa.cc',
         'test-circular-queue.cc',
         'test-compiler.cc',
+        'test-condition-variable.cc',
         'test-conversions.cc',
         'test-cpu.cc',
         'test-cpu-profiler.cc',
diff --git a/test/cctest/test-condition-variable.cc b/test/cctest/test-condition-variable.cc
new file mode 100644 (file)
index 0000000..774983e
--- /dev/null
@@ -0,0 +1,162 @@
+// Copyright 2013 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.
+
+#include "v8.h"
+
+#include "cctest.h"
+#include "platform/condition-variable.h"
+#include "platform/time.h"
+
+using namespace ::v8::internal;
+
+
+TEST(WaitForAfterNofityOnSameThread) {
+  for (int n = 0; n < 10; ++n) {
+    Mutex mutex;
+    ConditionVariable cv;
+
+    LockGuard<Mutex> lock_guard(&mutex);
+
+    cv.NotifyOne();
+    CHECK_EQ(false, cv.WaitFor(&mutex, TimeDelta::FromMicroseconds(n)));
+
+    cv.NotifyAll();
+    CHECK_EQ(false, cv.WaitFor(&mutex, TimeDelta::FromMicroseconds(n)));
+  }
+}
+
+
+class ThreadWithMutexAndConditionVariable V8_FINAL : public Thread {
+ public:
+  ThreadWithMutexAndConditionVariable()
+      : Thread("ThreadWithMutexAndConditionVariable"),
+        running_(false), finished_(false) {}
+  virtual ~ThreadWithMutexAndConditionVariable() {}
+
+  virtual void Run() V8_OVERRIDE {
+    LockGuard<Mutex> lock_guard(&mutex_);
+    running_ = true;
+    cv_.NotifyOne();
+    cv_.Wait(&mutex_);
+    running_ = false;
+    finished_ = true;
+    cv_.NotifyOne();
+  }
+
+  volatile bool running_;
+  volatile bool finished_;
+  ConditionVariable cv_;
+  Mutex mutex_;
+};
+
+
+TEST(MultipleThreadsWithSeparateConditionVariables) {
+  static const int kThreadCount = 16;
+  static const TimeDelta kMaxThreadStartTime =
+      TimeDelta::FromMilliseconds(250) * kThreadCount;
+  ThreadWithMutexAndConditionVariable threads[kThreadCount];
+
+  for (int n = 0; n < kThreadCount; ++n) {
+    LockGuard<Mutex> lock_guard(&threads[n].mutex_);
+    CHECK(!threads[n].running_);
+    CHECK(!threads[n].finished_);
+    threads[n].Start();
+    // Wait for nth thread to start.
+    CHECK(threads[n].cv_.WaitFor(&threads[n].mutex_, kMaxThreadStartTime));
+  }
+
+  for (int n = kThreadCount - 1; n >= 0; --n) {
+    LockGuard<Mutex> lock_guard(&threads[n].mutex_);
+    CHECK(threads[n].running_);
+    CHECK(!threads[n].finished_);
+  }
+
+  for (int n = 0; n < kThreadCount; ++n) {
+    threads[n].cv_.NotifyOne();
+  }
+
+  for (int n = kThreadCount - 1; n >= 0; --n) {
+    // Wait for nth thread to quit.
+    threads[n].Join();
+    LockGuard<Mutex> lock_guard(&threads[n].mutex_);
+    CHECK(!threads[n].running_);
+    CHECK(threads[n].finished_);
+  }
+}
+
+
+static int loop_counter = 0;
+static const int kLoopCounterLimit = 100;
+
+class LoopIncrementThread V8_FINAL : public Thread {
+ public:
+  LoopIncrementThread(const char* name,
+                      int rem,
+                      ConditionVariable* cv,
+                      Mutex* mutex)
+      : Thread(name), rem_(rem), cv_(cv), mutex_(mutex) {}
+  virtual ~LoopIncrementThread() {}
+
+  virtual void Run() V8_OVERRIDE {
+    int last_count = -1;
+    while (true) {
+      LockGuard<Mutex> lock_guard(mutex_);
+      int count = loop_counter;
+      while (count % 2 != rem_ && count < kLoopCounterLimit) {
+        cv_->Wait(mutex_);
+        count = loop_counter;
+      }
+      if (count >= kLoopCounterLimit) break;
+      CHECK_EQ(loop_counter, count);
+      if (last_count != -1) {
+        CHECK_EQ(last_count + 1, count);
+      }
+      count++;
+      loop_counter = count;
+      last_count = count;
+      cv_->NotifyOne();
+    }
+  }
+
+ private:
+  const int rem_;
+  ConditionVariable* cv_;
+  Mutex* mutex_;
+};
+
+
+TEST(LoopIncrement) {
+  Mutex mutex;
+  ConditionVariable cv;
+  LoopIncrementThread t0("t0", 0, &cv, &mutex);
+  LoopIncrementThread t1("t1", 1, &cv, &mutex);
+  t0.Start();
+  t1.Start();
+  t0.Join();
+  t1.Join();
+  CHECK_EQ(kLoopCounterLimit, loop_counter);
+}
index 7347aac..34ae43c 100644 (file)
 using namespace ::v8::internal;
 
 
-static void yield() {
-  usleep(1);
-}
-
-static const int kLockCounterLimit = 50;
-static int busy_lock_counter = 0;
-
-
-static void LoopIncrement(Mutex* mutex, int rem) {
-  while (true) {
-    int count = 0;
-    int last_count = -1;
-    do {
-      LockGuard<Mutex> lock_guard(mutex);
-      count = busy_lock_counter;
-      yield();
-    } while (count % 2 == rem && count < kLockCounterLimit);
-    if (count >= kLockCounterLimit) break;
-    LockGuard<Mutex> lock_guard(mutex);
-    CHECK_EQ(count, busy_lock_counter);
-    CHECK(last_count == -1 || count == last_count + 1);
-    busy_lock_counter++;
-    last_count = count;
-    yield();
-  }
-}
-
-
-static void* RunTestBusyLock(void* arg) {
-  LoopIncrement(static_cast<Mutex*>(arg), 0);
-  return 0;
-}
-
-
-// Runs two threads that repeatedly acquire the lock and conditionally
-// increment a variable.
-TEST(BusyLock) {
-  pthread_t other;
-  Mutex mutex;
-  int thread_created = pthread_create(&other,
-                                      NULL,
-                                      &RunTestBusyLock,
-                                      &mutex);
-  CHECK_EQ(0, thread_created);
-  LoopIncrement(&mutex, 1);
-  pthread_join(other, NULL);
-}
-
-
 TEST(VirtualMemory) {
   OS::SetUp();
   VirtualMemory* vm = new VirtualMemory(1 * MB);
index 469eaca..8548efe 100644 (file)
         '../../src/platform/time.h',
         '../../src/platform-posix.h',
         '../../src/platform.h',
+        '../../src/platform/condition-variable.cc',
+        '../../src/platform/condition-variable.h',
         '../../src/platform/mutex.cc',
         '../../src/platform/mutex.h',
         '../../src/platform/semaphore.cc',