• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "ecmascript/builtins/builtins_atomics.h"
17 
18 #include "ecmascript/base/atomic_helper.h"
19 #include "ecmascript/base/typed_array_helper-inl.h"
20 #include "libpandabase/utils/time.h"
21 
22 namespace panda::ecmascript::builtins {
23 using NumberHelper = base::NumberHelper;
24 using AtomicHelper = base::AtomicHelper;
25 using BytesSize = base::BytesSize;
26 using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer;
27 
28 WaiterList *g_waitLists = Singleton<WaiterList>::GetInstance();
29 Mutex *g_mutex = Singleton<Mutex>::GetInstance();
30 
31 // 25.4.2 Atomics.add ( typedArray, index, value )
Sub(EcmaRuntimeCallInfo * argv)32 JSTaggedValue BuiltinsAtomics::Sub(EcmaRuntimeCallInfo *argv)
33 {
34     ASSERT(argv);
35     JSThread *thread = argv->GetThread();
36     BUILTINS_API_TRACE(thread, Atomics, Sub);
37     [[maybe_unused]] EcmaHandleScope handleScope(thread);
38     JSHandle<JSTaggedValue> typedArray = GetCallArg(argv, 0);
39     JSHandle<JSTaggedValue> index = GetCallArg(argv, 1);
40     return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::SubFun());
41 }
42 
Add(EcmaRuntimeCallInfo * argv)43 JSTaggedValue BuiltinsAtomics::Add(EcmaRuntimeCallInfo *argv)
44 {
45     ASSERT(argv);
46     JSThread *thread = argv->GetThread();
47     BUILTINS_API_TRACE(thread, Atomics, Add);
48     [[maybe_unused]] EcmaHandleScope handleScope(thread);
49     JSHandle<JSTaggedValue> typedArray = GetCallArg(argv, 0);
50     JSHandle<JSTaggedValue> index = GetCallArg(argv, 1);
51     return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::AddFun());
52 }
53 
And(EcmaRuntimeCallInfo * argv)54 JSTaggedValue BuiltinsAtomics::And(EcmaRuntimeCallInfo *argv)
55 {
56     ASSERT(argv);
57     JSThread *thread = argv->GetThread();
58     BUILTINS_API_TRACE(thread, Atomics, And);
59     [[maybe_unused]] EcmaHandleScope handleScope(thread);
60     JSHandle<JSTaggedValue> typedArray = GetCallArg(argv, 0);
61     JSHandle<JSTaggedValue> index = GetCallArg(argv, 1);
62     return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::AndFun());
63 }
64 
Or(EcmaRuntimeCallInfo * argv)65 JSTaggedValue BuiltinsAtomics::Or(EcmaRuntimeCallInfo *argv)
66 {
67     ASSERT(argv);
68     JSThread *thread = argv->GetThread();
69     BUILTINS_API_TRACE(thread, Atomics, Or);
70     [[maybe_unused]] EcmaHandleScope handleScope(thread);
71     JSHandle<JSTaggedValue> typedArray = GetCallArg(argv, 0);
72     JSHandle<JSTaggedValue> index = GetCallArg(argv, 1);
73     return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::OrFun());
74 }
75 
Xor(EcmaRuntimeCallInfo * argv)76 JSTaggedValue BuiltinsAtomics::Xor(EcmaRuntimeCallInfo *argv)
77 {
78     ASSERT(argv);
79     JSThread *thread = argv->GetThread();
80     BUILTINS_API_TRACE(thread, Atomics, Xor);
81     [[maybe_unused]] EcmaHandleScope handleScope(thread);
82     JSHandle<JSTaggedValue> typedArray = GetCallArg(argv, 0);
83     JSHandle<JSTaggedValue> index = GetCallArg(argv, 1);
84     return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::XorFun());
85 }
86 
CompareExchange(EcmaRuntimeCallInfo * argv)87 JSTaggedValue BuiltinsAtomics::CompareExchange(EcmaRuntimeCallInfo *argv)
88 {
89     ASSERT(argv);
90     JSThread *thread = argv->GetThread();
91     BUILTINS_API_TRACE(thread, Atomics, CompareExchange);
92     [[maybe_unused]] EcmaHandleScope handleScope(thread);
93     JSHandle<JSTaggedValue> typedArray = GetCallArg(argv, 0);
94     JSHandle<JSTaggedValue> index = GetCallArg(argv, 1);
95     return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::CompareExchangeFun());
96 }
97 
Exchange(EcmaRuntimeCallInfo * argv)98 JSTaggedValue BuiltinsAtomics::Exchange(EcmaRuntimeCallInfo *argv)
99 {
100     ASSERT(argv);
101     JSThread *thread = argv->GetThread();
102     BUILTINS_API_TRACE(thread, Atomics, Exchange);
103     [[maybe_unused]] EcmaHandleScope handleScope(thread);
104     JSHandle<JSTaggedValue> typedArray = GetCallArg(argv, 0);
105     JSHandle<JSTaggedValue> index = GetCallArg(argv, 1);
106     return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::ExchangeFun());
107 }
108 
Store(EcmaRuntimeCallInfo * argv)109 JSTaggedValue BuiltinsAtomics::Store(EcmaRuntimeCallInfo *argv)
110 {
111     ASSERT(argv);
112     JSThread *thread = argv->GetThread();
113     BUILTINS_API_TRACE(thread, Atomics, Store);
114     [[maybe_unused]] EcmaHandleScope handleScope(thread);
115     JSHandle<JSTaggedValue> typedArray = GetCallArg(argv, 0);
116     JSHandle<JSTaggedValue> index = GetCallArg(argv, 1);
117     JSHandle<JSTaggedValue> value = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
118     return AtomicHelper::AtomicStore(thread, typedArray, index, value);
119 }
120 
Load(EcmaRuntimeCallInfo * argv)121 JSTaggedValue BuiltinsAtomics::Load(EcmaRuntimeCallInfo *argv)
122 {
123     ASSERT(argv);
124     JSThread *thread = argv->GetThread();
125     BUILTINS_API_TRACE(thread, Atomics, Load);
126     [[maybe_unused]] EcmaHandleScope handleScope(thread);
127     JSHandle<JSTaggedValue> typedArray = GetCallArg(argv, 0);
128     JSHandle<JSTaggedValue> index = GetCallArg(argv, 1);
129     return AtomicHelper::AtomicLoad(thread, typedArray, index);
130 }
131 
IsLockFree(EcmaRuntimeCallInfo * argv)132 JSTaggedValue BuiltinsAtomics::IsLockFree(EcmaRuntimeCallInfo *argv)
133 {
134     ASSERT(argv);
135     JSThread *thread = argv->GetThread();
136     BUILTINS_API_TRACE(thread, Atomics, IsLockFree);
137     [[maybe_unused]] EcmaHandleScope handleScope(thread);
138     JSHandle<JSTaggedValue> sizeTag = GetCallArg(argv, 0);
139     BytesSize size = BytesSize(JSTaggedValue::ToInt32(thread, sizeTag));
140     bool result = false;
141     switch (size) {
142         case BytesSize::ONEBYTES:
143         case BytesSize::TWOBYTES:
144         case BytesSize::FOURBYTES:
145         case BytesSize::EIGHTBYTES:
146             result = true;
147             break;
148         default:
149             result = false;
150             break;
151     }
152     return JSTaggedValue(result);
153 }
154 
155 // 25.4.11 Atomics.wait ( typedArray, index, value, timeout )
Wait(EcmaRuntimeCallInfo * argv)156 JSTaggedValue BuiltinsAtomics::Wait(EcmaRuntimeCallInfo *argv)
157 {
158     ASSERT(argv);
159     JSThread *thread = argv->GetThread();
160     BUILTINS_API_TRACE(thread, Atomics, Wait);
161     [[maybe_unused]] EcmaHandleScope handleScope(thread);
162 
163     JSHandle<JSTaggedValue> array = GetCallArg(argv, 0);
164     JSHandle<JSTaggedValue> index = GetCallArg(argv, 1);
165     JSHandle<JSTaggedValue> value = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
166     JSHandle<JSTaggedValue> timeout = GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
167 
168     // 1. Let buffer be ? ValidateIntegerTypedArray(typedArray, true).
169     JSHandle<JSTaggedValue> arrayBuffer(thread, AtomicHelper::ValidateIntegerTypedArray(thread, array, true));
170     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
171 
172     // 2. If IsSharedArrayBuffer(buffer) is false, throw a TypeError exception.
173     if (!arrayBuffer->IsSharedArrayBuffer()) {
174         THROW_TYPE_ERROR_AND_RETURN(thread, "buffer is not  sharedArrayBuffer.",
175                                     JSTaggedValue::Exception());
176     }
177 
178     // 3. Let indexedPosition be ? ValidateAtomicAccess(typedArray, index).
179     uint32_t indexedPosition = AtomicHelper::ValidateAtomicAccess(thread, array, index);
180     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
181 
182     // 4. If typedArray.[[TypedArrayName]] is "BigInt64Array", let v be ? ToBigInt64(value).
183     // 5. Otherwise, let v be ? ToInt32(value).
184     int64_t v = 0;
185     if (array->IsJSBigInt64Array()) {
186         if (value->IsBoolean()) {
187             value = JSHandle<JSTaggedValue>(thread, JSTaggedValue::ToBigInt64(thread, value));
188         }
189         v = JSHandle<BigInt>::Cast(value)->ToInt64();
190     } else {
191         v = static_cast<int64_t>(JSTaggedValue::ToInt32(thread, value));
192     }
193     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
194 
195     // 6. Let q be ? ToNumber(timeout).
196     // 7. If q is NaN or +∞��, let t be +∞; else if q is -∞��, let t be 0; else let t be max(ℝ(q), 0).
197     double t = 0;
198     if (timeout->IsUndefined()) {
199         t = base::POSITIVE_INFINITY;
200     } else {
201         JSTaggedNumber q = JSTaggedValue::ToNumber(thread, timeout);
202         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
203         t = q.GetNumber();
204         if (NumberHelper::IsNaN(q) || (!NumberHelper::IsFinite(q) && t > 0)) {
205             t = base::POSITIVE_INFINITY;
206         } else if (t < 0) {
207             t = 0;
208         }
209     }
210 
211     // 8. Let B be AgentCanSuspend().
212     // 9. If B is false, throw a TypeError exception.
213     if (!thread->GetCurrentEcmaContext()->GetAllowAtomicWait()) {
214         THROW_TYPE_ERROR_AND_RETURN(thread, "vm does not allow wait to block.",
215                                     JSTaggedValue::Exception());
216     }
217     WaitResult res = WaitResult::OK;
218     if (array->IsJSBigInt64Array()) {
219         // AtomicHelper::Wait<int64_t>(thread, arrayBuffer, indexedPosition, v, t);
220         res = DoWait<int64_t>(thread, arrayBuffer, indexedPosition, v, t);
221     } else {
222         // AtomicHelper::Wait<int32_t>(thread, arrayBuffer, indexedPosition, static_cast<int32_t>(v), t);
223         res = DoWait<int32_t>(thread, arrayBuffer, indexedPosition, static_cast<int32_t>(v), t);
224     }
225     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
226     if (res == WaitResult::OK) {
227         return globalConst->GetOkString();
228     } else if (res == WaitResult::NOT_EQ) {
229         return globalConst->GetNotEqualString();
230     }
231     return globalConst->GetTimeoutString();
232 }
233 
234 // 25.4.12 Atomics.notify ( typedArray, index, count )
Notify(EcmaRuntimeCallInfo * argv)235 JSTaggedValue BuiltinsAtomics::Notify(EcmaRuntimeCallInfo *argv)
236 {
237     ASSERT(argv);
238     JSThread *thread = argv->GetThread();
239     BUILTINS_API_TRACE(thread, Atomics, Notify);
240     [[maybe_unused]] EcmaHandleScope handleScope(thread);
241 
242     JSHandle<JSTaggedValue> array = GetCallArg(argv, 0);
243     JSHandle<JSTaggedValue> index = GetCallArg(argv, 1);
244     JSHandle<JSTaggedValue> count = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
245 
246     // 1. Let buffer be ? ValidateIntegerTypedArray(typedArray, true).
247     JSHandle<JSTaggedValue> arrayBuffer(thread, AtomicHelper::ValidateIntegerTypedArray(thread, array, true));
248     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
249 
250     // 2. Let indexedPosition be ? ValidateAtomicAccess(typedArray, index).
251     uint32_t indexedPosition = AtomicHelper::ValidateAtomicAccess(thread, array, index);
252     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
253 
254     // 3. If count is undefined, let c be +∞.
255     // 4. Else,
256     // a. Let intCount be ? ToIntegerOrInfinity(count).
257     // b. Let c be max(intCount, 0).
258     double c = 0;
259     if (count->IsUndefined()) {
260         c = base::POSITIVE_INFINITY;
261     } else {
262         JSTaggedNumber countTemp = JSTaggedValue::ToNumber(thread, count);
263         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
264         c = base::NumberHelper::TruncateDouble(countTemp.GetNumber());
265         c = c < 0 ? 0 : c;
266     }
267     // 6. If IsSharedArrayBuffer(buffer) is false, return +0��.
268     if (!arrayBuffer->IsSharedArrayBuffer()) {
269         return JSTaggedValue(0);
270     }
271     return JSTaggedValue(Signal(arrayBuffer, indexedPosition, c));
272 }
273 
274 template<typename callbackfun>
AtomicReadModifyWrite(JSThread * thread,const JSHandle<JSTaggedValue> & typedArray,JSHandle<JSTaggedValue> & index,EcmaRuntimeCallInfo * argv,const callbackfun & op)275 JSTaggedValue BuiltinsAtomics::AtomicReadModifyWrite(JSThread *thread, const JSHandle<JSTaggedValue> &typedArray,
276                                                      JSHandle<JSTaggedValue> &index, EcmaRuntimeCallInfo *argv,
277                                                      const callbackfun &op)
278 {
279     BUILTINS_API_TRACE(thread, Atomics, AtomicReadModifyWrite);
280     if (!typedArray->IsTypedArray()) {
281         THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception());
282     }
283     // 1. Let buffer be ? ValidateIntegerTypedArray(typedArray).
284     JSTaggedValue bufferValue = base::AtomicHelper::ValidateIntegerTypedArray(thread, typedArray);
285     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
286     JSHandle<JSTaggedValue> buffer(thread, bufferValue);
287     // 2. Let indexedPosition be ? ValidateAtomicAccess(typedArray, index).
288     uint32_t indexedPosition = base::AtomicHelper::ValidateAtomicAccess(thread, typedArray, index);
289     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
290     // 3. Let arrayTypeName be typedArray.[[TypedArrayName]].
291     JSHandle<JSTaggedValue> arrayTypeName(thread,
292                                           JSTypedArray::Cast(typedArray->GetTaggedObject())->GetTypedArrayName());
293     if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer.GetTaggedValue())) {
294         THROW_TYPE_ERROR_AND_RETURN(thread, "The ArrayBuffer of this value is detached buffer.",
295                                     JSTaggedValue::Exception());
296     }
297     // 7. NOTE: The above check is not redundant with the check in ValidateIntegerTypedArray because the call to
298     // ToBigInt or ToIntegerOrInfinity on the preceding lines can have arbitrary side effects, which could cause the
299     // buffer to become detached.
300     // 8. Let elementType be the Element Type value in Table 60 for arrayTypeName.
301     DataViewType elementType = JSTypedArray::GetTypeFromName(thread, arrayTypeName);
302     // 9. Return GetModifySetValueInBuffer(buffer, indexedPosition, elementType, v, op).
303     return AtomicReadModifyWriteCase(thread, buffer.GetTaggedValue(), elementType, indexedPosition, argv, op);
304 }
305 
306 template<typename callbackfun>
AtomicReadModifyWriteCase(JSThread * thread,JSTaggedValue arrBuf,DataViewType type,uint32_t indexedPosition,EcmaRuntimeCallInfo * argv,const callbackfun & op)307 JSTaggedValue BuiltinsAtomics::AtomicReadModifyWriteCase(JSThread *thread, JSTaggedValue arrBuf,
308                                                          DataViewType type, uint32_t indexedPosition,
309                                                          EcmaRuntimeCallInfo *argv, const callbackfun &op)
310 {
311     BUILTINS_API_TRACE(thread, Atomics, AtomicReadModifyWriteCase);
312     void *pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(arrBuf);
313     uint8_t *block = reinterpret_cast<uint8_t *>(pointer);
314     uint32_t size = argv->GetArgsNumber();
315     switch (type) {
316         case DataViewType::UINT8:
317             return HandleWithUint8(thread, size, block, indexedPosition, argv, op);
318         case DataViewType::INT8:
319             return HandleWithInt8(thread, size, block, indexedPosition, argv, op);
320         case DataViewType::UINT16:
321             return HandleWithUint16(thread, size, block, indexedPosition, argv, op);
322         case DataViewType::INT16:
323             return HandleWithInt16(thread, size, block, indexedPosition, argv, op);
324         case DataViewType::UINT32:
325             return HandleWithUint32(thread, size, block, indexedPosition, argv, op);
326         case DataViewType::INT32:
327             return HandleWithInt32(thread, size, block, indexedPosition, argv, op);
328         case DataViewType::BIGINT64:
329             return HandleWithBigInt64(thread, size, block, indexedPosition, argv, op);
330         case DataViewType::BIGUINT64:
331             return HandleWithBigUint64(thread, size, block, indexedPosition, argv, op);
332         default:
333             break;
334     }
335     LOG_ECMA(FATAL) << "this branch is unreachable";
336     UNREACHABLE();
337 }
338 
339 template<typename callbackfun>
HandleWithUint8(JSThread * thread,uint32_t size,uint8_t * block,uint32_t indexedPosition,EcmaRuntimeCallInfo * argv,const callbackfun & op)340 JSTaggedValue BuiltinsAtomics::HandleWithUint8(JSThread *thread, uint32_t size, uint8_t *block,
341                                                uint32_t indexedPosition,
342                                                EcmaRuntimeCallInfo *argv, const callbackfun &op)
343 {
344     BUILTINS_API_TRACE(thread, Atomics, HandleWithUint8);
345     JSHandle<JSTaggedValue> value = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
346     uint8_t tag = JSTaggedValue::ToUint8(thread, value);
347     if (size == 3) { // the number of parameters is 3
348         auto result = op((block + indexedPosition), &tag);
349         return BuiltinsBase::GetTaggedInt(result);
350     }
351     JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
352     uint8_t newTag = JSTaggedValue::ToUint8(thread, newValue);
353     uint8_t arg[ARGS_NUMBER] = {0};
354     arg[0] = tag;
355     arg[1] = newTag;
356     auto result = op((block + indexedPosition), arg);
357     return JSTaggedValue(result);
358 }
359 
360 template<typename callbackfun>
HandleWithInt8(JSThread * thread,uint32_t size,uint8_t * block,uint32_t indexedPosition,EcmaRuntimeCallInfo * argv,const callbackfun & op)361 JSTaggedValue BuiltinsAtomics::HandleWithInt8(JSThread *thread, uint32_t size, uint8_t *block,
362                                               uint32_t indexedPosition,
363                                               EcmaRuntimeCallInfo *argv, const callbackfun &op)
364 {
365     BUILTINS_API_TRACE(thread, Atomics, HandleWithInt8);
366     JSHandle<JSTaggedValue> value = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
367     int8_t tag = JSTaggedValue::ToInt8(thread, value);
368     if (size == 3) { // the number of parameters is 3
369         auto result = op(reinterpret_cast<int8_t *>(block + indexedPosition), &tag);
370         return BuiltinsBase::GetTaggedInt(result);
371     }
372     JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
373     int8_t newTag = JSTaggedValue::ToInt8(thread, newValue);
374     int8_t arg[ARGS_NUMBER] = {0};
375     arg[0] = tag;
376     arg[1] = newTag;
377     auto result = op(reinterpret_cast<int8_t *>(block + indexedPosition), arg);
378     return JSTaggedValue(result);
379 }
380 
381 template<typename callbackfun>
HandleWithUint16(JSThread * thread,uint32_t size,uint8_t * block,uint32_t indexedPosition,EcmaRuntimeCallInfo * argv,const callbackfun & op)382 JSTaggedValue BuiltinsAtomics::HandleWithUint16(JSThread *thread, uint32_t size, uint8_t *block,
383                                                 uint32_t indexedPosition,
384                                                 EcmaRuntimeCallInfo *argv, const callbackfun &op)
385 {
386     BUILTINS_API_TRACE(thread, Atomics, HandleWithUint16);
387     JSHandle<JSTaggedValue> value = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
388     uint16_t tag = JSTaggedValue::ToUint16(thread, value);
389     if (size == 3) { // the number of parameters is 3
390         auto result = op(reinterpret_cast<uint16_t *>(block + indexedPosition), &tag);
391         return BuiltinsBase::GetTaggedInt(result);
392     }
393     JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
394     uint16_t newTag = JSTaggedValue::ToUint16(thread, newValue);
395     uint16_t arg[ARGS_NUMBER] = {0};
396     arg[0] = tag;
397     arg[1] = newTag;
398     auto result = op(reinterpret_cast<uint16_t *>(block + indexedPosition), arg);
399     return JSTaggedValue(result);
400 }
401 
402 template<typename callbackfun>
HandleWithInt16(JSThread * thread,uint32_t size,uint8_t * block,uint32_t indexedPosition,EcmaRuntimeCallInfo * argv,const callbackfun & op)403 JSTaggedValue BuiltinsAtomics::HandleWithInt16(JSThread *thread, uint32_t size, uint8_t *block,
404                                                uint32_t indexedPosition,
405                                                EcmaRuntimeCallInfo *argv, const callbackfun &op)
406 {
407     BUILTINS_API_TRACE(thread, Atomics, HandleWithInt16);
408     JSHandle<JSTaggedValue> value = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
409     int16_t tag = JSTaggedValue::ToInt16(thread, value);
410     if (size == 3) { // the number of parameters is 3
411         auto result = op(reinterpret_cast<int16_t *>(block + indexedPosition), &tag);
412         return BuiltinsBase::GetTaggedInt(result);
413     }
414     JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
415     int16_t newTag = JSTaggedValue::ToInt16(thread, newValue);
416     int16_t arg[ARGS_NUMBER] = {0};
417     arg[0] = tag;
418     arg[1] = newTag;
419     auto result = op(reinterpret_cast<int16_t *>(block + indexedPosition), arg);
420     return JSTaggedValue(result);
421 }
422 
423 template<typename callbackfun>
HandleWithUint32(JSThread * thread,uint32_t size,uint8_t * block,uint32_t indexedPosition,EcmaRuntimeCallInfo * argv,const callbackfun & op)424 JSTaggedValue BuiltinsAtomics::HandleWithUint32(JSThread *thread, uint32_t size, uint8_t *block,
425                                                 uint32_t indexedPosition,
426                                                 EcmaRuntimeCallInfo *argv, const callbackfun &op)
427 {
428     BUILTINS_API_TRACE(thread, Atomics, HandleWithUint32);
429     JSHandle<JSTaggedValue> value = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
430     uint32_t tag = JSTaggedValue::ToUint32(thread, value);
431     if (size == 3) { // the number of parameters is 3
432         auto result = op(reinterpret_cast<uint32_t *>(block + indexedPosition), &tag);
433         return JSTaggedValue(result);
434     }
435     JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
436     uint32_t newTag = JSTaggedValue::ToUint32(thread, newValue);
437     uint32_t arg[ARGS_NUMBER] = {0};
438     arg[0] = tag;
439     arg[1] = newTag;
440     auto result = op(reinterpret_cast<uint32_t *>(block + indexedPosition), arg);
441     return JSTaggedValue(result);
442 }
443 
444 template<typename callbackfun>
HandleWithInt32(JSThread * thread,uint32_t size,uint8_t * block,uint32_t indexedPosition,EcmaRuntimeCallInfo * argv,const callbackfun & op)445 JSTaggedValue BuiltinsAtomics::HandleWithInt32(JSThread *thread, uint32_t size, uint8_t *block,
446                                                uint32_t indexedPosition,
447                                                EcmaRuntimeCallInfo *argv, const callbackfun &op)
448 {
449     BUILTINS_API_TRACE(thread, Atomics, HandleWithInt32);
450     JSHandle<JSTaggedValue> value = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
451     int32_t tag = JSTaggedValue::ToInt32(thread, value);
452     if (size == 3) { // the number of parameters is 3
453         auto result = op(reinterpret_cast<int32_t *>(block + indexedPosition), &tag);
454         return BuiltinsBase::GetTaggedInt(result);
455     }
456     JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
457     int32_t newTag = JSTaggedValue::ToInt32(thread, newValue);
458     int32_t arg[ARGS_NUMBER] = {0};
459     arg[0] = tag;
460     arg[1] = newTag;
461     auto result = op(reinterpret_cast<int32_t *>(block + indexedPosition), arg);
462     return JSTaggedValue(result);
463 }
464 
465 template<typename callbackfun>
HandleWithBigInt64(JSThread * thread,uint32_t size,uint8_t * block,uint32_t indexedPosition,EcmaRuntimeCallInfo * argv,const callbackfun & op)466 JSTaggedValue BuiltinsAtomics::HandleWithBigInt64(JSThread *thread, uint32_t size, uint8_t *block,
467                                                   uint32_t indexedPosition,
468                                                   EcmaRuntimeCallInfo *argv, const callbackfun &op)
469 {
470     BUILTINS_API_TRACE(thread, Atomics, HandleWithBigInt64);
471     JSHandle<JSTaggedValue> value = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
472     int64_t val = 0;
473     bool lossless = true;
474     BigInt::BigIntToInt64(thread, value, &val, &lossless);
475     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
476     if (size == 3) { // the number of parameters is 3
477         auto result = op(reinterpret_cast<int64_t *>(block + indexedPosition), &val);
478         return BigInt::Int64ToBigInt(thread, result).GetTaggedValue();
479     }
480     JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
481     int64_t newVal = 0;
482     BigInt::BigIntToInt64(thread, newValue, &newVal, &lossless);
483     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
484     int64_t arg[ARGS_NUMBER] = {0};
485     arg[0] = val;
486     arg[1] = newVal;
487     auto result = op(reinterpret_cast<int64_t *>(block + indexedPosition), arg);
488     return BigInt::Int64ToBigInt(thread, result).GetTaggedValue();
489 }
490 
491 template<typename callbackfun>
HandleWithBigUint64(JSThread * thread,uint32_t size,uint8_t * block,uint32_t indexedPosition,EcmaRuntimeCallInfo * argv,const callbackfun & op)492 JSTaggedValue BuiltinsAtomics::HandleWithBigUint64(JSThread *thread, uint32_t size, uint8_t *block,
493                                                    uint32_t indexedPosition,
494                                                    EcmaRuntimeCallInfo *argv, const callbackfun &op)
495 {
496     BUILTINS_API_TRACE(thread, Atomics, HandleWithBigUint64);
497     JSHandle<JSTaggedValue> value = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
498     uint64_t val = 0;
499     bool lossless = true;
500     BigInt::BigIntToUint64(thread, value, &val, &lossless);
501     if (size == 3) { // the number of parameters is 3
502         auto result = op(reinterpret_cast<uint64_t *>(block + indexedPosition), &val);
503         return BigInt::Uint64ToBigInt(thread, result).GetTaggedValue();
504     }
505     JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
506     uint64_t newVal = 0;
507     BigInt::BigIntToUint64(thread, newValue, &newVal, &lossless);
508     uint64_t arg[ARGS_NUMBER] = {0};
509     arg[0] = val;
510     arg[1] = newVal;
511     auto result = op(reinterpret_cast<uint64_t *>(block + indexedPosition), arg);
512     return BigInt::Uint64ToBigInt(thread, result).GetTaggedValue();
513 }
514 
515 template <typename T>
DoWait(JSThread * thread,JSHandle<JSTaggedValue> & arrayBuffer,size_t index,T execpt,double timeout)516 WaitResult BuiltinsAtomics::DoWait(JSThread *thread, JSHandle<JSTaggedValue> &arrayBuffer,
517                                    size_t index, T execpt, double timeout)
518 {
519     BUILTINS_API_TRACE(thread, Atomics, DoWait);
520     MutexGuard lockGuard(g_mutex);
521     void *buffer = BuiltinsArrayBuffer::GetDataPointFromBuffer(arrayBuffer.GetTaggedValue());
522     ASSERT(buffer != nullptr);
523     WaiterListNode *node = thread->GetCurrentEcmaContext()->GetWaiterListNode();
524     node->date_ = buffer;
525     node->index_ = index;
526     node->waitPointer_ = reinterpret_cast<int8_t*>(buffer) + index;
527     node->waiting_ = true;
528     std::atomic<T> *atomicValue = reinterpret_cast<std::atomic<T> *>(node->waitPointer_);
529     T value = atomicValue->load();
530     if (value != execpt) {
531         return WaitResult::NOT_EQ;
532     }
533     g_waitLists->AddNode(node);
534     uint64_t currentTime = 0;
535     uint64_t timeoutTime = 0;
536     bool hasTimeout = timeout != base::POSITIVE_INFINITY;
537     if (hasTimeout) {
538         currentTime = time::GetCurrentTimeInMillis();
539         timeoutTime = currentTime + static_cast<uint64_t>(timeout);
540     }
541     WaitResult res = WaitResult::OK;
542     while (true) {
543         if (!node->waiting_) {
544             res = WaitResult::OK;
545             break;
546         }
547         if (hasTimeout) {
548             currentTime = time::GetCurrentTimeInMillis();
549             if (currentTime >= timeoutTime) {
550                 res = WaitResult::TIME_OUT;
551                 break;
552             }
553             uint64_t untilTime = timeoutTime - currentTime;
554             ASSERT(untilTime != 0);
555 
556             node->cond_.TimedWait(g_mutex, untilTime);
557         } else {
558             node->cond_.Wait(g_mutex);
559         }
560     }
561     g_waitLists->DeleteNode(node);
562     node->waiting_ = false;
563     return res;
564 }
565 
Signal(JSHandle<JSTaggedValue> & arrayBuffer,const size_t & index,double wakeCount)566 uint32_t BuiltinsAtomics::Signal(JSHandle<JSTaggedValue> &arrayBuffer, const size_t &index, double wakeCount)
567 {
568     void *buffer = BuiltinsArrayBuffer::GetDataPointFromBuffer(arrayBuffer.GetTaggedValue());
569     ASSERT(buffer != nullptr);
570     MutexGuard lockGuard(g_mutex);
571     auto &locationListMap = g_waitLists->locationListMap_;
572     auto iter = locationListMap.find(reinterpret_cast<int8_t *>(buffer) + index);
573     if (iter == locationListMap.end()) {
574         return 0;
575     }
576     WaiterListNode *node = iter->second.pHead;
577     uint32_t wokenUpCount = 0;
578     while (node != nullptr && wakeCount > 0.0) {
579         if (!node->waiting_) {
580             node = node->next_;
581             continue;
582         }
583         if (buffer == node->date_) {
584             ASSERT(index == node->index_);
585             node->waiting_ = false;
586 
587             WaiterListNode *oldNode = node;
588             node = node->next_;
589             oldNode->cond_.Signal();
590             if (wakeCount != base::POSITIVE_INFINITY) {
591                 wakeCount--;
592             }
593             wokenUpCount++;
594             continue;
595         }
596         node = node->next_;
597     }
598     return wokenUpCount;
599 }
600 } // namespace panda::ecmascript::builtins
601