• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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