• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 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 #ifndef ECMASCRIPT_SHARED_OBJECTS_CONCURRENT_API_SCOPE_H
17 #define ECMASCRIPT_SHARED_OBJECTS_CONCURRENT_API_SCOPE_H
18 
19 #include "ecmascript/js_object.h"
20 #include "ecmascript/shared_objects/js_shared_set.h"
21 #include "ecmascript/shared_objects/js_shared_typed_array.h"
22 
23 #include "ecmascript/containers/containers_errors.h"
24 #include "macros.h"
25 
26 namespace panda::ecmascript {
27 enum class ModType : uint8_t {
28     READ = 0,
29     WRITE = 1
30 };
31 template<typename Container, ModType modType = ModType::READ>
32 class ConcurrentApiScope final {
33 public:
34     ConcurrentApiScope(JSThread *thread, const JSHandle<JSTaggedValue> &objHandle, SCheckMode mode = SCheckMode::CHECK)
thread_(thread)35         : thread_(thread), objHandle_(objHandle), checkMode_(mode)
36     {
37         if (checkMode_ == SCheckMode::SKIP) {
38             return;
39         }
40         if constexpr (modType == ModType::READ) {
41             CanRead();
42         } else {
43             CanWrite();
44         }
45     }
46 
~ConcurrentApiScope()47     ~ConcurrentApiScope()
48     {
49         if (checkMode_ == SCheckMode::SKIP) {
50             return;
51         }
52         if constexpr (modType == ModType::READ) {
53             ReadDone();
54         } else {
55             WriteDone();
56         }
57     }
58 
59     static constexpr uint32_t WRITE_MOD_MASK = 1 << 31;
60 
61 private:
62     NO_COPY_SEMANTIC(ConcurrentApiScope);
63     NO_MOVE_SEMANTIC(ConcurrentApiScope);
GetModRecord()64     inline uint32_t GetModRecord()
65     {
66         return reinterpret_cast<volatile std::atomic<uint32_t> *>(
67             ToUintPtr(objHandle_->GetTaggedObject()) +
68             Container::MOD_RECORD_OFFSET)->load(std::memory_order_acquire);
69     }
70 
CanWrite()71     inline void CanWrite()
72     {
73         // Set to ModType::WRITE, expect no writers and readers
74         constexpr uint32_t expectedModRecord = 0;
75         constexpr uint32_t desiredModRecord = WRITE_MOD_MASK;
76         uint32_t ret = Barriers::AtomicSetPrimitive(objHandle_->GetTaggedObject(),
77             Container::MOD_RECORD_OFFSET, expectedModRecord, desiredModRecord);
78         if (ret != expectedModRecord) {
79             auto error = containers::ContainerError::BusinessError(
80                 thread_, containers::ErrorFlag::CONCURRENT_MODIFICATION_ERROR, "Concurrent modification exception");
81             THROW_NEW_ERROR_AND_RETURN(thread_, error);
82         }
83     }
84 
WriteDone()85     inline void WriteDone()
86     {
87         constexpr uint32_t expectedModRecord = WRITE_MOD_MASK;
88         constexpr uint32_t desiredModRecord = 0u;
89         uint32_t ret = Barriers::AtomicSetPrimitive(objHandle_->GetTaggedObject(),
90             Container::MOD_RECORD_OFFSET, expectedModRecord, desiredModRecord);
91         if (ret != expectedModRecord) {
92             auto error = containers::ContainerError::BusinessError(
93                 thread_, containers::ErrorFlag::CONCURRENT_MODIFICATION_ERROR, "Concurrent modification exception");
94             THROW_NEW_ERROR_AND_RETURN(thread_, error);
95         }
96     }
97 
CanRead()98     inline void CanRead()
99     {
100         while (true) {
101             // Expect no writers
102             expectModRecord_ = GetModRecord();
103             if ((expectModRecord_ & WRITE_MOD_MASK)) {
104                 auto error = containers::ContainerError::BusinessError(
105                     thread_, containers::ErrorFlag::CONCURRENT_MODIFICATION_ERROR, "Concurrent modification exception");
106                 THROW_NEW_ERROR_AND_RETURN(thread_, error);
107             }
108             // Increase readers by 1
109             desiredModRecord_ = expectModRecord_ + 1;
110             auto ret = Barriers::AtomicSetPrimitive(objHandle_->GetTaggedObject(),
111                 Container::MOD_RECORD_OFFSET, expectModRecord_, desiredModRecord_);
112             if (ret == expectModRecord_) {
113                 break;
114             }
115         }
116     }
117 
ReadDone()118     inline void ReadDone()
119     {
120         std::swap(expectModRecord_, desiredModRecord_);
121         while (true) {
122             auto ret = Barriers::AtomicSetPrimitive(objHandle_->GetTaggedObject(),
123                 Container::MOD_RECORD_OFFSET, expectModRecord_, desiredModRecord_);
124             if (ret == expectModRecord_) {
125                 break;
126             }
127             expectModRecord_ = GetModRecord();
128             if ((expectModRecord_ & WRITE_MOD_MASK) ||
129                  expectModRecord_ == 0) {
130                 auto error = containers::ContainerError::BusinessError(
131                     thread_, containers::ErrorFlag::CONCURRENT_MODIFICATION_ERROR, "Concurrent modification exception");
132                 THROW_NEW_ERROR_AND_RETURN(thread_, error);
133             }
134             // Decrease readers by 1
135             desiredModRecord_ = expectModRecord_ - 1;
136         }
137     }
138 
139     JSThread *thread_ {nullptr};
140     JSHandle<JSTaggedValue> objHandle_;
141     SCheckMode checkMode_ { SCheckMode::CHECK };
142     // For readers
143     uint32_t expectModRecord_ {0};
144     uint32_t desiredModRecord_ {0};
145 
146     static_assert(std::is_same_v<Container, JSSharedSet> || std::is_same_v<Container, JSSharedMap> ||
147                   std::is_same_v<Container, JSSharedArray> || std::is_same_v<Container, JSSharedTypedArray> ||
148                   std::is_same_v<Container, JSAPIBitVector>);
149 };
150 } // namespace panda::ecmascript
151 #endif  // ECMASCRIPT_SHARED_OBJECTS_CONCURRENT_API_SCOPE_H