builtins-sharedarraybuffer.cc 9.97 KB
Newer Older
1 2 3 4
// Copyright 2016 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.

5 6 7
#include "src/base/macros.h"
#include "src/base/platform/mutex.h"
#include "src/base/platform/time.h"
8
#include "src/builtins/builtins-utils-inl.h"
9
#include "src/builtins/builtins.h"
10
#include "src/codegen/code-factory.h"
11
#include "src/common/globals.h"
12
#include "src/execution/futex-emulation.h"
13
#include "src/heap/factory.h"
14
#include "src/logging/counters.h"
15
#include "src/numbers/conversions-inl.h"
16
#include "src/objects/js-array-buffer-inl.h"
17
#include "src/objects/objects-inl.h"
18 19 20 21

namespace v8 {
namespace internal {

22
// See builtins-arraybuffer.cc for implementations of
23
// SharedArrayBuffer.prototype.byteLength and SharedArrayBuffer.prototype.slice
24

25
// https://tc39.es/ecma262/#sec-atomics.islockfree
26
inline bool AtomicIsLockFree(double size) {
27
  // According to the standard, 1, 2, and 4 byte atomics are supposed to be
28 29 30
  // 'lock free' on every platform. 'Lock free' means that all possible uses of
  // those atomics guarantee forward progress for the agent cluster (i.e. all
  // threads in contrast with a single thread).
31
  //
32 33 34 35 36 37 38 39
  // This property is often, but not always, aligned with whether atomic
  // accesses are implemented with software locks such as mutexes.
  //
  // V8 has lock free atomics for all sizes on all supported first-class
  // architectures: ia32, x64, ARM32 variants, and ARM64. Further, this property
  // is depended upon by WebAssembly, which prescribes that all atomic accesses
  // are always lock free.
  return size == 1 || size == 2 || size == 4 || size == 8;
40 41
}

42
// https://tc39.es/ecma262/#sec-atomics.islockfree
43 44 45
BUILTIN(AtomicsIsLockFree) {
  HandleScope scope(isolate);
  Handle<Object> size = args.atOrUndefined(isolate, 1);
46 47
  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, size,
                                     Object::ToNumber(isolate, size));
48 49 50
  return *isolate->factory()->ToBoolean(AtomicIsLockFree(size->Number()));
}

51 52 53
// https://tc39.es/ecma262/#sec-validatesharedintegertypedarray
V8_WARN_UNUSED_RESULT MaybeHandle<JSTypedArray> ValidateIntegerTypedArray(
    Isolate* isolate, Handle<Object> object, const char* method_name,
54
    bool only_int32_and_big_int64 = false) {
55 56
  if (object->IsJSTypedArray()) {
    Handle<JSTypedArray> typed_array = Handle<JSTypedArray>::cast(object);
57 58 59 60 61 62 63 64 65 66 67 68 69 70

    if (typed_array->WasDetached()) {
      THROW_NEW_ERROR(
          isolate,
          NewTypeError(
              MessageTemplate::kDetachedOperation,
              isolate->factory()->NewStringFromAsciiChecked(method_name)),
          JSTypedArray);
    }

    if (only_int32_and_big_int64) {
      if (typed_array->type() == kExternalInt32Array ||
          typed_array->type() == kExternalBigInt64Array) {
        return typed_array;
71
      }
72 73 74 75 76
    } else {
      if (typed_array->type() != kExternalFloat32Array &&
          typed_array->type() != kExternalFloat64Array &&
          typed_array->type() != kExternalUint8ClampedArray)
        return typed_array;
77 78 79 80 81
    }
  }

  THROW_NEW_ERROR(
      isolate,
82
      NewTypeError(only_int32_and_big_int64
83 84
                       ? MessageTemplate::kNotInt32OrBigInt64TypedArray
                       : MessageTemplate::kNotIntegerTypedArray,
85 86 87 88
                   object),
      JSTypedArray);
}

89
// https://tc39.es/ecma262/#sec-validateatomicaccess
90
// ValidateAtomicAccess( typedArray, requestIndex )
91
V8_WARN_UNUSED_RESULT Maybe<size_t> ValidateAtomicAccess(
92 93
    Isolate* isolate, Handle<JSTypedArray> typed_array,
    Handle<Object> request_index) {
94 95 96 97 98 99 100
  Handle<Object> access_index_obj;
  ASSIGN_RETURN_ON_EXCEPTION_VALUE(
      isolate, access_index_obj,
      Object::ToIndex(isolate, request_index,
                      MessageTemplate::kInvalidAtomicAccessIndex),
      Nothing<size_t>());

101
  size_t access_index;
102
  size_t typed_array_length = typed_array->length();
103
  if (!TryNumberToSize(*access_index_obj, &access_index) ||
104
      access_index >= typed_array_length) {
105 106 107 108 109 110 111
    isolate->Throw(*isolate->factory()->NewRangeError(
        MessageTemplate::kInvalidAtomicAccessIndex));
    return Nothing<size_t>();
  }
  return Just<size_t>(access_index);
}

112
namespace {
113 114 115 116 117 118 119 120 121

inline size_t GetAddress64(size_t index, size_t byte_offset) {
  return (index << 3) + byte_offset;
}

inline size_t GetAddress32(size_t index, size_t byte_offset) {
  return (index << 2) + byte_offset;
}

122 123 124 125 126 127 128 129 130 131
}  // namespace

// ES #sec-atomics.notify
// Atomics.notify( typedArray, index, count )
BUILTIN(AtomicsNotify) {
  HandleScope scope(isolate);
  Handle<Object> array = args.atOrUndefined(isolate, 1);
  Handle<Object> index = args.atOrUndefined(isolate, 2);
  Handle<Object> count = args.atOrUndefined(isolate, 3);

132
  Handle<JSTypedArray> sta;
133
  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
134 135
      isolate, sta,
      ValidateIntegerTypedArray(isolate, array, "Atomics.notify", true));
136

137
  // 2. Let i be ? ValidateAtomicAccess(typedArray, index).
138
  Maybe<size_t> maybe_index = ValidateAtomicAccess(isolate, sta, index);
139
  if (maybe_index.IsNothing()) return ReadOnlyRoots(isolate).exception();
140 141
  size_t i = maybe_index.FromJust();

142 143 144 145
  // 3. If count is undefined, let c be +∞.
  // 4. Else,
  //   a. Let intCount be ? ToInteger(count).
  //   b. Let c be max(intCount, 0).
146 147 148 149
  uint32_t c;
  if (count->IsUndefined(isolate)) {
    c = kMaxUInt32;
  } else {
150 151
    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, count,
                                       Object::ToInteger(isolate, count));
152
    double count_double = count->Number();
153
    if (count_double < 0) {
154
      count_double = 0;
155
    } else if (count_double > kMaxUInt32) {
156
      count_double = kMaxUInt32;
157
    }
158 159 160
    c = static_cast<uint32_t>(count_double);
  }

