1 /**
2 * Copyright (c) 2021-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 #include "plugins/ets/runtime/types/ets_object.h"
17 #include <atomic>
18 #include <cstdint>
19 #include "plugins/ets/runtime/ets_mark_word.h"
20 #include "plugins/ets/runtime/ets_object_state_info.h"
21 #include "plugins/ets/runtime/ets_coroutine.h"
22 #include "runtime/handle_base-inl.h"
23
24 namespace ark::ets {
25
26 /* static */
Create(EtsCoroutine * etsCoroutine,EtsClass * klass)27 EtsObject *EtsObject::Create(EtsCoroutine *etsCoroutine, EtsClass *klass)
28 {
29 ASSERT_HAVE_ACCESS_TO_MANAGED_OBJECTS();
30 ASSERT(klass != nullptr);
31 return static_cast<EtsObject *>(ObjectHeader::Create(etsCoroutine, klass->GetRuntimeClass()));
32 }
33
34 /* static */
Create(EtsClass * klass)35 EtsObject *EtsObject::Create(EtsClass *klass)
36 {
37 return Create(EtsCoroutine::GetCurrent(), klass);
38 }
39
40 /* static */
CreateNonMovable(EtsClass * klass)41 EtsObject *EtsObject::CreateNonMovable(EtsClass *klass)
42 {
43 ASSERT(klass != nullptr);
44 return static_cast<EtsObject *>(ObjectHeader::CreateNonMovable(klass->GetRuntimeClass()));
45 }
46
GetHashCode()47 uint32_t EtsObject::GetHashCode()
48 {
49 uint32_t hash = 0;
50 while (!TryGetHashCode(&hash)) {
51 }
52 return hash;
53 }
54
GetInteropIndex() const55 uint32_t EtsObject::GetInteropIndex() const
56 {
57 uint32_t index = 0;
58 while (!TryGetInteropIndex(&index)) {
59 }
60 return index;
61 }
62
SetInteropIndex(uint32_t index)63 void EtsObject::SetInteropIndex(uint32_t index)
64 {
65 while (!TrySetInteropIndex(index)) {
66 }
67 }
68
DropInteropIndex()69 void EtsObject::DropInteropIndex()
70 {
71 while (!TryDropInteropIndex()) {
72 }
73 }
74
TryGetHashCode(uint32_t * hash)75 bool EtsObject::TryGetHashCode(uint32_t *hash)
76 {
77 ASSERT(hash != nullptr);
78 // Atomic with relaxed order reason: on this level of execution where we use only atomic mark word for testing the
79 // state and its changing if it's needed. When we EtsObjectStateInfo static methods, we will reread value of mark
80 // word with acquire order to mark future relaxed read from EtsObjectStateInfo visible for us.
81 EtsMarkWord currentMarkWord = AtomicGetMark(std::memory_order_relaxed);
82 switch (currentMarkWord.GetState()) {
83 case EtsMarkWord::STATE_HASHED: {
84 *hash = currentMarkWord.GetHash();
85 return true;
86 }
87 case EtsMarkWord::STATE_HAS_INTEROP_INDEX: {
88 auto localHash = GenerateHashCode();
89 auto interopHash = currentMarkWord.GetInteropIndex();
90 if (!EtsObjectStateInfo::TryAddNewInfo(this, localHash, interopHash)) {
91 return false;
92 }
93 *hash = localHash;
94 return true;
95 }
96 case EtsMarkWord::STATE_UNLOCKED: {
97 // In STATE_UNLOCKED case we should just set mark word in HASHED_STATE with new hash.
98 auto newMarkWord = currentMarkWord.DecodeFromHash(GenerateHashCode());
99 // Atomic with relaxed order reason: AtomicSetMark is CAS operation so if 'currentMarkWord' is not actual
100 // this commend will find actual one and next load will read it. In this execution branch we have no relaxed
101 // or non-atomic store, so release ordering is not needed.
102 AtomicSetMark(currentMarkWord, newMarkWord, std::memory_order_relaxed);
103 return false;
104 }
105 case EtsMarkWord::STATE_USE_INFO: {
106 // In STATE_USE_INFO case we should read hash from EtsObjectState table.
107 return EtsObjectStateInfo::TryReadEtsHash(this, hash);
108 }
109 default: {
110 LOG(FATAL, RUNTIME) << "Error on TryGetHashCode(): invalid state " << currentMarkWord.GetState();
111 return true; // go out of loop
112 }
113 }
114 }
115
TryGetInteropIndex(uint32_t * index) const116 bool EtsObject::TryGetInteropIndex(uint32_t *index) const
117 {
118 ASSERT(index != nullptr);
119 // Atomic with relaxed order reason: on this level of execution where we use only atomic mark word for testing the
120 // state and its changing if it's needed. When we use EtsObjectStateInfo static methods, we will reread value of
121 // mark word with acquire order to mark future relaxed read from EtsObjectStateInfo visible for us.
122 EtsMarkWord currentMarkWord = AtomicGetMark(std::memory_order_relaxed);
123 switch (currentMarkWord.GetState()) {
124 case EtsMarkWord::STATE_HAS_INTEROP_INDEX: {
125 *index = currentMarkWord.GetInteropIndex();
126 return true;
127 }
128 case EtsMarkWord::STATE_USE_INFO: {
129 // In STATE_USE_INFO case we should read hash from EtsObjectState table.
130 return EtsObjectStateInfo::TryReadInteropIndex(this, index);
131 }
132 default: {
133 LOG(FATAL, RUNTIME) << "Error on TryGetInteropCode(): invalid state " << currentMarkWord.GetState();
134 return true; // go out of loop
135 }
136 }
137 }
138
TrySetInteropIndex(uint32_t index)139 bool EtsObject::TrySetInteropIndex(uint32_t index)
140 {
141 // Atomic with relaxed order reason: on this level of execution where we use only atomic mark word for testing the
142 // state and its changing if it's needed. When we use EtsObjectStateInfo static methods, we will reread value of
143 // mark word with acquire order to mark future relaxed read from EtsObjectStateInfo visible for us.
144 EtsMarkWord currentMarkWord = AtomicGetMark(std::memory_order_relaxed);
145 switch (currentMarkWord.GetState()) {
146 case EtsMarkWord::STATE_UNLOCKED:
147 [[fallthrough]];
148 case EtsMarkWord::STATE_HAS_INTEROP_INDEX: {
149 auto newMarkWord = currentMarkWord.DecodeFromInteropIndex(index);
150 // Atomic with relaxed order reason: AtomicSetMark is CAS operation so if 'currentMarkWord' is not actual
151 // this commend will find actual one and next load will read it. In this execution branch we have no relaxed
152 // or non-atomic store, so release ordering is not needed.
153 return AtomicSetMark(currentMarkWord, newMarkWord, std::memory_order_relaxed);
154 }
155 case EtsMarkWord::STATE_HASHED: {
156 auto hash = currentMarkWord.GetHash();
157 return EtsObjectStateInfo::TryAddNewInfo(this, hash, index);
158 }
159 case EtsMarkWord::STATE_USE_INFO: {
160 // we should check if interop index is invalid. If it's true, we can set new one, otherwise we should fail.
161 return EtsObjectStateInfo::TryResetInteropIndex(this, index);
162 }
163 default: {
164 LOG(FATAL, RUNTIME) << "Error on TrySetInteropCode(): invalid state " << currentMarkWord.GetState();
165 return true; // go out of loop
166 }
167 }
168 }
169
TryDropInteropIndex()170 bool EtsObject::TryDropInteropIndex()
171 {
172 // Atomic with relaxed order reason: on this level of execution where we use only atomic mark word for testing the
173 // state and its changing if it's needed. When we EtsObjectStateInfo static methods, we will reread value of mark
174 // word with acquire order to mark future relaxed read from EtsObjectStateInfo visible for us.
175 EtsMarkWord currentMarkWord = AtomicGetMark(std::memory_order_relaxed);
176 switch (currentMarkWord.GetState()) {
177 case EtsMarkWord::STATE_HAS_INTEROP_INDEX: {
178 auto newMarkWord = currentMarkWord.DecodeFromUnlocked();
179 return AtomicSetMark(currentMarkWord, newMarkWord, std::memory_order_relaxed);
180 }
181 case EtsMarkWord::STATE_USE_INFO: {
182 return EtsObjectStateInfo::TryDropInteropIndex(this);
183 }
184 default: {
185 LOG(FATAL, RUNTIME) << "Error on TryDropInteropCode(): invalid state " << currentMarkWord.GetState();
186 return true; // go out of loop
187 }
188 }
189 }
190
TryCheckIfHasInteropIndex(bool * hasInteropIndex) const191 bool EtsObject::TryCheckIfHasInteropIndex(bool *hasInteropIndex) const
192 {
193 // Atomic with relaxed order reason: on this level of execution where we use only atomic mark word for testing the
194 // state and its changing if it's needed. When we EtsObjectStateInfo static methods, we will reread value of mark
195 // word with acquire order to mark future relaxed read from EtsObjectStateInfo visible for us.
196 EtsMarkWord currentMarkWord = AtomicGetMark(std::memory_order_relaxed);
197 switch (currentMarkWord.GetState()) {
198 case EtsMarkWord::STATE_USE_INFO: {
199 return EtsObjectStateInfo::TryCheckIfInteropIndexIsValid(this, hasInteropIndex);
200 }
201 case EtsMarkWord::STATE_HAS_INTEROP_INDEX: {
202 *hasInteropIndex = true;
203 return true;
204 }
205 case EtsMarkWord::STATE_HASHED: {
206 [[fallthrough]];
207 }
208 case EtsMarkWord::UNUSED_STATE_STATE_LIGHT_LOCKED: {
209 [[fallthrough]];
210 }
211 case EtsMarkWord::STATE_UNLOCKED: {
212 *hasInteropIndex = false;
213 return true;
214 }
215 default:
216 LOG(FATAL, RUNTIME) << "Invalid state " << currentMarkWord.GetState();
217 return true;
218 }
219 }
220
221 /*static*/
GenerateHashCode()222 uint32_t EtsObject::GenerateHashCode()
223 {
224 auto hash = ObjectHeader::GenerateHashCode();
225 return hash >> EtsMarkWord::INTEROP_INDEX_FLAG_SIZE;
226 }
227
228 } // namespace ark::ets
229