1 /** 2 * Copyright (c) 2024-2025 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 PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_PROXY_SHARED_REFERENCE_FLAGS_H 17 #define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_PROXY_SHARED_REFERENCE_FLAGS_H 18 19 #include "plugins/ets/runtime/types/ets_object.h" 20 21 namespace ark::ets::interop::js::ets_proxy { 22 23 // |------------------------------------------------------------| 24 // | SharedReference flag bits represenatation:32 | 25 // |------------------------------------------------------------| 26 // | NextIndex:28|12 | ... | MarkBit:1 | JSbit:1 | ETSbit:1 | 27 // |------------------------------------------------------------| 28 class SharedReferenceFlags { 29 public: 30 using ValueType = ObjectPointerType; 31 using IndexType = decltype(std::declval<EtsObject>().GetInteropIndex()); 32 static_assert(sizeof(IndexType) <= sizeof(ValueType)); 33 34 enum class Bit : ValueType { 35 ETS = 1U << 0U, 36 JS = 1U << 1U, 37 MARK = 1U << 2U, 38 }; 39 IsEmpty()40 bool IsEmpty() const 41 { 42 return GetRawFlags() == EMPTY_FLAGS; 43 } 44 ClearFlags()45 void ClearFlags() 46 { 47 SetRawFlags(EMPTY_FLAGS); 48 } 49 50 template <SharedReferenceFlags::Bit BIT> SetBit()51 bool SetBit() 52 { 53 static constexpr auto BIT_VALUE = static_cast<ValueType>(BIT); 54 return SetFlagCAS<BIT_VALUE>(); 55 } 56 57 template <SharedReferenceFlags::Bit BIT> HasBit()58 bool HasBit() const 59 { 60 static constexpr auto BIT_VALUE = static_cast<ValueType>(BIT); 61 return (GetRawFlags() & BIT_VALUE) != 0U; 62 } 63 Unmark()64 void Unmark() 65 { 66 UnsetFlagCAS<static_cast<ValueType>(Bit::MARK)>(); 67 } 68 SetNextIndex(IndexType index)69 void SetNextIndex(IndexType index) 70 { 71 static constexpr ValueType NO_NEXT_INDEX_MASK = (1U << NEXT_INDEX_SHIFT) - 1U; 72 ValueType indexMask = index << NEXT_INDEX_SHIFT; 73 ValueType oldFlags = GetRawFlags(); 74 while (!flags_.compare_exchange_weak(oldFlags, (oldFlags & NO_NEXT_INDEX_MASK) | indexMask, 75 std::memory_order_seq_cst)) { 76 } 77 } 78 GetNextIndex()79 IndexType GetNextIndex() const 80 { 81 return static_cast<IndexType>(GetRawFlags() >> NEXT_INDEX_SHIFT); 82 } 83 84 friend std::ostream &operator<<(std::ostream &out, const SharedReferenceFlags &flags) 85 { 86 out << "idx:" << flags.GetNextIndex() << " mark:" << flags.HasBit<SharedReferenceFlags::Bit::MARK>() 87 << " JS:" << flags.HasBit<SharedReferenceFlags::Bit::JS>() 88 << " ETS:" << flags.HasBit<SharedReferenceFlags::Bit::ETS>(); 89 return out; 90 } 91 92 private: 93 static constexpr ValueType EMPTY_FLAGS = 0U; 94 static constexpr ValueType NEXT_INDEX_SHIFT = 95 sizeof(ValueType) * BITS_PER_BYTE - MarkWord::MarkWordRepresentation::HASH_SIZE; 96 static_assert(static_cast<ValueType>(Bit::MARK) < (1U << NEXT_INDEX_SHIFT)); 97 GetRawFlags()98 ALWAYS_INLINE ValueType GetRawFlags() const 99 { 100 // Atomic with acquire order reason: data race with flags_ with dependecies on reads after the load which 101 // should become visible 102 return flags_.load(std::memory_order_acquire); 103 } 104 SetRawFlags(ValueType flags)105 ALWAYS_INLINE void SetRawFlags(ValueType flags) 106 { 107 // Atomic with release order reason: other thread should see last value of flags 108 flags_.store(flags, std::memory_order_release); 109 } 110 111 template <ValueType FLAG> SetFlagCAS()112 ALWAYS_INLINE bool SetFlagCAS() 113 { 114 static_assert(Popcount(FLAG) == 1); 115 ValueType oldFlags; 116 do { 117 oldFlags = GetRawFlags(); 118 if ((oldFlags & FLAG) != 0) { 119 return false; 120 } 121 } while (!flags_.compare_exchange_weak(oldFlags, oldFlags | FLAG, std::memory_order_seq_cst)); 122 return true; 123 } 124 125 template <ValueType FLAG> UnsetFlagCAS()126 ALWAYS_INLINE void UnsetFlagCAS() 127 { 128 static_assert(Popcount(FLAG) == 1); 129 static constexpr ValueType MASK = ~FLAG; 130 ValueType oldFlags; 131 do { 132 oldFlags = GetRawFlags(); 133 if ((oldFlags & FLAG) == 0) { 134 return; 135 } 136 } while (!flags_.compare_exchange_weak(oldFlags, oldFlags & MASK, std::memory_order_seq_cst)); 137 } 138 139 std::atomic<ValueType> flags_ {}; 140 }; 141 142 } // namespace ark::ets::interop::js::ets_proxy 143 144 #endif // PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_PROXY_SHARED_REFERENCE_FLAGS_H 145