161 162 163
  // Steps 5-9 performed in FutexEmulation::Wake.

  // 10. If IsSharedArrayBuffer(buffer) is false, return 0.
164
  Handle<JSArrayBuffer> array_buffer = sta->GetBuffer();
165
  size_t wake_addr;
166

167 168 169 170 171
  if (V8_UNLIKELY(!sta->GetBuffer()->is_shared())) {
    return Smi::FromInt(0);
  }

  // Steps 11-17 performed in FutexEmulation::Wake.
172
  if (sta->type() == kExternalBigInt64Array) {
173
    wake_addr = GetAddress64(i, sta->byte_offset());
174 175
  } else {
    DCHECK(sta->type() == kExternalInt32Array);
176
    wake_addr = GetAddress32(i, sta->byte_offset());
177
  }
178
  return FutexEmulation::Wake(array_buffer, wake_addr, c);
179 180
}

181 182 183
Object DoWait(Isolate* isolate, FutexEmulation::WaitMode mode,
              Handle<Object> array, Handle<Object> index, Handle<Object> value,
              Handle<Object> timeout) {
184
  // 1. Let buffer be ? ValidateIntegerTypedArray(typedArray, true).
185 186
  Handle<JSTypedArray> sta;
  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
187 188
      isolate, sta,
      ValidateIntegerTypedArray(isolate, array, "Atomics.wait", true));
189

