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 #include "engine_value_manager.h"
16
17 #include <charconv>
18 #include <mutex>
19
20 #include <core/property/intf_property_api.h>
21 #include <core/property/scoped_handle.h>
22
23 #include <meta/api/engine/util.h>
24 #include <meta/api/make_callback.h>
25 #include <meta/ext/event_util.h>
26 #include <meta/interface/engine/intf_engine_data.h>
27 #include <meta/interface/intf_task_queue_registry.h>
28
29 META_BEGIN_NAMESPACE()
30
31 namespace Internal {
32
33 template<typename Type>
ReadValueFromEngine(const EnginePropertyParams & params)34 static Type ReadValueFromEngine(const EnginePropertyParams& params)
35 {
36 Type res {};
37 if (CORE_NS::ScopedHandle<const Type> guard { params.handle.Handle() }) {
38 res = *(const Type*)((uintptr_t) & *guard + params.Offset());
39 }
40 return res;
41 }
42
~EngineValueManager()43 EngineValueManager::~EngineValueManager()
44 {
45 ITaskQueue::WeakPtr q;
46 ITaskQueue::Token token {};
47 {
48 std::unique_lock lock { mutex_ };
49 q = queue_;
50 token = task_token_;
51 }
52 if (token) {
53 if (auto queue = q.lock()) {
54 queue->CancelTask(token);
55 }
56 }
57 }
58
SetNotificationQueue(const ITaskQueue::WeakPtr & q)59 void EngineValueManager::SetNotificationQueue(const ITaskQueue::WeakPtr& q)
60 {
61 queue_ = q;
62 }
GetCompatibleValueInternal(IEngineValue::Ptr p,EnginePropertyParams params)63 static IEngineValueInternal::Ptr GetCompatibleValueInternal(IEngineValue::Ptr p, EnginePropertyParams params)
64 {
65 IEngineValueInternal::Ptr ret;
66 if (auto i = interface_pointer_cast<IEngineValueInternal>(p)) {
67 if (auto acc = i->GetInternalAccess()) {
68 if (acc->IsCompatible(params.property.type)) {
69 ret = i;
70 }
71 }
72 }
73 return ret;
74 }
AddValue(EnginePropertyParams p,EngineValueOptions options)75 IEngineValue::Ptr EngineValueManager::AddValue(EnginePropertyParams p, EngineValueOptions options)
76 {
77 BASE_NS::string name { p.property.name };
78 if (!options.namePrefix.empty()) {
79 if (name.empty()) {
80 name = options.namePrefix;
81 } else {
82 name = options.namePrefix + "." + name;
83 }
84 }
85 if (auto access = META_NS::GetObjectRegistry().GetEngineData().GetInternalValueAccess(p.property.type)) {
86 IEngineValue::Ptr v;
87 if (auto it = values_.find(name); it != values_.end()) {
88 if (auto acc = GetCompatibleValueInternal(it->second.value, p)) {
89 InterfaceUniqueLock valueLock { it->second.value };
90 acc->SetPropertyParams(p);
91 v = it->second.value;
92 } else {
93 values_.erase(it);
94 }
95 }
96 if (!v) {
97 v = IEngineValue::Ptr(new EngineValue(name, access, p));
98 v->Sync(META_NS::EngineSyncDirection::FROM_ENGINE);
99 values_[name] = ValueInfo { v };
100 }
101 if (options.values) {
102 options.values->push_back(v);
103 }
104 return v;
105 }
106 CORE_LOG_W("No engine internal access type registered for '%s' (required by '%s')",
107 BASE_NS::string(p.property.type.name).c_str(), name.c_str());
108 return nullptr;
109 }
110
ConstructValues(EnginePropertyHandle handle,EngineValueOptions options)111 bool EngineValueManager::ConstructValues(EnginePropertyHandle handle, EngineValueOptions options)
112 {
113 if (auto rh = handle.Handle()) {
114 if (auto api = rh->Owner()) {
115 std::unique_lock lock { mutex_ };
116 for (auto& prop : api->MetaData()) {
117 EnginePropertyParams params(handle, prop, 0);
118 params.pushValueToEngineDirectly = options.pushValuesDirectlyToEngine;
119 AddValue(params, options);
120 }
121 return true;
122 }
123 }
124 return false;
125 }
126
ConstructValues(IValue::Ptr value,EngineValueOptions options)127 bool EngineValueManager::ConstructValues(IValue::Ptr value, EngineValueOptions options)
128 {
129 auto prefix = options.namePrefix;
130 if (prefix.empty()) {
131 if (auto i = interface_cast<IEngineValue>(value)) {
132 prefix = i->GetName();
133 }
134 }
135 if (value->IsCompatible(UidFromType<CORE_NS::IPropertyHandle*>())) {
136 if (auto p = GetValue<CORE_NS::IPropertyHandle*>(value->GetValue())) {
137 EnginePropertyHandle h { nullptr, {}, value };
138 return ConstructValues(
139 h, EngineValueOptions { prefix, options.values, options.pushValuesDirectlyToEngine });
140 }
141 }
142 if (auto i = interface_cast<IEngineValueInternal>(value)) {
143 auto params = i->GetPropertyParams();
144 for (auto&& p : params.property.metaData.memberProperties) {
145 EnginePropertyParams propParams { params, p };
146 propParams.pushValueToEngineDirectly = options.pushValuesDirectlyToEngine;
147 AddValue(propParams, EngineValueOptions { prefix, options.values });
148 }
149 }
150 return true;
151 }
152
ConstructValue(EnginePropertyParams property,EngineValueOptions options)153 bool EngineValueManager::ConstructValue(EnginePropertyParams property, EngineValueOptions options)
154 {
155 std::unique_lock lock { mutex_ };
156 AddValue(property, options);
157 return true;
158 }
159
ConstructValueImplArraySubs(EnginePropertyParams params,BASE_NS::string pathTaken,BASE_NS::string_view path,EngineValueOptions options)160 bool EngineValueManager::ConstructValueImplArraySubs(
161 EnginePropertyParams params, BASE_NS::string pathTaken, BASE_NS::string_view path, EngineValueOptions options)
162 {
163 if (!params.property.metaData.containerMethods) {
164 CORE_LOG_W("Cannot index non-array in property path");
165 return false;
166 }
167 auto endPos = path.find_first_of(']');
168 if (endPos == BASE_NS::string_view::npos) {
169 CORE_LOG_W("Invalid property path");
170 return false;
171 }
172 size_t index {};
173 std::from_chars_result res = std::from_chars(path.data() + 1, path.data() + endPos, index);
174 if (res.ec != std::errc {} || res.ptr != path.data() + endPos) {
175 CORE_LOG_W("Invalid property path array index");
176 return false;
177 }
178 EnginePropertyParams elementParams;
179 auto cmethods = params.property.metaData.containerMethods;
180 if (params.property.type.isArray) {
181 if (index >= params.property.count) {
182 CORE_LOG_W("Invalid property path array index, out of range");
183 return false;
184 }
185 elementParams = EnginePropertyParams(params, cmethods->property);
186 elementParams.baseOffset += elementParams.property.size * index;
187 } else {
188 if (params.containerMethods) {
189 CORE_LOG_W("Can only refer to one subscription over container");
190 return false;
191 }
192 auto handle = params.handle.Handle();
193 if (!handle) {
194 CORE_LOG_W("Invalid property handle");
195 return false;
196 }
197 auto data = (uintptr_t)handle->RLock();
198 if (!data) {
199 CORE_LOG_W("Invalid property data");
200 return false;
201 }
202 if (index >= cmethods->size(data + params.Offset())) {
203 handle->RUnlock();
204 CORE_LOG_W("Invalid property path container index, out of range");
205 return false;
206 }
207 handle->RUnlock();
208 elementParams = EnginePropertyParams(params, cmethods, index);
209 }
210 pathTaken += path.substr(0, endPos + 1);
211 path.remove_prefix(endPos + 1);
212 if (path.empty()) {
213 return ConstructValueImplAdd(elementParams, BASE_NS::move(pathTaken), options) != nullptr;
214 }
215 path.remove_prefix(1);
216 return ConstructValueImpl(elementParams, BASE_NS::move(pathTaken), path, options);
217 }
218
ConstructValueImplAdd(EnginePropertyParams params,BASE_NS::string pathTaken,EngineValueOptions options)219 IEngineValue::Ptr EngineValueManager::ConstructValueImplAdd(
220 EnginePropertyParams params, BASE_NS::string pathTaken, EngineValueOptions options)
221 {
222 if (!options.namePrefix.empty() && !pathTaken.empty()) {
223 options.namePrefix += ".";
224 }
225 options.namePrefix += pathTaken;
226
227 std::unique_lock lock { mutex_ };
228 return AddValue(params, options);
229 }
230
ConstructValueImpl(EnginePropertyParams params,BASE_NS::string pathTaken,BASE_NS::string_view path,EngineValueOptions options)231 bool EngineValueManager::ConstructValueImpl(
232 EnginePropertyParams params, BASE_NS::string pathTaken, BASE_NS::string_view path, EngineValueOptions options)
233 {
234 BASE_NS::string previousPath = pathTaken;
235 if (path == params.property.name) {
236 return ConstructValueImplAdd(params, BASE_NS::move(pathTaken), options) != nullptr;
237 }
238 if (!params.property.name.empty()) {
239 if (!pathTaken.empty()) {
240 pathTaken += ".";
241 }
242 pathTaken += params.property.name;
243 path.remove_prefix(params.property.name.size());
244
245 if (path.starts_with('[')) {
246 return ConstructValueImplArraySubs(params, BASE_NS::move(pathTaken), path, options);
247 }
248 path.remove_prefix(1);
249 }
250
251 if (params.property.type == PROPERTYTYPE(CORE_NS::IPropertyHandle*)) {
252 if (CORE_NS::IPropertyHandle* phandle = ReadValueFromEngine<CORE_NS::IPropertyHandle*>(params)) {
253 if (auto pv = ConstructValueImplAdd(params, previousPath, options)) {
254 EnginePropertyHandle h { nullptr, {}, pv };
255 return ConstructValueImpl(h, BASE_NS::move(pathTaken), path, options);
256 }
257 CORE_LOG_W("Failed to construct parent engine value");
258 }
259 } else {
260 auto root = path.substr(0, path.find_first_of('.'));
261 for (auto&& p : params.property.metaData.memberProperties) {
262 if (p.name == root) {
263 EnginePropertyParams propParams(params, p);
264 return ConstructValueImpl(propParams, BASE_NS::move(pathTaken), path, options);
265 }
266 }
267 }
268 return false;
269 }
270
ConstructValueImpl(EnginePropertyHandle handle,BASE_NS::string pathTaken,BASE_NS::string_view path,EngineValueOptions options)271 bool EngineValueManager::ConstructValueImpl(
272 EnginePropertyHandle handle, BASE_NS::string pathTaken, BASE_NS::string_view path, EngineValueOptions options)
273 {
274 if (auto rh = handle.Handle()) {
275 if (auto api = rh->Owner()) {
276 auto root = path.substr(0, path.find_first_of(".["));
277 if (!root.empty()) {
278 for (auto& prop : api->MetaData()) {
279 if (prop.name == root) {
280 EnginePropertyParams params(handle, prop, 0);
281 params.pushValueToEngineDirectly = options.pushValuesDirectlyToEngine;
282 return ConstructValueImpl(params, BASE_NS::move(pathTaken), path, options);
283 }
284 }
285 }
286 }
287 }
288 return false;
289 }
290
ConstructValue(EnginePropertyHandle handle,BASE_NS::string_view path,EngineValueOptions options)291 bool EngineValueManager::ConstructValue(
292 EnginePropertyHandle handle, BASE_NS::string_view path, EngineValueOptions options)
293 {
294 return ConstructValueImpl(handle, "", BASE_NS::string(path), options);
295 }
296
RemoveValue(BASE_NS::string_view name)297 bool EngineValueManager::RemoveValue(BASE_NS::string_view name)
298 {
299 std::unique_lock lock { mutex_ };
300 auto it = values_.find(name);
301 bool ret = it != values_.end();
302 if (ret) {
303 values_.erase(it);
304 }
305 return ret;
306 }
307
RemoveAll()308 void EngineValueManager::RemoveAll()
309 {
310 std::unique_lock lock { mutex_ };
311 values_.clear();
312 }
313
ConstructProperty(BASE_NS::string_view name) const314 IProperty::Ptr EngineValueManager::ConstructProperty(BASE_NS::string_view name) const
315 {
316 IEngineValue::Ptr value;
317 {
318 std::shared_lock lock { mutex_ };
319 auto it = values_.find(name);
320 if (it != values_.end()) {
321 value = it->second.value;
322 }
323 }
324 return PropertyFromEngineValue(name, value);
325 }
326
ConstructAllProperties() const327 BASE_NS::vector<IProperty::Ptr> EngineValueManager::ConstructAllProperties() const
328 {
329 BASE_NS::vector<IProperty::Ptr> ret;
330 std::shared_lock lock { mutex_ };
331 for (auto&& v : values_) {
332 ret.push_back(PropertyFromEngineValue(v.first, v.second.value));
333 }
334 return ret;
335 }
336
GetEngineValue(BASE_NS::string_view name) const337 IEngineValue::Ptr EngineValueManager::GetEngineValue(BASE_NS::string_view name) const
338 {
339 std::shared_lock lock { mutex_ };
340 auto it = values_.find(name);
341 return it != values_.end() ? it->second.value : nullptr;
342 }
343
GetAllEngineValues() const344 BASE_NS::vector<IEngineValue::Ptr> EngineValueManager::GetAllEngineValues() const
345 {
346 BASE_NS::vector<IEngineValue::Ptr> ret;
347 std::shared_lock lock { mutex_ };
348 for (auto&& v : values_) {
349 ret.push_back(v.second.value);
350 }
351 return ret;
352 }
353
NotifySyncs()354 void EngineValueManager::NotifySyncs()
355 {
356 BASE_NS::vector<IEngineValue::Ptr> values;
357 {
358 std::unique_lock lock { mutex_ };
359 for (auto&& v : values_) {
360 if (auto i = interface_cast<IEngineValueInternal>(v.second.value); i && i->ResetPendingNotify()) {
361 values.push_back(v.second.value);
362 }
363 }
364 task_token_ = {};
365 }
366 for (auto&& v : values) {
367 if (auto noti = interface_cast<INotifyOnChange>(v)) {
368 Invoke<IOnChanged>(noti->OnChanged());
369 }
370 }
371 }
372
SyncValue(const IEngineValue::Ptr & value,EngineSyncDirection dir)373 static AnyReturnValue SyncValue(const IEngineValue::Ptr& value, EngineSyncDirection dir)
374 {
375 InterfaceUniqueLock valueLock { value };
376 if (auto i = interface_pointer_cast<IEngineValueInternal>(value)) {
377 // if this engine value depends on another one, sync the dependency first
378 if (auto pv = interface_pointer_cast<IEngineValue>(i->GetPropertyParams().handle.parentValue)) {
379 SyncValue(pv, dir);
380 }
381 }
382 return value->Sync(dir);
383 }
384
Sync(EngineSyncDirection dir)385 bool EngineValueManager::Sync(EngineSyncDirection dir)
386 {
387 bool ret = true;
388 bool notify = false;
389 {
390 std::unique_lock lock { mutex_ };
391 for (auto&& v : values_) {
392 auto res = SyncValue(v.second.value, dir);
393 if (res && res != AnyReturn::NOTHING_TO_DO) {
394 notify = true;
395 }
396 ret = ret && res;
397 }
398 notify = notify && !task_token_;
399 if (notify && !queue_.expired()) {
400 if (auto queue = queue_.lock()) {
401 task_token_ = queue->AddTask(MakeCallback<ITaskQueueTask>([this] {
402 NotifySyncs();
403 return false;
404 }));
405 } else {
406 CORE_LOG_E("Invalid task queue id");
407 }
408 notify = false;
409 }
410 }
411 if (notify) {
412 NotifySyncs();
413 }
414 return ret;
415 }
416 } // namespace Internal
417
418 META_END_NAMESPACE()
419