"src/full-codegen.h",
"src/func-name-inferrer.cc",
"src/func-name-inferrer.h",
+ "src/futex-emulation.cc",
+ "src/futex-emulation.h",
"src/gdb-jit.cc",
"src/gdb-jit.h",
"src/global-handles.cc",
"src/runtime/runtime-debug.cc",
"src/runtime/runtime-forin.cc",
"src/runtime/runtime-function.cc",
+ "src/runtime/runtime-futex.cc",
"src/runtime/runtime-generator.cc",
"src/runtime/runtime-i18n.cc",
"src/runtime/runtime-internal.cc",
--- /dev/null
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/futex-emulation.h"
+
+#include <limits>
+
+#include "src/base/macros.h"
+#include "src/base/platform/time.h"
+#include "src/conversions.h"
+#include "src/handles-inl.h"
+#include "src/isolate.h"
+#include "src/list-inl.h"
+
+namespace v8 {
+namespace internal {
+
+base::LazyMutex FutexEmulation::mutex_ = LAZY_MUTEX_INITIALIZER;
+base::LazyInstance<FutexWaitList>::type FutexEmulation::wait_list_ =
+ LAZY_INSTANCE_INITIALIZER;
+
+
+FutexWaitList::FutexWaitList() : head_(nullptr), tail_(nullptr) {}
+
+
+void FutexWaitList::AddNode(FutexWaitListNode* node) {
+ DCHECK(node->prev_ == nullptr && node->next_ == nullptr);
+ if (tail_) {
+ tail_->next_ = node;
+ } else {
+ head_ = node;
+ }
+
+ node->prev_ = tail_;
+ node->next_ = nullptr;
+ tail_ = node;
+}
+
+
+void FutexWaitList::RemoveNode(FutexWaitListNode* node) {
+ if (node->prev_) {
+ node->prev_->next_ = node->next_;
+ } else {
+ head_ = node->next_;
+ }
+
+ if (node->next_) {
+ node->next_->prev_ = node->prev_;
+ } else {
+ tail_ = node->prev_;
+ }
+
+ node->prev_ = node->next_ = nullptr;
+}
+
+
+Object* FutexEmulation::Wait(Isolate* isolate,
+ Handle<JSArrayBuffer> array_buffer, size_t addr,
+ int32_t value, double rel_timeout_ms) {
+ // We never want to wait longer than this amount of time; this way we can
+ // interrupt this thread even if this is an "infinitely blocking" wait.
+ // TODO(binji): come up with a better way of interrupting only when
+ // necessary, rather than busy-waiting.
+ const base::TimeDelta kMaxWaitTime = base::TimeDelta::FromMilliseconds(50);
+
+ DCHECK(addr < NumberToSize(isolate, array_buffer->byte_length()));
+
+ void* backing_store = array_buffer->backing_store();
+ int32_t* p =
+ reinterpret_cast<int32_t*>(static_cast<int8_t*>(backing_store) + addr);
+
+ base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer());
+
+ if (*p != value) {
+ return Smi::FromInt(Result::kNotEqual);
+ }
+
+ FutexWaitListNode* node = isolate->futex_wait_list_node();
+
+ node->backing_store_ = backing_store;
+ node->wait_addr_ = addr;
+ node->waiting_ = true;
+
+ bool use_timeout = rel_timeout_ms != V8_INFINITY;
+
+ base::TimeDelta rel_timeout;
+ if (use_timeout) {
+ // Convert to nanoseconds.
+ double rel_timeout_ns = rel_timeout_ms *
+ base::Time::kNanosecondsPerMicrosecond *
+ base::Time::kMicrosecondsPerMillisecond;
+ if (rel_timeout_ns >
+ static_cast<double>(std::numeric_limits<int64_t>::max())) {
+ // 2**63 nanoseconds is 292 years. Let's just treat anything greater as
+ // infinite.
+ use_timeout = false;
+ } else {
+ rel_timeout = base::TimeDelta::FromNanoseconds(
+ static_cast<int64_t>(rel_timeout_ns));
+ }
+ }
+
+ base::TimeDelta rel_time_left = rel_timeout;
+
+ wait_list_.Pointer()->AddNode(node);
+
+ Object* result;
+
+ while (true) {
+ base::TimeDelta time_to_wait = (use_timeout && rel_time_left < kMaxWaitTime)
+ ? rel_time_left
+ : kMaxWaitTime;
+
+ base::Time start_time = base::Time::NowFromSystemTime();
+ bool wait_for_result = node->cond_.WaitFor(mutex_.Pointer(), time_to_wait);
+ USE(wait_for_result);
+
+ if (!node->waiting_) {
+ result = Smi::FromInt(Result::kOk);
+ break;
+ }
+
+ // Spurious wakeup or timeout.
+ base::Time end_time = base::Time::NowFromSystemTime();
+ base::TimeDelta waited_for = end_time - start_time;
+ rel_time_left -= waited_for;
+
+ if (use_timeout && rel_time_left < base::TimeDelta::FromMicroseconds(0)) {
+ result = Smi::FromInt(Result::kTimedOut);
+ break;
+ }
+
+ // Potentially handle interrupts before continuing to wait.
+ Object* interrupt_object = isolate->stack_guard()->HandleInterrupts();
+ if (interrupt_object->IsException()) {
+ result = interrupt_object;
+ break;
+ }
+ }
+
+ wait_list_.Pointer()->RemoveNode(node);
+
+ return result;
+}
+
+
+Object* FutexEmulation::Wake(Isolate* isolate,
+ Handle<JSArrayBuffer> array_buffer, size_t addr,
+ int num_waiters_to_wake) {
+ DCHECK(addr < NumberToSize(isolate, array_buffer->byte_length()));
+
+ int waiters_woken = 0;
+ void* backing_store = array_buffer->backing_store();
+
+ base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer());
+ FutexWaitListNode* node = wait_list_.Pointer()->head_;
+ while (node && num_waiters_to_wake > 0) {
+ if (backing_store == node->backing_store_ && addr == node->wait_addr_) {
+ node->waiting_ = false;
+ node->cond_.NotifyOne();
+ --num_waiters_to_wake;
+ waiters_woken++;
+ }
+
+ node = node->next_;
+ }
+
+ return Smi::FromInt(waiters_woken);
+}
+
+
+Object* FutexEmulation::WakeOrRequeue(Isolate* isolate,
+ Handle<JSArrayBuffer> array_buffer,
+ size_t addr, int num_waiters_to_wake,
+ int32_t value, size_t addr2) {
+ DCHECK(addr < NumberToSize(isolate, array_buffer->byte_length()));
+ DCHECK(addr2 < NumberToSize(isolate, array_buffer->byte_length()));
+
+ void* backing_store = array_buffer->backing_store();
+ int32_t* p =
+ reinterpret_cast<int32_t*>(static_cast<int8_t*>(backing_store) + addr);
+
+ base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer());
+ if (*p != value) {
+ return Smi::FromInt(Result::kNotEqual);
+ }
+
+ // Wake |num_waiters_to_wake|
+ int waiters_woken = 0;
+ FutexWaitListNode* node = wait_list_.Pointer()->head_;
+ while (node) {
+ if (backing_store == node->backing_store_ && addr == node->wait_addr_) {
+ if (num_waiters_to_wake > 0) {
+ node->waiting_ = false;
+ node->cond_.NotifyOne();
+ --num_waiters_to_wake;
+ waiters_woken++;
+ } else {
+ node->wait_addr_ = addr2;
+ }
+ }
+
+ node = node->next_;
+ }
+
+ return Smi::FromInt(waiters_woken);
+}
+
+
+Object* FutexEmulation::NumWaitersForTesting(Isolate* isolate,
+ Handle<JSArrayBuffer> array_buffer,
+ size_t addr) {
+ DCHECK(addr < NumberToSize(isolate, array_buffer->byte_length()));
+ void* backing_store = array_buffer->backing_store();
+
+ base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer());
+
+ int waiters = 0;
+ FutexWaitListNode* node = wait_list_.Pointer()->head_;
+ while (node) {
+ if (backing_store == node->backing_store_ && addr == node->wait_addr_) {
+ waiters++;
+ }
+
+ node = node->next_;
+ }
+
+ return Smi::FromInt(waiters);
+}
+
+} // namespace internal
+} // namespace v8
--- /dev/null
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_FUTEX_EMULATION_H_
+#define V8_FUTEX_EMULATION_H_
+
+#include <stdint.h>
+
+#include "src/allocation.h"
+#include "src/base/lazy-instance.h"
+#include "src/base/macros.h"
+#include "src/base/platform/condition-variable.h"
+#include "src/base/platform/mutex.h"
+#include "src/handles.h"
+
+// Support for emulating futexes, a low-level synchronization primitive. They
+// are natively supported by Linux, but must be emulated for other platforms.
+// This library emulates them on all platforms using mutexes and condition
+// variables for consistency.
+//
+// This is used by the Futex API defined in the SharedArrayBuffer draft spec,
+// found here: https://github.com/lars-t-hansen/ecmascript_sharedmem
+
+namespace v8 {
+
+namespace base {
+class TimeDelta;
+} // base
+
+namespace internal {
+
+class Isolate;
+
+class FutexWaitListNode {
+ public:
+ FutexWaitListNode()
+ : prev_(nullptr),
+ next_(nullptr),
+ backing_store_(nullptr),
+ wait_addr_(0),
+ waiting_(false) {}
+
+ private:
+ friend class FutexEmulation;
+ friend class FutexWaitList;
+
+ base::ConditionVariable cond_;
+ FutexWaitListNode* prev_;
+ FutexWaitListNode* next_;
+ void* backing_store_;
+ size_t wait_addr_;
+ bool waiting_;
+
+ DISALLOW_COPY_AND_ASSIGN(FutexWaitListNode);
+};
+
+
+class FutexWaitList {
+ public:
+ FutexWaitList();
+
+ void AddNode(FutexWaitListNode* node);
+ void RemoveNode(FutexWaitListNode* node);
+
+ private:
+ friend class FutexEmulation;
+
+ FutexWaitListNode* head_;
+ FutexWaitListNode* tail_;
+
+ DISALLOW_COPY_AND_ASSIGN(FutexWaitList);
+};
+
+
+class FutexEmulation : public AllStatic {
+ public:
+ // These must match the values in src/harmony-atomics.js
+ enum Result {
+ kOk = 0,
+ kNotEqual = -1,
+ kTimedOut = -2,
+ };
+
+ // Check that array_buffer[addr] == value, and return kNotEqual if not. If
+ // they are equal, block execution on |isolate|'s thread until woken via
+ // |Wake|, or when the time given in |rel_timeout_ms| elapses. Note that
+ // |rel_timeout_ms| can be Infinity.
+ // If woken, return kOk, otherwise return kTimedOut. The initial check and
+ // the decision to wait happen atomically.
+ static Object* Wait(Isolate* isolate, Handle<JSArrayBuffer> array_buffer,
+ size_t addr, int32_t value, double rel_timeout_ms);
+
+ // Wake |num_waiters_to_wake| threads that are waiting on the given |addr|.
+ // The rest of the waiters will continue to wait. The return value is the
+ // number of woken waiters.
+ static Object* Wake(Isolate* isolate, Handle<JSArrayBuffer> array_buffer,
+ size_t addr, int num_waiters_to_wake);
+
+ // Check that array_buffer[addr] == value, and return kNotEqual if not. If
+ // they are equal, wake |num_waiters_to_wake| threads that are waiting on the
+ // given |addr|. The rest of the waiters will continue to wait, but will now
+ // be waiting on |addr2| instead of |addr|. The return value is the number of
+ // woken waiters or kNotEqual as described above.
+ static Object* WakeOrRequeue(Isolate* isolate,
+ Handle<JSArrayBuffer> array_buffer, size_t addr,
+ int num_waiters_to_wake, int32_t value,
+ size_t addr2);
+
+ // Return the number of threads waiting on |addr|. Should only be used for
+ // testing.
+ static Object* NumWaitersForTesting(Isolate* isolate,
+ Handle<JSArrayBuffer> array_buffer,
+ size_t addr);
+
+ private:
+ static base::LazyMutex mutex_;
+ static base::LazyInstance<FutexWaitList>::type wait_list_;
+};
+}
+} // namespace v8::internal
+
+#endif // V8_FUTEX_EMULATION_H_
var GlobalObject = global.Object;
+var MathMax;
+
+utils.Import(function(from) {
+ MathMax = from.MathMax;
+});
+
// -------------------------------------------------------------------
function CheckSharedTypedArray(sta) {
- if (!%_IsSharedTypedArray(sta)) {
+ if (!%IsSharedTypedArray(sta)) {
throw MakeTypeError(kNotSharedTypedArray, sta);
}
}
function CheckSharedIntegerTypedArray(ia) {
- if (!%_IsSharedIntegerTypedArray(ia)) {
+ if (!%IsSharedIntegerTypedArray(ia)) {
throw MakeTypeError(kNotIntegerSharedTypedArray, ia);
}
}
+function CheckSharedInteger32TypedArray(ia) {
+ CheckSharedIntegerTypedArray(ia);
+ if (%_ClassOf(ia) !== 'Int32Array') {
+ throw MakeTypeError(kNotInt32SharedTypedArray, ia);
+ }
+}
+
//-------------------------------------------------------------------
function AtomicsCompareExchangeJS(sta, index, oldValue, newValue) {
return %_AtomicsIsLockFree(size);
}
+// Futexes
+
+function AtomicsFutexWaitJS(ia, index, value, timeout) {
+ CheckSharedInteger32TypedArray(ia);
+ index = $toInteger(index);
+ if (index < 0 || index >= %_TypedArrayGetLength(ia)) {
+ return UNDEFINED;
+ }
+ if (IS_UNDEFINED(timeout)) {
+ timeout = INFINITY;
+ } else {
+ timeout = $toNumber(timeout);
+ if (NUMBER_IS_NAN(timeout)) {
+ timeout = INFINITY;
+ } else {
+ timeout = MathMax(0, timeout);
+ }
+ }
+ return %AtomicsFutexWait(ia, index, value, timeout);
+}
+
+function AtomicsFutexWakeJS(ia, index, count) {
+ CheckSharedInteger32TypedArray(ia);
+ index = $toInteger(index);
+ if (index < 0 || index >= %_TypedArrayGetLength(ia)) {
+ return UNDEFINED;
+ }
+ count = MathMax(0, $toInteger(count));
+ return %AtomicsFutexWake(ia, index, count);
+}
+
+function AtomicsFutexWakeOrRequeueJS(ia, index1, count, value, index2) {
+ CheckSharedInteger32TypedArray(ia);
+ index1 = $toInteger(index1);
+ count = MathMax(0, $toInteger(count));
+ value = $toInt32(value);
+ index2 = $toInteger(index2);
+ if (index1 < 0 || index1 >= %_TypedArrayGetLength(ia) ||
+ index2 < 0 || index2 >= %_TypedArrayGetLength(ia)) {
+ return UNDEFINED;
+ }
+ return %AtomicsFutexWakeOrRequeue(ia, index1, count, value, index2);
+}
+
// -------------------------------------------------------------------
function AtomicsConstructor() {}
%AddNamedProperty(Atomics, symbolToStringTag, "Atomics", READ_ONLY | DONT_ENUM);
+// These must match the values in src/futex-emulation.h
+utils.InstallConstants(Atomics, [
+ "OK", 0,
+ "NOTEQUAL", -1,
+ "TIMEDOUT", -2,
+]);
+
utils.InstallFunctions(Atomics, DONT_ENUM, [
"compareExchange", AtomicsCompareExchangeJS,
"load", AtomicsLoadJS,
"xor", AtomicsXorJS,
"exchange", AtomicsExchangeJS,
"isLockFree", AtomicsIsLockFreeJS,
+ "futexWait", AtomicsFutexWaitJS,
+ "futexWake", AtomicsFutexWakeJS,
+ "futexWakeOrRequeue", AtomicsFutexWakeOrRequeueJS,
]);
})
#include "src/date.h"
#include "src/execution.h"
#include "src/frames.h"
+#include "src/futex-emulation.h"
#include "src/global-handles.h"
#include "src/handles.h"
#include "src/hashmap.h"
return array_buffer_allocator_;
}
+ FutexWaitListNode* futex_wait_list_node() { return &futex_wait_list_node_; }
+
protected:
explicit Isolate(bool enable_serializer);
v8::ArrayBuffer::Allocator* array_buffer_allocator_;
+ FutexWaitListNode futex_wait_list_node_;
+
friend class ExecutionAccess;
friend class HandleScopeImplementer;
friend class OptimizingCompileDispatcher;
T(NotTypedArray, "this is not a typed array.") \
T(NotSharedTypedArray, "% is not a shared typed array.") \
T(NotIntegerSharedTypedArray, "% is not an integer shared typed array.") \
+ T(NotInt32SharedTypedArray, "% is not an int32 shared typed array.") \
T(ObjectGetterExpectingFunction, \
"Object.prototype.__defineGetter__: Expecting function") \
T(ObjectGetterCallable, "Getter must be a function: %") \
--- /dev/null
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/futex-emulation.h"
+
+#include "src/v8.h"
+
+#include "src/arguments.h"
+#include "src/base/platform/time.h"
+#include "src/globals.h"
+#include "src/runtime/runtime-utils.h"
+
+// Implement Futex API for SharedArrayBuffers as defined in the
+// SharedArrayBuffer draft spec, found here:
+// https://github.com/lars-t-hansen/ecmascript_sharedmem
+
+namespace v8 {
+namespace internal {
+
+RUNTIME_FUNCTION(Runtime_AtomicsFutexWait) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 4);
+ CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, sta, 0);
+ CONVERT_SIZE_ARG_CHECKED(index, 1);
+ CONVERT_INT32_ARG_CHECKED(value, 2);
+ CONVERT_DOUBLE_ARG_CHECKED(timeout, 3);
+ RUNTIME_ASSERT(sta->GetBuffer()->is_shared());
+ RUNTIME_ASSERT(index < NumberToSize(isolate, sta->length()));
+ RUNTIME_ASSERT(sta->type() == kExternalInt32Array);
+ RUNTIME_ASSERT(timeout == V8_INFINITY || !std::isnan(timeout));
+
+ Handle<JSArrayBuffer> array_buffer = sta->GetBuffer();
+ size_t addr = index << 2;
+
+ return FutexEmulation::Wait(isolate, array_buffer, addr, value, timeout);
+}
+
+
+RUNTIME_FUNCTION(Runtime_AtomicsFutexWake) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, sta, 0);
+ CONVERT_SIZE_ARG_CHECKED(index, 1);
+ CONVERT_INT32_ARG_CHECKED(count, 2);
+ RUNTIME_ASSERT(sta->GetBuffer()->is_shared());
+ RUNTIME_ASSERT(index < NumberToSize(isolate, sta->length()));
+ RUNTIME_ASSERT(sta->type() == kExternalInt32Array);
+
+ Handle<JSArrayBuffer> array_buffer = sta->GetBuffer();
+ size_t addr = index << 2;
+
+ return FutexEmulation::Wake(isolate, array_buffer, addr, count);
+}
+
+
+RUNTIME_FUNCTION(Runtime_AtomicsFutexWakeOrRequeue) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 5);
+ CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, sta, 0);
+ CONVERT_SIZE_ARG_CHECKED(index1, 1);
+ CONVERT_INT32_ARG_CHECKED(count, 2);
+ CONVERT_INT32_ARG_CHECKED(value, 3);
+ CONVERT_SIZE_ARG_CHECKED(index2, 4);
+ RUNTIME_ASSERT(sta->GetBuffer()->is_shared());
+ RUNTIME_ASSERT(index1 < NumberToSize(isolate, sta->length()));
+ RUNTIME_ASSERT(index2 < NumberToSize(isolate, sta->length()));
+ RUNTIME_ASSERT(sta->type() == kExternalInt32Array);
+
+ Handle<JSArrayBuffer> array_buffer = sta->GetBuffer();
+ size_t addr1 = index1 << 2;
+ size_t addr2 = index2 << 2;
+
+ return FutexEmulation::WakeOrRequeue(isolate, array_buffer, addr1, count,
+ value, addr2);
+}
+
+
+RUNTIME_FUNCTION(Runtime_AtomicsFutexNumWaitersForTesting) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, sta, 0);
+ CONVERT_SIZE_ARG_CHECKED(index, 1);
+ RUNTIME_ASSERT(sta->GetBuffer()->is_shared());
+ RUNTIME_ASSERT(index < NumberToSize(isolate, sta->length()));
+ RUNTIME_ASSERT(sta->type() == kExternalInt32Array);
+
+ Handle<JSArrayBuffer> array_buffer = sta->GetBuffer();
+ size_t addr = index << 2;
+
+ return FutexEmulation::NumWaitersForTesting(isolate, array_buffer, addr);
+}
+}
+} // namespace v8::internal
F(AtomicsIsLockFree, 1, 1)
+#define FOR_EACH_INTRINSIC_FUTEX(F) \
+ F(AtomicsFutexWait, 4, 1) \
+ F(AtomicsFutexWake, 3, 1) \
+ F(AtomicsFutexWakeOrRequeue, 5, 1) \
+ F(AtomicsFutexNumWaitersForTesting, 2, 1)
+
+
#define FOR_EACH_INTRINSIC_CLASSES(F) \
F(ThrowNonMethodError, 0, 1) \
F(ThrowUnsupportedSuperError, 0, 1) \
FOR_EACH_INTRINSIC_DEBUG(F) \
FOR_EACH_INTRINSIC_FORIN(F) \
FOR_EACH_INTRINSIC_FUNCTION(F) \
+ FOR_EACH_INTRINSIC_FUTEX(F) \
FOR_EACH_INTRINSIC_GENERATOR(F) \
FOR_EACH_INTRINSIC_I18N(F) \
FOR_EACH_INTRINSIC_INTERNAL(F) \
class CcTestArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
- virtual void* Allocate(size_t length) { return malloc(length); }
+ virtual void* Allocate(size_t length) {
+ void* data = AllocateUninitialized(length);
+ return data == NULL ? data : memset(data, 0, length);
+ }
virtual void* AllocateUninitialized(size_t length) { return malloc(length); }
virtual void Free(void* data, size_t length) { free(data); }
// TODO(dslomov): Remove when v8:2823 is fixed.
#include "src/compilation-cache.h"
#include "src/debug.h"
#include "src/execution.h"
+#include "src/futex-emulation.h"
#include "src/objects.h"
#include "src/parser.h"
#include "src/unicode-inl.h"
"result;\n",
0);
}
+
+class FutexInterruptionThread : public v8::base::Thread {
+ public:
+ explicit FutexInterruptionThread(v8::Isolate* isolate)
+ : Thread(Options("FutexInterruptionThread")), isolate_(isolate) {}
+
+ virtual void Run() {
+ // Wait a bit before terminating.
+ v8::base::OS::Sleep(v8::base::TimeDelta::FromMilliseconds(100));
+ v8::V8::TerminateExecution(isolate_);
+ }
+
+ private:
+ v8::Isolate* isolate_;
+};
+
+
+TEST(FutexInterruption) {
+ i::FLAG_harmony_sharedarraybuffer = true;
+ i::FLAG_harmony_atomics = true;
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::HandleScope scope(isolate);
+ LocalContext env;
+
+ FutexInterruptionThread timeout_thread(isolate);
+
+ v8::TryCatch try_catch(CcTest::isolate());
+ timeout_thread.Start();
+
+ CompileRun(
+ "var ab = new SharedArrayBuffer(4);"
+ "var i32a = new Int32Array(ab);"
+ "Atomics.futexWait(i32a, 0, 0);");
+ CHECK(try_catch.HasTerminated());
+}
--- /dev/null
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --allow-natives-syntax --harmony-atomics --harmony-sharedarraybuffer
+
+(function TestFailsWithNonSharedArray() {
+ var ab = new ArrayBuffer(16);
+
+ var i8a = new Int8Array(ab);
+ var i16a = new Int16Array(ab);
+ var i32a = new Int32Array(ab);
+ var ui8a = new Uint8Array(ab);
+ var ui8ca = new Uint8ClampedArray(ab);
+ var ui16a = new Uint16Array(ab);
+ var ui32a = new Uint32Array(ab);
+ var f32a = new Float32Array(ab);
+ var f64a = new Float64Array(ab);
+
+ [i8a, i16a, i32a, ui8a, ui8ca, ui16a, ui32a, f32a, f64a].forEach(function(
+ ta) {
+ assertThrows(function() { Atomics.futexWait(ta, 0, 0); });
+ assertThrows(function() { Atomics.futexWake(ta, 0, 1); });
+ assertThrows(function() { Atomics.futexWakeOrRequeue(ta, 0, 1, 0, 0); });
+ });
+})();
+
+(function TestFailsWithNonSharedInt32Array() {
+ var sab = new SharedArrayBuffer(16);
+
+ var i8a = new Int8Array(sab);
+ var i16a = new Int16Array(sab);
+ var ui8a = new Uint8Array(sab);
+ var ui8ca = new Uint8ClampedArray(sab);
+ var ui16a = new Uint16Array(sab);
+ var ui32a = new Uint32Array(sab);
+ var f32a = new Float32Array(sab);
+ var f64a = new Float64Array(sab);
+
+ [i8a, i16a, ui8a, ui8ca, ui16a, ui32a, f32a, f64a].forEach(function(
+ ta) {
+ assertThrows(function() { Atomics.futexWait(ta, 0, 0); });
+ assertThrows(function() { Atomics.futexWake(ta, 0, 1); });
+ assertThrows(function() { Atomics.futexWakeOrRequeue(ta, 0, 1, 0, 0); });
+ });
+})();
+
+(function TestInvalidIndex() {
+ var i32a = new Int32Array(new SharedArrayBuffer(16));
+
+ // Valid indexes are 0-3.
+ [-1, 4, 100].forEach(function(invalidIndex) {
+ assertEquals(undefined, Atomics.futexWait(i32a, invalidIndex, 0));
+ assertEquals(undefined, Atomics.futexWake(i32a, invalidIndex, 0));
+ var validIndex = 0;
+ assertEquals(undefined, Atomics.futexWakeOrRequeue(i32a, invalidIndex, 0, 0,
+ validIndex));
+ assertEquals(undefined, Atomics.futexWakeOrRequeue(i32a, validIndex, 0, 0,
+ invalidIndex));
+ });
+
+})();
+
+(function TestWaitTimeout() {
+ var i32a = new Int32Array(new SharedArrayBuffer(16));
+ var waitMs = 100;
+ var startTime = new Date();
+ assertEquals(Atomics.TIMEDOUT, Atomics.futexWait(i32a, 0, 0, waitMs));
+ var endTime = new Date();
+ assertTrue(endTime - startTime >= waitMs);
+})();
+
+(function TestWaitNotEqual() {
+ var i32a = new Int32Array(new SharedArrayBuffer(16));
+ assertEquals(Atomics.NOTEQUAL, Atomics.futexWait(i32a, 0, 42));
+})();
+
+(function TestWaitNegativeTimeout() {
+ var i32a = new Int32Array(new SharedArrayBuffer(16));
+ assertEquals(Atomics.TIMEDOUT, Atomics.futexWait(i32a, 0, 0, -1));
+ assertEquals(Atomics.TIMEDOUT, Atomics.futexWait(i32a, 0, 0, -Infinity));
+})();
+
+//// WORKER ONLY TESTS
+
+if (this.Worker) {
+
+ var TestWaitWithTimeout = function(timeout) {
+ var sab = new SharedArrayBuffer(16);
+ var i32a = new Int32Array(sab);
+
+ var workerScript =
+ `onmessage = function(sab) {
+ var i32a = new Int32Array(sab);
+ var result = Atomics.futexWait(i32a, 0, 0, ${timeout});
+ postMessage(result);
+ };`;
+
+ var worker = new Worker(workerScript);
+ worker.postMessage(sab, [sab]);
+
+ // Spin until the worker is waiting on the futex.
+ while (%AtomicsFutexNumWaitersForTesting(i32a, 0) != 1) {}
+
+ Atomics.futexWake(i32a, 0, 1);
+ assertEquals(Atomics.OK, worker.getMessage());
+ worker.terminate();
+ };
+
+ // Test various infinite timeouts
+ TestWaitWithTimeout(undefined);
+ TestWaitWithTimeout(NaN);
+ TestWaitWithTimeout(Infinity);
+
+
+ (function TestWakeMulti() {
+ var sab = new SharedArrayBuffer(20);
+ var i32a = new Int32Array(sab);
+
+ // SAB values:
+ // i32a[id], where id in range [0, 3]:
+ // 0 => Worker |id| is still waiting on the futex
+ // 1 => Worker |id| is not waiting on futex, but has not be reaped by the
+ // main thread.
+ // 2 => Worker |id| has been reaped.
+ //
+ // i32a[4]:
+ // always 0. Each worker is waiting on this index.
+
+ var workerScript =
+ `onmessage = function(msg) {
+ var id = msg.id;
+ var i32a = new Int32Array(msg.sab);
+
+ // Wait on i32a[4] (should be zero).
+ var result = Atomics.futexWait(i32a, 4, 0);
+ // Set i32a[id] to 1 to notify the main thread which workers were
+ // woken up.
+ Atomics.store(i32a, id, 1);
+ postMessage(result);
+ };`;
+
+ var id;
+ var workers = [];
+ for (id = 0; id < 4; id++) {
+ workers[id] = new Worker(workerScript);
+ workers[id].postMessage({sab: sab, id: id}, [sab]);
+ }
+
+ // Spin until all workers are waiting on the futex.
+ while (%AtomicsFutexNumWaitersForTesting(i32a, 4) != 4) {}
+
+ // Wake up three waiters.
+ assertEquals(3, Atomics.futexWake(i32a, 4, 3));
+
+ var wokenCount = 0;
+ var waitingId = 0 + 1 + 2 + 3;
+ while (wokenCount < 3) {
+ for (id = 0; id < 4; id++) {
+ // Look for workers that have not yet been reaped. Set i32a[id] to 2
+ // when they've been processed so we don't look at them again.
+ if (Atomics.compareExchange(i32a, id, 1, 2) == 1) {
+ assertEquals(Atomics.OK, workers[id].getMessage());
+ workers[id].terminate();
+ waitingId -= id;
+ wokenCount++;
+ }
+ }
+ }
+
+ assertEquals(3, wokenCount);
+ assertEquals(0, Atomics.load(i32a, waitingId));
+ assertEquals(1, %AtomicsFutexNumWaitersForTesting(i32a, 4));
+
+ // Finally wake the last waiter.
+ assertEquals(1, Atomics.futexWake(i32a, 4, 1));
+ assertEquals(Atomics.OK, workers[waitingId].getMessage());
+ workers[waitingId].terminate();
+
+ assertEquals(0, %AtomicsFutexNumWaitersForTesting(i32a, 4));
+
+ })();
+
+ (function TestWakeOrRequeue() {
+ var sab = new SharedArrayBuffer(24);
+ var i32a = new Int32Array(sab);
+
+ // SAB values:
+ // i32a[id], where id in range [0, 3]:
+ // 0 => Worker |id| is still waiting on the futex
+ // 1 => Worker |id| is not waiting on futex, but has not be reaped by the
+ // main thread.
+ // 2 => Worker |id| has been reaped.
+ //
+ // i32a[4]:
+ // always 0. Each worker will initially wait on this index.
+ //
+ // i32a[5]:
+ // always 0. Requeued workers will wait on this index.
+
+ var workerScript =
+ `onmessage = function(msg) {
+ var id = msg.id;
+ var i32a = new Int32Array(msg.sab);
+
+ var result = Atomics.futexWait(i32a, 4, 0, Infinity);
+ Atomics.store(i32a, id, 1);
+ postMessage(result);
+ };`;
+
+ var workers = [];
+ for (id = 0; id < 4; id++) {
+ workers[id] = new Worker(workerScript);
+ workers[id].postMessage({sab: sab, id: id}, [sab]);
+ }
+
+ // Spin until all workers are waiting on the futex.
+ while (%AtomicsFutexNumWaitersForTesting(i32a, 4) != 4) {}
+
+ var index1 = 4;
+ var index2 = 5;
+
+ // If futexWakeOrRequeue is called with the incorrect value, it shouldn't
+ // wake any waiters.
+ assertEquals(Atomics.NOTEQUAL,
+ Atomics.futexWakeOrRequeue(i32a, index1, 1, 42, index2));
+
+ assertEquals(4, %AtomicsFutexNumWaitersForTesting(i32a, index1));
+ assertEquals(0, %AtomicsFutexNumWaitersForTesting(i32a, index2));
+
+ // Now wake with the correct value.
+ assertEquals(1, Atomics.futexWakeOrRequeue(i32a, index1, 1, 0, index2));
+
+ // The workers that are still waiting should atomically be transferred to
+ // the new index.
+ assertEquals(3, %AtomicsFutexNumWaitersForTesting(i32a, index2));
+
+ // The woken worker may not have been scheduled yet. Look for which thread
+ // has set its i32a value to 1.
+ var wokenCount = 0;
+ while (wokenCount < 1) {
+ for (id = 0; id < 4; id++) {
+ if (Atomics.compareExchange(i32a, id, 1, 2) == 1) {
+ wokenCount++;
+ }
+ }
+ }
+
+ assertEquals(0, %AtomicsFutexNumWaitersForTesting(i32a, index1));
+
+ // Wake the remaining waiters.
+ assertEquals(3, Atomics.futexWake(i32a, index2, 3));
+
+ // As above, wait until the workers have been scheduled.
+ wokenCount = 0;
+ while (wokenCount < 3) {
+ for (id = 0; id < 4; id++) {
+ if (Atomics.compareExchange(i32a, id, 1, 2) == 1) {
+ wokenCount++;
+ }
+ }
+ }
+
+ assertEquals(0, %AtomicsFutexNumWaitersForTesting(i32a, index1));
+ assertEquals(0, %AtomicsFutexNumWaitersForTesting(i32a, index2));
+
+ for (id = 0; id < 4; ++id) {
+ assertEquals(Atomics.OK, workers[id].getMessage());
+ workers[id].terminate();
+ }
+
+ })();
+
+}
'../../src/full-codegen.h',
'../../src/func-name-inferrer.cc',
'../../src/func-name-inferrer.h',
+ '../../src/futex-emulation.cc',
+ '../../src/futex-emulation.h',
'../../src/gdb-jit.cc',
'../../src/gdb-jit.h',
'../../src/global-handles.cc',
'../../src/runtime/runtime-debug.cc',
'../../src/runtime/runtime-forin.cc',
'../../src/runtime/runtime-function.cc',
+ '../../src/runtime/runtime-futex.cc',
'../../src/runtime/runtime-generator.cc',
'../../src/runtime/runtime-i18n.cc',
'../../src/runtime/runtime-internal.cc',
return (constants, macros)
-TEMPLATE_PATTERN = re.compile(r'^\s+T\(([A-Z][a-zA-Z]*),')
+TEMPLATE_PATTERN = re.compile(r'^\s+T\(([A-Z][a-zA-Z0-9]*),')
def ReadMessageTemplates(lines):
templates = []