190 191 192 193 194 195 196
  // 2. If IsSharedArrayBuffer(buffer) is false, throw a TypeError exception.
  if (V8_UNLIKELY(!sta->GetBuffer()->is_shared())) {
    THROW_NEW_ERROR_RETURN_FAILURE(
        isolate, NewTypeError(MessageTemplate::kNotSharedTypedArray, array));
  }

  // 3. Let i be ? ValidateAtomicAccess(typedArray, index).
197
  Maybe<size_t> maybe_index = ValidateAtomicAccess(isolate, sta, index);
198
  if (maybe_index.IsNothing()) return ReadOnlyRoots(isolate).exception();
199 200
  size_t i = maybe_index.FromJust();

201 202 203
  // 4. Let arrayTypeName be typedArray.[[TypedArrayName]].
  // 5. If arrayTypeName is "BigInt64Array", let v be ? ToBigInt64(value).
  // 6. Otherwise, let v be ? ToInt32(value).
204 205 206 207 208 209 210 211
  if (sta->type() == kExternalBigInt64Array) {
    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, value,
                                       BigInt::FromObject(isolate, value));
  } else {
    DCHECK(sta->type() == kExternalInt32Array);
    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, value,
                                       Object::ToInt32(isolate, value));
  }
212

213 214
  // 7. Let q be ? ToNumber(timeout).
  // 8. If q is NaN, let t be +∞, else let t be max(q, 0).
215 216
  double timeout_number;
  if (timeout->IsUndefined(isolate)) {
217
    timeout_number = ReadOnlyRoots(isolate).infinity_value().Number();
218 219
  } else {
    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, timeout,
220
                                       Object::ToNumber(isolate, timeout));
221 222
    timeout_number = timeout->Number();
    if (std::isnan(timeout_number))
223
      timeout_number = ReadOnlyRoots(isolate).infinity_value().Number();
224 225 226 227
    else if (timeout_number < 0)
      timeout_number = 0;
  }

228
  // 9. If mode is sync, then
229 230 231 232
  //   a. Let B be AgentCanSuspend().
  //   b. If B is false, throw a TypeError exception.
  if (mode == FutexEmulation::WaitMode::kSync &&
      !isolate->allow_atomics_wait()) {
233 234 235 236 237 238
    THROW_NEW_ERROR_RETURN_FAILURE(
        isolate, NewTypeError(MessageTemplate::kAtomicsWaitNotAllowed));
  }

  Handle<JSArrayBuffer> array_buffer = sta->GetBuffer();

239 240
  if (sta->type() == kExternalBigInt64Array) {
    return FutexEmulation::WaitJs64(
241
        isolate, mode, array_buffer, GetAddress64(i, sta->byte_offset()),
242 243 244
        Handle<BigInt>::cast(value)->AsInt64(), timeout_number);
  } else {
    DCHECK(sta->type() == kExternalInt32Array);
245
    return FutexEmulation::WaitJs32(isolate, mode, array_buffer,
246 247 248
                                    GetAddress32(i, sta->byte_offset()),
                                    NumberToInt32(*value), timeout_number);
  }
249 250
}

251
// https://tc39.es/ecma262/#sec-atomics.wait
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
// Atomics.wait( typedArray, index, value, timeout )
BUILTIN(AtomicsWait) {
  HandleScope scope(isolate);
  Handle<Object> array = args.atOrUndefined(isolate, 1);
  Handle<Object> index = args.atOrUndefined(isolate, 2);
  Handle<Object> value = args.atOrUndefined(isolate, 3);
  Handle<Object> timeout = args.atOrUndefined(isolate, 4);

  return DoWait(isolate, FutexEmulation::WaitMode::kSync, array, index, value,
                timeout);
}

BUILTIN(AtomicsWaitAsync) {
  HandleScope scope(isolate);
  Handle<Object> array = args.atOrUndefined(isolate, 1);
  Handle<Object> index = args.atOrUndefined(isolate, 2);
  Handle<Object> value = args.atOrUndefined(isolate, 3);
  Handle<Object> timeout = args.atOrUndefined(isolate, 4);

  return DoWait(isolate, FutexEmulation::WaitMode::kAsync, array, index, value,
                timeout);
}

275 276
}  // namespace internal
}  // namespace v8