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 #include <atomic>
17
18 #include "plugins/ets/runtime/ets_object_state_info.h"
19 #include "plugins/ets/runtime/ets_mark_word.h"
20 #include "plugins/ets/runtime/ets_vm.h"
21
22 namespace ark::ets {
23
DeflateInternal()24 bool EtsObjectStateInfo::DeflateInternal()
25 {
26 auto interopIndex = GetInteropIndex();
27 if (interopIndex != INVALID_INTEROP_INDEX) {
28 LOG(DEBUG, RUNTIME) << "Info: " << this << " can not be deflated: contains valid interop index";
29 return false;
30 }
31 auto markWord = obj_->AtomicGetMark(std::memory_order_acquire);
32 ASSERT(markWord.GetState() == EtsMarkWord::STATE_USE_INFO);
33 // Info state means that we will have a ets hash so it should be used in new mark word
34 auto hash = GetEtsHash();
35 // We create new mark word,
36 auto newMarkWord = markWord.DecodeFromHash(hash);
37 ASSERT(newMarkWord.GetHash() == hash);
38 // Atomic with relaxed order reason: ordering constraints are not required
39 // CC-OFFNXT(G.CTL.03) false positive
40 while (!obj_->AtomicSetMark<false>(markWord, newMarkWord, std::memory_order_relaxed)) {
41 }
42 return true;
43 }
44
45 /*static*/
TryAddNewInfo(EtsObject * obj,uint32_t hash,uint32_t index)46 bool EtsObjectStateInfo::TryAddNewInfo(EtsObject *obj, uint32_t hash, uint32_t index)
47 {
48 // Atomic with acquire order reason: ordering constraints for correctness of future non-atomic and relaxed loadings.
49 auto markWord = obj->AtomicGetMark(std::memory_order_acquire);
50 // First we check if state of mark word is still MarkWord::STATE_HASHED.
51 auto state = markWord.GetState();
52 if (state != EtsMarkWord::STATE_HASHED && state != EtsMarkWord::STATE_HAS_INTEROP_INDEX) {
53 // Currect state is not STATE_HASHED so we can not add info for this object.
54 return false;
55 }
56 // Creating of new info and writing `hash` and `index` in it.
57 auto *co = EtsCoroutine::GetCurrent();
58 ASSERT(co != nullptr);
59 auto *table = co->GetPandaVM()->GetEtsObjectStateTable();
60 ASSERT(table != nullptr);
61 auto *info = table->CreateInfo(obj);
62 LOG_IF(info == nullptr, FATAL, RUNTIME) << "Couldn't create new info. Out of memory?";
63 info->SetEtsHash(hash);
64 info->SetInteropIndex(index);
65 // Finally we should try to exchange mark word with new one with MONITOR state.
66 // Creating of new local mark word
67 auto newMarkWord = markWord.DecodeFromInfo(info->GetId());
68 ASSERT(newMarkWord.GetState() == EtsMarkWord::STATE_USE_INFO);
69 ASSERT(newMarkWord.GetInfoId() == info->GetId());
70 // Atomic with release order reason: ordering constraints for correctness of past non-atomic and relaxed storings.
71 if (!obj->AtomicSetMark(markWord, newMarkWord, std::memory_order_release)) {
72 // Other thread have changed object mark word
73 table->FreeInfo(info->GetId());
74 return false;
75 }
76 return true;
77 }
78
79 /*static*/
TryReadEtsHash(const EtsObject * obj,uint32_t * hash)80 bool EtsObjectStateInfo::TryReadEtsHash(const EtsObject *obj, uint32_t *hash)
81 {
82 // Atomic with acquire order reason: ordering constraints for correctness of future non-atomic and relaxed loadings.
83 auto markWord = obj->AtomicGetMark(std::memory_order_acquire);
84 // First we check if state of mark word is still EtsMarkWord::STATE_USE_INFO.
85 // This method can be called only if state of the object is EtsMarkWord::STATE_USE_INFO.
86 // The state can be changed only in GC.
87 ASSERT(markWord.GetState() == EtsMarkWord::STATE_USE_INFO);
88 // We need to get EtsObjectStateInfo from table
89 auto *co = EtsCoroutine::GetCurrent();
90 ASSERT(co != nullptr);
91 auto *table = co->GetPandaVM()->GetEtsObjectStateTable();
92 auto id = markWord.GetInfoId();
93 auto *info = table->LookupInfo(id);
94 LOG_IF(info == nullptr || info->GetEtsObject() != obj, FATAL, RUNTIME)
95 << "TryReadEtsHash: info was concurrently deleted from table";
96 // we can read hash and return it.
97 *hash = info->GetEtsHash();
98 return true;
99 }
100
101 /*static*/
TryReadInteropIndex(const EtsObject * obj,uint32_t * index)102 bool EtsObjectStateInfo::TryReadInteropIndex(const EtsObject *obj, uint32_t *index)
103 {
104 // Atomic with acquire order reason: ordering constraints for correctness of future non-atomic and relaxed loadings.
105 auto markWord = obj->AtomicGetMark(std::memory_order_acquire);
106 // This method can be called only if state of the object is EtsMarkWord::STATE_USE_INFO.
107 // The state can be changed only in GC.
108 ASSERT(markWord.GetState() == EtsMarkWord::STATE_USE_INFO);
109 // We need to get EtsObjectStateInfo from table
110 auto *co = EtsCoroutine::GetCurrent();
111 ASSERT(co != nullptr);
112 auto *table = co->GetPandaVM()->GetEtsObjectStateTable();
113 auto id = markWord.GetInfoId();
114 auto *info = table->LookupInfo(id);
115 LOG_IF(info == nullptr || info->GetEtsObject() != obj, FATAL, RUNTIME)
116 << "TryReadInteropIndex: info was concurrently deleted from table";
117 // we can read hash, check if it's valid and return it.
118 auto currentIndex = info->GetInteropIndex();
119 LOG_IF(currentIndex == INVALID_INTEROP_INDEX, FATAL, RUNTIME) << "Try read invalid interop index";
120 *index = currentIndex;
121 return true;
122 }
123
124 /*static*/
TryDropInteropIndex(EtsObject * obj)125 bool EtsObjectStateInfo::TryDropInteropIndex(EtsObject *obj)
126 {
127 // Atomic with acquire order reason: ordering constraints for correctness of future non-atomic and relaxed loadings.
128 auto markWord = obj->AtomicGetMark(std::memory_order_acquire);
129 // This method can be called only if state of the object is EtsMarkWord::STATE_USE_INFO.
130 // The state can be changed only in GC.
131 ASSERT(markWord.GetState() == EtsMarkWord::STATE_USE_INFO);
132 auto *co = EtsCoroutine::GetCurrent();
133 ASSERT(co != nullptr);
134 auto *table = co->GetPandaVM()->GetEtsObjectStateTable();
135 ASSERT(table != nullptr);
136 auto id = markWord.GetInfoId();
137 auto *info = table->LookupInfo(id);
138 LOG_IF(info == nullptr || info->GetEtsObject() != obj, FATAL, RUNTIME)
139 << "TryDropInteropIndex: info was concurrently deleted from table";
140 // Droping here means that we set INVALID_INDEX
141 info->SetInteropIndex(EtsObjectStateInfo::INVALID_INTEROP_INDEX);
142 return true;
143 }
144
145 /*static*/
TryResetInteropIndex(EtsObject * obj,uint32_t index)146 bool EtsObjectStateInfo::TryResetInteropIndex(EtsObject *obj, uint32_t index)
147 {
148 auto markWord = obj->AtomicGetMark(std::memory_order_acquire);
149 // This method can be called only if state of the object is EtsMarkWord::STATE_USE_INFO.
150 // The state can be changed only in GC.
151 ASSERT(markWord.GetState() == EtsMarkWord::STATE_USE_INFO);
152 // We need to find current interop index
153 auto *co = EtsCoroutine::GetCurrent();
154 ASSERT(co != nullptr);
155 auto *table = co->GetPandaVM()->GetEtsObjectStateTable();
156 auto id = markWord.GetInfoId();
157 auto *info = table->LookupInfo(id);
158 LOG_IF(info == nullptr || info->GetEtsObject() != obj, FATAL, RUNTIME)
159 << "TryResetInteropIndex: info was concurrently deleted from table";
160 info->SetInteropIndex(index);
161 return true;
162 }
163
TryCheckIfInteropIndexIsValid(const EtsObject * obj,bool * isValid)164 bool EtsObjectStateInfo::TryCheckIfInteropIndexIsValid(const EtsObject *obj, bool *isValid)
165 {
166 auto markWord = obj->AtomicGetMark(std::memory_order_acquire);
167 // This method can be called only if state of the object is EtsMarkWord::STATE_USE_INFO.
168 // The state can be changed only in GC.
169 ASSERT(markWord.GetState() == EtsMarkWord::STATE_USE_INFO);
170 // We need to find current interop index
171 auto *co = EtsCoroutine::GetCurrent();
172 ASSERT(co != nullptr);
173 auto *table = co->GetPandaVM()->GetEtsObjectStateTable();
174 auto id = markWord.GetInfoId();
175 auto *info = table->LookupInfo(id);
176 if (info == nullptr || info->GetEtsObject() != obj) {
177 LOG(DEBUG, RUNTIME) << "TryCheckIfInteropIndexIsValid: info was concurrently deleted from table";
178 return true;
179 }
180 auto currentIndex = info->GetInteropIndex();
181 *isValid = currentIndex != INVALID_INTEROP_INDEX;
182 return true;
183 }
184
185 } // namespace ark::ets
186