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