Migrate some layout timer-related code from std::chrono to Seconds and MonotonicTime
[WebKit-https.git] / Source / JavaScriptCore / runtime / AtomicsObject.cpp
1 /*
2  * Copyright (C) 2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "AtomicsObject.h"
28
29 #include "JSCInlines.h"
30 #include "JSTypedArrays.h"
31 #include "ObjectPrototype.h"
32 #include "ReleaseHeapAccessScope.h"
33 #include "TypedArrayController.h"
34
35 namespace JSC {
36
37 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(AtomicsObject);
38
39 #define FOR_EACH_ATOMICS_FUNC(macro)                                    \
40     macro(add, Add, 3)                                                  \
41     macro(and, And, 3)                                                  \
42     macro(compareExchange, CompareExchange, 4)                          \
43     macro(exchange, Exchange, 3)                                        \
44     macro(isLockFree, IsLockFree, 1)                                    \
45     macro(load, Load, 2)                                                \
46     macro(or, Or, 3)                                                    \
47     macro(store, Store, 3)                                              \
48     macro(sub, Sub, 3)                                                  \
49     macro(wait, Wait, 4)                                                \
50     macro(wake, Wake, 3)                                                \
51     macro(xor, Xor, 3)
52
53 #define DECLARE_FUNC_PROTO(lowerName, upperName, count)                 \
54     EncodedJSValue JSC_HOST_CALL atomicsFunc ## upperName(ExecState*);
55 FOR_EACH_ATOMICS_FUNC(DECLARE_FUNC_PROTO)
56 #undef DECLARE_FUNC_PROTO
57
58 const ClassInfo AtomicsObject::s_info = { "Atomics", &Base::s_info, 0, CREATE_METHOD_TABLE(AtomicsObject) };
59
60 AtomicsObject::AtomicsObject(VM& vm, Structure* structure)
61     : JSNonFinalObject(vm, structure)
62 {
63 }
64
65 AtomicsObject* AtomicsObject::create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
66 {
67     AtomicsObject* object = new (NotNull, allocateCell<AtomicsObject>(vm.heap)) AtomicsObject(vm, structure);
68     object->finishCreation(vm, globalObject);
69     return object;
70 }
71
72 Structure* AtomicsObject::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
73 {
74     return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
75 }
76
77 void AtomicsObject::finishCreation(VM& vm, JSGlobalObject* globalObject)
78 {
79     Base::finishCreation(vm);
80     ASSERT(inherits(info()));
81     
82 #define PUT_DIRECT_NATIVE_FUNC(lowerName, upperName, count) \
83     putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, #lowerName), count, atomicsFunc ## upperName, Atomics ## upperName ## Intrinsic, DontEnum);
84     FOR_EACH_ATOMICS_FUNC(PUT_DIRECT_NATIVE_FUNC)
85 #undef PUT_DIRECT_NATIVE_FUNC
86 }
87
88 namespace {
89
90 template<unsigned numExtraArgs, typename Adaptor, typename Func>
91 EncodedJSValue atomicOperationWithArgsCase(ExecState* exec, ThrowScope& scope, JSArrayBufferView* typedArrayView, unsigned accessIndex, const Func& func)
92 {
93     JSGenericTypedArrayView<Adaptor>* typedArray = jsCast<JSGenericTypedArrayView<Adaptor>*>(typedArrayView);
94     
95     typename Adaptor::Type extraArgs[numExtraArgs + 1]; // Add 1 to avoid 0 size array error in VS.
96     for (unsigned i = 0; i < numExtraArgs; ++i) {
97         int32_t value = exec->argument(2 + i).toInt32(exec);
98         RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined()));
99         extraArgs[i] = Adaptor::toNativeFromInt32(value);
100     }
101
102     typename Adaptor::Type result = func(typedArray->typedVector() + accessIndex, extraArgs);
103     return JSValue::encode(Adaptor::toJSValue(result));
104 }
105
106 unsigned validatedAccessIndex(VM& vm, ExecState* exec, JSArrayBufferView* typedArrayView)
107 {
108     auto scope = DECLARE_THROW_SCOPE(vm);
109     JSValue accessIndexValue = exec->argument(1);
110     if (UNLIKELY(!accessIndexValue.isInt32())) {
111         accessIndexValue = jsNumber(accessIndexValue.toNumber(exec));
112         RETURN_IF_EXCEPTION(scope, 0);
113         if (!accessIndexValue.isInt32()) {
114             throwRangeError(exec, scope, ASCIILiteral("Access index is not an integer."));
115             return 0;
116         }
117     }
118     int32_t accessIndex = accessIndexValue.asInt32();
119     
120     ASSERT(typedArrayView->length() <= static_cast<unsigned>(INT_MAX));
121     if (static_cast<unsigned>(accessIndex) >= typedArrayView->length()) {
122         throwRangeError(exec, scope, ASCIILiteral("Access index out of bounds for atomic access."));
123         return 0;
124     }
125     
126     return accessIndex;
127 }
128
129 template<unsigned numExtraArgs, typename Func>
130 EncodedJSValue atomicOperationWithArgs(ExecState* exec, const Func& func)
131 {
132     VM& vm = exec->vm();
133     auto scope = DECLARE_THROW_SCOPE(vm);
134
135     JSValue typedArrayValue = exec->argument(0);
136     if (!typedArrayValue.isCell()) {
137         throwTypeError(exec, scope, ASCIILiteral("Typed array argument must be a cell."));
138         return JSValue::encode(jsUndefined());
139     }
140     
141     JSCell* typedArrayCell = typedArrayValue.asCell();
142     
143     JSType type = typedArrayCell->type();
144     switch (type) {
145     case Int8ArrayType:
146     case Int16ArrayType:
147     case Int32ArrayType:
148     case Uint8ArrayType:
149     case Uint16ArrayType:
150     case Uint32ArrayType:
151         break;
152     default:
153         throwTypeError(exec, scope, ASCIILiteral("Typed array argument must be an Int8Array, Int16Array, Int32Array, Uint8Array, Uint16Array, or Uint32Array."));
154         return JSValue::encode(jsUndefined());
155     }
156     
157     JSArrayBufferView* typedArrayView = jsCast<JSArrayBufferView*>(typedArrayCell);
158     if (!typedArrayView->isShared()) {
159         throwTypeError(exec, scope, ASCIILiteral("Typed array argument must wrap a SharedArrayBuffer."));
160         return JSValue::encode(jsUndefined());
161     }
162     
163     unsigned accessIndex = validatedAccessIndex(vm, exec, typedArrayView);
164     RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined()));
165     
166     switch (type) {
167     case Int8ArrayType:
168         return atomicOperationWithArgsCase<numExtraArgs, Int8Adaptor>(exec, scope, typedArrayView, accessIndex, func);
169     case Int16ArrayType:
170         return atomicOperationWithArgsCase<numExtraArgs, Int16Adaptor>(exec, scope, typedArrayView, accessIndex, func);
171     case Int32ArrayType:
172         return atomicOperationWithArgsCase<numExtraArgs, Int32Adaptor>(exec, scope, typedArrayView, accessIndex, func);
173     case Uint8ArrayType:
174         return atomicOperationWithArgsCase<numExtraArgs, Uint8Adaptor>(exec, scope, typedArrayView, accessIndex, func);
175     case Uint16ArrayType:
176         return atomicOperationWithArgsCase<numExtraArgs, Uint16Adaptor>(exec, scope, typedArrayView, accessIndex, func);
177     case Uint32ArrayType:
178         return atomicOperationWithArgsCase<numExtraArgs, Uint32Adaptor>(exec, scope, typedArrayView, accessIndex, func);
179     default:
180         RELEASE_ASSERT_NOT_REACHED();
181         return JSValue::encode(jsUndefined());
182     }
183 }
184
185 } // anonymous namespace
186
187 EncodedJSValue JSC_HOST_CALL atomicsFuncAdd(ExecState* exec)
188 {
189     return atomicOperationWithArgs<1>(
190         exec, [&] (auto* ptr, const auto* args) {
191             return WTF::atomicExchangeAdd(ptr, args[0]);
192         });
193 }
194
195 EncodedJSValue JSC_HOST_CALL atomicsFuncAnd(ExecState* exec)
196 {
197     return atomicOperationWithArgs<1>(
198         exec, [&] (auto* ptr, const auto* args) {
199             return WTF::atomicExchangeAnd(ptr, args[0]);
200         });
201 }
202
203 EncodedJSValue JSC_HOST_CALL atomicsFuncCompareExchange(ExecState* exec)
204 {
205     return atomicOperationWithArgs<2>(
206         exec, [&] (auto* ptr, const auto* args) {
207             return WTF::atomicCompareExchangeStrong(ptr, args[0], args[1]);
208         });
209 }
210
211 EncodedJSValue JSC_HOST_CALL atomicsFuncExchange(ExecState* exec)
212 {
213     return atomicOperationWithArgs<1>(
214         exec, [&] (auto* ptr, const auto* args) {
215             return WTF::atomicExchange(ptr, args[0]);
216         });
217 }
218
219 EncodedJSValue JSC_HOST_CALL atomicsFuncIsLockFree(ExecState* exec)
220 {
221     VM& vm = exec->vm();
222     auto scope = DECLARE_THROW_SCOPE(vm);
223
224     int32_t size = exec->argument(0).toInt32(exec);
225     RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined()));
226     
227     bool result;
228     switch (size) {
229     case 1:
230     case 2:
231     case 4:
232         result = true;
233         break;
234     default:
235         result = false;
236         break;
237     }
238     return JSValue::encode(jsBoolean(result));
239 }
240
241 EncodedJSValue JSC_HOST_CALL atomicsFuncLoad(ExecState* exec)
242 {
243     return atomicOperationWithArgs<0>(
244         exec, [&] (auto* ptr, const auto*) {
245             return WTF::atomicLoad(ptr);
246         });
247 }
248
249 EncodedJSValue JSC_HOST_CALL atomicsFuncOr(ExecState* exec)
250 {
251     return atomicOperationWithArgs<1>(
252         exec, [&] (auto* ptr, const auto* args) {
253             return WTF::atomicExchangeOr(ptr, args[0]);
254         });
255 }
256
257 EncodedJSValue JSC_HOST_CALL atomicsFuncStore(ExecState* exec)
258 {
259     return atomicOperationWithArgs<1>(
260         exec, [&] (auto* ptr, const auto* args) {
261             auto value = args[0];
262             WTF::atomicStore(ptr, value);
263             return value;
264         });
265 }
266
267 EncodedJSValue JSC_HOST_CALL atomicsFuncSub(ExecState* exec)
268 {
269     return atomicOperationWithArgs<1>(
270         exec, [&] (auto* ptr, const auto* args) {
271             return WTF::atomicExchangeSub(ptr, args[0]);
272         });
273 }
274
275 EncodedJSValue JSC_HOST_CALL atomicsFuncWait(ExecState* exec)
276 {
277     VM& vm = exec->vm();
278     auto scope = DECLARE_THROW_SCOPE(vm);
279     
280     JSInt32Array* typedArray = jsDynamicCast<JSInt32Array*>(exec->argument(0));
281     if (!typedArray) {
282         throwTypeError(exec, scope, ASCIILiteral("Typed array for wait/wake must be an Int32Array."));
283         return JSValue::encode(jsUndefined());
284     }
285     
286     if (!typedArray->isShared()) {
287         throwTypeError(exec, scope, ASCIILiteral("Typed array for wait/wake must wrap a SharedArrayBuffer."));
288         return JSValue::encode(jsUndefined());
289     }
290
291     unsigned accessIndex = validatedAccessIndex(vm, exec, typedArray);
292     RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined()));
293     
294     int32_t* ptr = typedArray->typedVector() + accessIndex;
295     
296     int32_t expectedValue = exec->argument(2).toInt32(exec);
297     RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined()));
298     
299     double timeoutInMilliseconds = exec->argument(3).toNumber(exec);
300     RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined()));
301     
302     if (!vm.m_typedArrayController->isAtomicsWaitAllowedOnCurrentThread()) {
303         throwTypeError(exec, scope, ASCIILiteral("Atomics.wait cannot be called from the current thread."));
304         return JSValue::encode(jsUndefined());
305     }
306     
307     Seconds timeout = Seconds::fromMilliseconds(timeoutInMilliseconds);
308
309     // This covers the proposed rule:
310     //
311     // 4. If timeout is not provided or is undefined then let t be +inf. Otherwise:
312     //     a. Let q be ? ToNumber(timeout).
313     //     b. If q is NaN then let t be +inf, otherwise let t be max(0, q).
314     //
315     // exec->argument(3) returns undefined if it's not provided and ToNumber(undefined) returns NaN,
316     // so NaN is the only special case.
317     if (timeout == timeout)
318         timeout = std::max(0_s, timeout);
319     else
320         timeout = Seconds::infinity();
321     
322     bool didPassValidation = false;
323     ParkingLot::ParkResult result;
324     {
325         ReleaseHeapAccessScope releaseHeapAccessScope(vm.heap);
326         result = ParkingLot::parkConditionally(
327             ptr,
328             [&] () -> bool {
329                 didPassValidation = WTF::atomicLoad(ptr) == expectedValue;
330                 return didPassValidation;
331             },
332             [] () { },
333             MonotonicTime::now() + timeout);
334     }
335     const char* resultString;
336     if (!didPassValidation)
337         resultString = "not-equal";
338     else if (!result.wasUnparked)
339         resultString = "timed-out";
340     else
341         resultString = "ok";
342     return JSValue::encode(jsString(exec, ASCIILiteral(resultString)));
343 }
344
345 EncodedJSValue JSC_HOST_CALL atomicsFuncWake(ExecState* exec)
346 {
347     VM& vm = exec->vm();
348     auto scope = DECLARE_THROW_SCOPE(vm);
349     
350     JSInt32Array* typedArray = jsDynamicCast<JSInt32Array*>(exec->argument(0));
351     if (!typedArray) {
352         throwTypeError(exec, scope, ASCIILiteral("Typed array for wait/wake must be an Int32Array."));
353         return JSValue::encode(jsUndefined());
354     }
355     
356     if (!typedArray->isShared()) {
357         throwTypeError(exec, scope, ASCIILiteral("Typed array for wait/wake must wrap a SharedArrayBuffer."));
358         return JSValue::encode(jsUndefined());
359     }
360
361     unsigned accessIndex = validatedAccessIndex(vm, exec, typedArray);
362     RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined()));
363     
364     int32_t* ptr = typedArray->typedVector() + accessIndex;
365     
366     JSValue countValue = exec->argument(2);
367     unsigned count = UINT_MAX;
368     if (!countValue.isUndefined()) {
369         int32_t countInt = countValue.toInt32(exec);
370         RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined()));
371         count = std::max(0, countInt);
372     }
373
374     return JSValue::encode(jsNumber(ParkingLot::unparkCount(ptr, count)));
375 }
376
377 EncodedJSValue JSC_HOST_CALL atomicsFuncXor(ExecState* exec)
378 {
379     return atomicOperationWithArgs<1>(
380         exec, [&] (auto* ptr, const auto* args) {
381             return WTF::atomicExchangeXor(ptr, args[0]);
382         });
383 }
384
385 } // namespace JSC
386