18bf80f4bSopenharmony_ci/*
28bf80f4bSopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd.
38bf80f4bSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
48bf80f4bSopenharmony_ci * you may not use this file except in compliance with the License.
58bf80f4bSopenharmony_ci * You may obtain a copy of the License at
68bf80f4bSopenharmony_ci *
78bf80f4bSopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
88bf80f4bSopenharmony_ci *
98bf80f4bSopenharmony_ci * Unless required by applicable law or agreed to in writing, software
108bf80f4bSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
118bf80f4bSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
128bf80f4bSopenharmony_ci * See the License for the specific language governing permissions and
138bf80f4bSopenharmony_ci * limitations under the License.
148bf80f4bSopenharmony_ci */
158bf80f4bSopenharmony_ci#include "startable_object_controller.h"
168bf80f4bSopenharmony_ci
178bf80f4bSopenharmony_ci#include <meta/api/future.h>
188bf80f4bSopenharmony_ci#include <meta/api/iteration.h>
198bf80f4bSopenharmony_ci#include <meta/api/make_callback.h>
208bf80f4bSopenharmony_ci#include <meta/api/task.h>
218bf80f4bSopenharmony_ci#include <meta/api/util.h>
228bf80f4bSopenharmony_ci#include <meta/interface/intf_content.h>
238bf80f4bSopenharmony_ci#include <meta/interface/intf_task_queue_registry.h>
248bf80f4bSopenharmony_ci#include <meta/interface/property/property_events.h>
258bf80f4bSopenharmony_ci
268bf80f4bSopenharmony_ciMETA_BEGIN_NAMESPACE()
278bf80f4bSopenharmony_ci
288bf80f4bSopenharmony_cibool StartableObjectController::Build(const IMetadata::Ptr& data)
298bf80f4bSopenharmony_ci{
308bf80f4bSopenharmony_ci    auto& reg = GetObjectRegistry();
318bf80f4bSopenharmony_ci    observer_ = reg.Create<IObjectHierarchyObserver>(ClassId::ObjectHierarchyObserver);
328bf80f4bSopenharmony_ci    CORE_ASSERT(observer_);
338bf80f4bSopenharmony_ci    clock_ = reg.Create<IClock>(ClassId::SystemClock);
348bf80f4bSopenharmony_ci    CORE_ASSERT(clock_);
358bf80f4bSopenharmony_ci
368bf80f4bSopenharmony_ci    observer_->OnHierarchyChanged()->AddHandler(
378bf80f4bSopenharmony_ci        MakeCallback<IOnHierarchyChanged>(this, &StartableObjectController::HierarchyChanged));
388bf80f4bSopenharmony_ci
398bf80f4bSopenharmony_ci    META_ACCESS_PROPERTY(StartBehavior)->OnChanged()->AddHandler(MakeCallback<IOnChanged>([this]() {
408bf80f4bSopenharmony_ci        if (META_ACCESS_PROPERTY_VALUE(StartBehavior) == StartBehavior::AUTOMATIC) {
418bf80f4bSopenharmony_ci            // If StartBehavior changes to AUTOMATIC, start all AUTOMATIC startables
428bf80f4bSopenharmony_ci            StartAll(ControlBehavior::CONTROL_AUTOMATIC);
438bf80f4bSopenharmony_ci        }
448bf80f4bSopenharmony_ci    }));
458bf80f4bSopenharmony_ci
468bf80f4bSopenharmony_ci    defaultTickerQueue_ = META_NS::GetObjectRegistry().Create<ITaskQueue>(ClassId::ThreadedTaskQueue);
478bf80f4bSopenharmony_ci    CORE_ASSERT(defaultTickerQueue_);
488bf80f4bSopenharmony_ci    tickerTask_ = META_NS::MakeCallback<ITaskQueueTask>([this]() {
498bf80f4bSopenharmony_ci        TickAll(clock_->GetTime());
508bf80f4bSopenharmony_ci        return true; // Recurring
518bf80f4bSopenharmony_ci    });
528bf80f4bSopenharmony_ci    CORE_ASSERT(tickerTask_);
538bf80f4bSopenharmony_ci
548bf80f4bSopenharmony_ci    META_ACCESS_PROPERTY(TickInterval)
558bf80f4bSopenharmony_ci        ->OnChanged()
568bf80f4bSopenharmony_ci        ->AddHandler(MakeCallback<IOnChanged>(this, &StartableObjectController::UpdateTicker));
578bf80f4bSopenharmony_ci    META_ACCESS_PROPERTY(TickOrder)->OnChanged()->AddHandler(
588bf80f4bSopenharmony_ci        MakeCallback<IOnChanged>(this, &StartableObjectController::InvalidateTickables));
598bf80f4bSopenharmony_ci    UpdateTicker();
608bf80f4bSopenharmony_ci    return true;
618bf80f4bSopenharmony_ci}
628bf80f4bSopenharmony_ci
638bf80f4bSopenharmony_civoid StartableObjectController::Destroy()
648bf80f4bSopenharmony_ci{
658bf80f4bSopenharmony_ci    if (tickerQueue_ && tickerToken_) {
668bf80f4bSopenharmony_ci        tickerQueue_->CancelTask(tickerToken_);
678bf80f4bSopenharmony_ci    }
688bf80f4bSopenharmony_ci    InvalidateTickables();
698bf80f4bSopenharmony_ci    SetTarget({}, {});
708bf80f4bSopenharmony_ci    observer_.reset();
718bf80f4bSopenharmony_ci}
728bf80f4bSopenharmony_ci
738bf80f4bSopenharmony_cibool StartableObjectController::SetStartableQueueId(
748bf80f4bSopenharmony_ci    const BASE_NS::Uid& startStartableQueueId, const BASE_NS::Uid& stopStartableQueueId)
758bf80f4bSopenharmony_ci{
768bf80f4bSopenharmony_ci    startQueueId_ = startStartableQueueId;
778bf80f4bSopenharmony_ci    stopQueueId_ = stopStartableQueueId;
788bf80f4bSopenharmony_ci    return true;
798bf80f4bSopenharmony_ci}
808bf80f4bSopenharmony_ci
818bf80f4bSopenharmony_civoid StartableObjectController::SetTarget(const IObject::Ptr& hierarchyRoot, HierarchyChangeModeValue mode)
828bf80f4bSopenharmony_ci{
838bf80f4bSopenharmony_ci    if (!observer_) {
848bf80f4bSopenharmony_ci        return;
858bf80f4bSopenharmony_ci    }
868bf80f4bSopenharmony_ci    InvalidateTickables();
878bf80f4bSopenharmony_ci    target_ = hierarchyRoot;
888bf80f4bSopenharmony_ci    bool automatic = META_ACCESS_PROPERTY_VALUE(StartBehavior) == StartBehavior::AUTOMATIC;
898bf80f4bSopenharmony_ci    if (automatic && !hierarchyRoot) {
908bf80f4bSopenharmony_ci        StopAll(ControlBehavior::CONTROL_AUTOMATIC);
918bf80f4bSopenharmony_ci    }
928bf80f4bSopenharmony_ci    observer_->SetTarget(hierarchyRoot, mode);
938bf80f4bSopenharmony_ci    if (automatic && hierarchyRoot) {
948bf80f4bSopenharmony_ci        StartAll(ControlBehavior::CONTROL_AUTOMATIC);
958bf80f4bSopenharmony_ci    }
968bf80f4bSopenharmony_ci}
978bf80f4bSopenharmony_ci
988bf80f4bSopenharmony_ciIObject::Ptr StartableObjectController::GetTarget() const
998bf80f4bSopenharmony_ci{
1008bf80f4bSopenharmony_ci    return observer_->GetTarget();
1018bf80f4bSopenharmony_ci}
1028bf80f4bSopenharmony_ci
1038bf80f4bSopenharmony_ciBASE_NS::vector<IObject::Ptr> StartableObjectController::GetAllObserved() const
1048bf80f4bSopenharmony_ci{
1058bf80f4bSopenharmony_ci    return observer_->GetAllObserved();
1068bf80f4bSopenharmony_ci}
1078bf80f4bSopenharmony_ci
1088bf80f4bSopenharmony_cibool StartableObjectController::StartAll(ControlBehavior behavior)
1098bf80f4bSopenharmony_ci{
1108bf80f4bSopenharmony_ci    if (const auto root = target_.lock()) {
1118bf80f4bSopenharmony_ci        return AddOperation({ StartableOperation::START, target_ }, startQueueId_);
1128bf80f4bSopenharmony_ci    }
1138bf80f4bSopenharmony_ci    return false;
1148bf80f4bSopenharmony_ci}
1158bf80f4bSopenharmony_ci
1168bf80f4bSopenharmony_cibool StartableObjectController::StopAll(ControlBehavior behavior)
1178bf80f4bSopenharmony_ci{
1188bf80f4bSopenharmony_ci    if (auto root = target_.lock()) {
1198bf80f4bSopenharmony_ci        return AddOperation({ StartableOperation::STOP, target_ }, stopQueueId_);
1208bf80f4bSopenharmony_ci    }
1218bf80f4bSopenharmony_ci    return false;
1228bf80f4bSopenharmony_ci}
1238bf80f4bSopenharmony_ci
1248bf80f4bSopenharmony_citemplate<class T, class Callback>
1258bf80f4bSopenharmony_civoid IterateChildren(const BASE_NS::vector<T>& children, bool reverse, Callback&& callback)
1268bf80f4bSopenharmony_ci{
1278bf80f4bSopenharmony_ci    if (reverse) {
1288bf80f4bSopenharmony_ci        for (auto it = children.rbegin(); it != children.rend(); ++it) {
1298bf80f4bSopenharmony_ci            callback(*it);
1308bf80f4bSopenharmony_ci        }
1318bf80f4bSopenharmony_ci    } else {
1328bf80f4bSopenharmony_ci        for (auto&& child : children) {
1338bf80f4bSopenharmony_ci            callback(child);
1348bf80f4bSopenharmony_ci        }
1358bf80f4bSopenharmony_ci    }
1368bf80f4bSopenharmony_ci}
1378bf80f4bSopenharmony_ci
1388bf80f4bSopenharmony_citemplate<class Callback>
1398bf80f4bSopenharmony_civoid IterateHierarchy(const IObject::Ptr& root, bool reverse, Callback&& callback)
1408bf80f4bSopenharmony_ci{
1418bf80f4bSopenharmony_ci    if (const auto container = interface_cast<IContainer>(root)) {
1428bf80f4bSopenharmony_ci        IterateChildren(container->GetAll(), reverse, callback);
1438bf80f4bSopenharmony_ci    }
1448bf80f4bSopenharmony_ci    if (const auto content = interface_cast<IContent>(root)) {
1458bf80f4bSopenharmony_ci        if (auto object = GetValue(content->Content())) {
1468bf80f4bSopenharmony_ci            callback(object);
1478bf80f4bSopenharmony_ci        }
1488bf80f4bSopenharmony_ci    }
1498bf80f4bSopenharmony_ci}
1508bf80f4bSopenharmony_ci
1518bf80f4bSopenharmony_citemplate<class ObjectType, class Callback>
1528bf80f4bSopenharmony_civoid IterateAttachments(const IObject::Ptr& object, bool reverse, Callback&& callback)
1538bf80f4bSopenharmony_ci{
1548bf80f4bSopenharmony_ci    if (const auto attach = interface_cast<IAttach>(object)) {
1558bf80f4bSopenharmony_ci        if (auto container = attach->GetAttachmentContainer(false)) {
1568bf80f4bSopenharmony_ci            IterateChildren(container->GetAll<ObjectType>(), reverse, BASE_NS::forward<Callback>(callback));
1578bf80f4bSopenharmony_ci        }
1588bf80f4bSopenharmony_ci    }
1598bf80f4bSopenharmony_ci}
1608bf80f4bSopenharmony_ci
1618bf80f4bSopenharmony_citemplate<class Callback>
1628bf80f4bSopenharmony_civoid IterateStartables(const IObject::Ptr& object, bool reverse, Callback&& callback)
1638bf80f4bSopenharmony_ci{
1648bf80f4bSopenharmony_ci    IterateAttachments<IStartable, Callback>(object, reverse, BASE_NS::forward<Callback>(callback));
1658bf80f4bSopenharmony_ci}
1668bf80f4bSopenharmony_ci
1678bf80f4bSopenharmony_citemplate<class Callback>
1688bf80f4bSopenharmony_civoid IterateTickables(const IObject::Ptr& object, TraversalType order, Callback&& callback)
1698bf80f4bSopenharmony_ci{
1708bf80f4bSopenharmony_ci    if (!object) {
1718bf80f4bSopenharmony_ci        return;
1728bf80f4bSopenharmony_ci    }
1738bf80f4bSopenharmony_ci    bool rootFirst = order != TraversalType::DEPTH_FIRST_POST_ORDER;
1748bf80f4bSopenharmony_ci    if (rootFirst) {
1758bf80f4bSopenharmony_ci        IterateAttachments<ITickable, Callback>(object, false, BASE_NS::forward<Callback>(callback));
1768bf80f4bSopenharmony_ci    }
1778bf80f4bSopenharmony_ci    IterateShared(
1788bf80f4bSopenharmony_ci        object,
1798bf80f4bSopenharmony_ci        [&callback](const IObject::Ptr& object) {
1808bf80f4bSopenharmony_ci            IterateAttachments<ITickable, Callback>(object, false, callback);
1818bf80f4bSopenharmony_ci            return true;
1828bf80f4bSopenharmony_ci        },
1838bf80f4bSopenharmony_ci        order);
1848bf80f4bSopenharmony_ci    if (!rootFirst) {
1858bf80f4bSopenharmony_ci        IterateAttachments<ITickable, Callback>(object, false, BASE_NS::forward<Callback>(callback));
1868bf80f4bSopenharmony_ci    }
1878bf80f4bSopenharmony_ci}
1888bf80f4bSopenharmony_ci
1898bf80f4bSopenharmony_civoid StartableObjectController::HierarchyChanged(const HierarchyChangedInfo& info)
1908bf80f4bSopenharmony_ci{
1918bf80f4bSopenharmony_ci    if (info.change == HierarchyChangeType::ADDED || info.change == HierarchyChangeType::REMOVING ||
1928bf80f4bSopenharmony_ci        info.change == HierarchyChangeType::MOVED) {
1938bf80f4bSopenharmony_ci        // Any hierarchy change (add/remove/move) invalidates the tick order
1948bf80f4bSopenharmony_ci        InvalidateTickables();
1958bf80f4bSopenharmony_ci        if (info.change == HierarchyChangeType::ADDED) {
1968bf80f4bSopenharmony_ci            AddOperation({ StartableOperation::START, info.object }, startQueueId_);
1978bf80f4bSopenharmony_ci        } else if (info.change == HierarchyChangeType::REMOVING) {
1988bf80f4bSopenharmony_ci            AddOperation({ StartableOperation::STOP, info.object }, stopQueueId_);
1998bf80f4bSopenharmony_ci        }
2008bf80f4bSopenharmony_ci    }
2018bf80f4bSopenharmony_ci}
2028bf80f4bSopenharmony_ci
2038bf80f4bSopenharmony_ciBASE_NS::vector<IStartable::Ptr> StartableObjectController::GetAllStartables() const
2048bf80f4bSopenharmony_ci{
2058bf80f4bSopenharmony_ci    BASE_NS::vector<IStartable::Ptr> startables;
2068bf80f4bSopenharmony_ci    auto add = [&startables](const IStartable::Ptr& startable) { startables.push_back(startable); };
2078bf80f4bSopenharmony_ci    if (const auto root = target_.lock()) {
2088bf80f4bSopenharmony_ci        IterateStartables(root, false, add);
2098bf80f4bSopenharmony_ci        IterateShared(
2108bf80f4bSopenharmony_ci            root,
2118bf80f4bSopenharmony_ci            [&add](const IObject::Ptr& object) {
2128bf80f4bSopenharmony_ci                IterateStartables(object, false, add);
2138bf80f4bSopenharmony_ci                return true;
2148bf80f4bSopenharmony_ci            },
2158bf80f4bSopenharmony_ci            TraversalType::DEPTH_FIRST_POST_ORDER);
2168bf80f4bSopenharmony_ci    }
2178bf80f4bSopenharmony_ci    return startables;
2188bf80f4bSopenharmony_ci}
2198bf80f4bSopenharmony_ci
2208bf80f4bSopenharmony_civoid StartableObjectController::StartHierarchy(const IObject::Ptr& root, ControlBehavior behavior)
2218bf80f4bSopenharmony_ci{
2228bf80f4bSopenharmony_ci    const auto traversal = META_ACCESS_PROPERTY_VALUE(TraversalType);
2238bf80f4bSopenharmony_ci    if (traversal != TraversalType::DEPTH_FIRST_POST_ORDER && traversal != TraversalType::FULL_HIERARCHY) {
2248bf80f4bSopenharmony_ci        CORE_LOG_E("Only DEPTH_FIRST_POST_ORDER is supported");
2258bf80f4bSopenharmony_ci    }
2268bf80f4bSopenharmony_ci
2278bf80f4bSopenharmony_ci    if (!root) {
2288bf80f4bSopenharmony_ci        return;
2298bf80f4bSopenharmony_ci    }
2308bf80f4bSopenharmony_ci
2318bf80f4bSopenharmony_ci    IterateHierarchy(root, false, [this, behavior](const IObject::Ptr& object) { StartHierarchy(object, behavior); });
2328bf80f4bSopenharmony_ci
2338bf80f4bSopenharmony_ci    // Don't traverse hierarchy for attachments
2348bf80f4bSopenharmony_ci    IterateStartables(
2358bf80f4bSopenharmony_ci        root, false, [this, behavior](const IStartable::Ptr& startable) { StartStartable(startable.get(), behavior); });
2368bf80f4bSopenharmony_ci
2378bf80f4bSopenharmony_ci    StartStartable(interface_cast<IStartable>(root), behavior);
2388bf80f4bSopenharmony_ci}
2398bf80f4bSopenharmony_ci
2408bf80f4bSopenharmony_civoid StartableObjectController::StartStartable(IStartable* const startable, ControlBehavior behavior)
2418bf80f4bSopenharmony_ci{
2428bf80f4bSopenharmony_ci    if (startable) {
2438bf80f4bSopenharmony_ci        const auto state = GetValue(startable->StartableState());
2448bf80f4bSopenharmony_ci        if (state == StartableState::ATTACHED) {
2458bf80f4bSopenharmony_ci            const auto mode = GetValue(startable->StartableMode());
2468bf80f4bSopenharmony_ci            if (behavior == ControlBehavior::CONTROL_ALL || mode == StartBehavior::AUTOMATIC) {
2478bf80f4bSopenharmony_ci                startable->Start();
2488bf80f4bSopenharmony_ci            }
2498bf80f4bSopenharmony_ci        }
2508bf80f4bSopenharmony_ci    }
2518bf80f4bSopenharmony_ci}
2528bf80f4bSopenharmony_ci
2538bf80f4bSopenharmony_civoid StartableObjectController::StopHierarchy(const IObject::Ptr& root, ControlBehavior behavior)
2548bf80f4bSopenharmony_ci{
2558bf80f4bSopenharmony_ci    const auto traversal = META_ACCESS_PROPERTY_VALUE(TraversalType);
2568bf80f4bSopenharmony_ci    if (traversal != TraversalType::DEPTH_FIRST_POST_ORDER && traversal != TraversalType::FULL_HIERARCHY) {
2578bf80f4bSopenharmony_ci        CORE_LOG_E("Only DEPTH_FIRST_POST_ORDER is supported");
2588bf80f4bSopenharmony_ci    }
2598bf80f4bSopenharmony_ci    if (!root) {
2608bf80f4bSopenharmony_ci        return;
2618bf80f4bSopenharmony_ci    }
2628bf80f4bSopenharmony_ci
2638bf80f4bSopenharmony_ci    StopStartable(interface_cast<IStartable>(root), behavior);
2648bf80f4bSopenharmony_ci
2658bf80f4bSopenharmony_ci    IterateStartables(
2668bf80f4bSopenharmony_ci        root, true, [this, behavior](const IStartable::Ptr& startable) { StopStartable(startable.get(), behavior); });
2678bf80f4bSopenharmony_ci
2688bf80f4bSopenharmony_ci    IterateHierarchy(root, true, [this, behavior](const IObject::Ptr& object) { StopHierarchy(object, behavior); });
2698bf80f4bSopenharmony_ci}
2708bf80f4bSopenharmony_ci
2718bf80f4bSopenharmony_civoid StartableObjectController::StopStartable(IStartable* const startable, ControlBehavior behavior)
2728bf80f4bSopenharmony_ci{
2738bf80f4bSopenharmony_ci    if (startable) {
2748bf80f4bSopenharmony_ci        const auto state = GetValue(startable->StartableState());
2758bf80f4bSopenharmony_ci        if (state == StartableState::STARTED) {
2768bf80f4bSopenharmony_ci            const auto mode = GetValue(startable->StartableMode());
2778bf80f4bSopenharmony_ci            if (behavior == ControlBehavior::CONTROL_ALL || mode == StartBehavior::AUTOMATIC) {
2788bf80f4bSopenharmony_ci                startable->Stop();
2798bf80f4bSopenharmony_ci            }
2808bf80f4bSopenharmony_ci        }
2818bf80f4bSopenharmony_ci    }
2828bf80f4bSopenharmony_ci}
2838bf80f4bSopenharmony_ci
2848bf80f4bSopenharmony_cibool StartableObjectController::HasTasks(const BASE_NS::Uid& queueId) const
2858bf80f4bSopenharmony_ci{
2868bf80f4bSopenharmony_ci    std::shared_lock lock(mutex_);
2878bf80f4bSopenharmony_ci    if (auto it = operations_.find(queueId); it != operations_.end()) {
2888bf80f4bSopenharmony_ci        return !it->second.empty();
2898bf80f4bSopenharmony_ci    }
2908bf80f4bSopenharmony_ci    return false;
2918bf80f4bSopenharmony_ci}
2928bf80f4bSopenharmony_ci
2938bf80f4bSopenharmony_civoid StartableObjectController::RunTasks(const BASE_NS::Uid& queueId)
2948bf80f4bSopenharmony_ci{
2958bf80f4bSopenharmony_ci    BASE_NS::vector<StartableOperation> operations;
2968bf80f4bSopenharmony_ci    {
2978bf80f4bSopenharmony_ci        std::unique_lock lock(mutex_);
2988bf80f4bSopenharmony_ci        // Take tasks for the given queue id
2998bf80f4bSopenharmony_ci        if (auto it = operations_.find(queueId); it != operations_.end()) {
3008bf80f4bSopenharmony_ci            operations.swap(it->second);
3018bf80f4bSopenharmony_ci        }
3028bf80f4bSopenharmony_ci    }
3038bf80f4bSopenharmony_ci    for (auto&& op : operations) {
3048bf80f4bSopenharmony_ci        // This may potentially end up calling Start/StopHierarchy several times
3058bf80f4bSopenharmony_ci        // for the same subtrees, but we will accept that. Start/Stop will only
3068bf80f4bSopenharmony_ci        // be called once since the functions check for current state.
3078bf80f4bSopenharmony_ci        if (auto root = op.root_.lock()) {
3088bf80f4bSopenharmony_ci            switch (op.operation_) {
3098bf80f4bSopenharmony_ci                case StartableOperation::START:
3108bf80f4bSopenharmony_ci                    ++executingStart_;
3118bf80f4bSopenharmony_ci                    StartHierarchy(root, ControlBehavior::CONTROL_AUTOMATIC);
3128bf80f4bSopenharmony_ci                    --executingStart_;
3138bf80f4bSopenharmony_ci                    break;
3148bf80f4bSopenharmony_ci                case StartableOperation::STOP:
3158bf80f4bSopenharmony_ci                    StopHierarchy(root, ControlBehavior::CONTROL_AUTOMATIC);
3168bf80f4bSopenharmony_ci                    break;
3178bf80f4bSopenharmony_ci                default:
3188bf80f4bSopenharmony_ci                    break;
3198bf80f4bSopenharmony_ci            }
3208bf80f4bSopenharmony_ci        }
3218bf80f4bSopenharmony_ci    }
3228bf80f4bSopenharmony_ci}
3238bf80f4bSopenharmony_ci
3248bf80f4bSopenharmony_cibool StartableObjectController::ProcessOps(const BASE_NS::Uid& queueId)
3258bf80f4bSopenharmony_ci{
3268bf80f4bSopenharmony_ci    if (!HasTasks(queueId)) {
3278bf80f4bSopenharmony_ci        // No tasks for the given queue, bail out
3288bf80f4bSopenharmony_ci        return true;
3298bf80f4bSopenharmony_ci    }
3308bf80f4bSopenharmony_ci
3318bf80f4bSopenharmony_ci    auto task = [queueId, internal = IStartableObjectControllerInternal::WeakPtr {
3328bf80f4bSopenharmony_ci        GetSelf<IStartableObjectControllerInternal>() }]() {
3338bf80f4bSopenharmony_ci            if (auto me = internal.lock()) {
3348bf80f4bSopenharmony_ci                me->RunTasks(queueId);
3358bf80f4bSopenharmony_ci            }
3368bf80f4bSopenharmony_ci    };
3378bf80f4bSopenharmony_ci
3388bf80f4bSopenharmony_ci    if (queueId != BASE_NS::Uid {} && !executingStart_) {
3398bf80f4bSopenharmony_ci        if (auto queue = GetTaskQueueRegistry().GetTaskQueue(queueId)) {
3408bf80f4bSopenharmony_ci            queue->AddWaitableTask(CreateWaitableTask(BASE_NS::move(task)));
3418bf80f4bSopenharmony_ci            return true;
3428bf80f4bSopenharmony_ci        }
3438bf80f4bSopenharmony_ci        CORE_LOG_W("Cannot get task queue '%s'. Running the task synchronously.", BASE_NS::to_string(queueId).c_str());
3448bf80f4bSopenharmony_ci    }
3458bf80f4bSopenharmony_ci    // Just run the task immediately if we don't have a queue to defer it to
3468bf80f4bSopenharmony_ci    task();
3478bf80f4bSopenharmony_ci    return true;
3488bf80f4bSopenharmony_ci}
3498bf80f4bSopenharmony_ci
3508bf80f4bSopenharmony_cibool StartableObjectController::AddOperation(StartableOperation&& operation, const BASE_NS::Uid& queueId)
3518bf80f4bSopenharmony_ci{
3528bf80f4bSopenharmony_ci    auto object = operation.root_.lock();
3538bf80f4bSopenharmony_ci    if (!object) {
3548bf80f4bSopenharmony_ci        return false;
3558bf80f4bSopenharmony_ci    }
3568bf80f4bSopenharmony_ci    // Note that queueId may be {}, but it is still a valid key for our queue map
3578bf80f4bSopenharmony_ci    {
3588bf80f4bSopenharmony_ci        std::unique_lock lock(mutex_);
3598bf80f4bSopenharmony_ci        auto& ops = operations_[queueId];
3608bf80f4bSopenharmony_ci        for (auto it = ops.begin(); it != ops.end(); ++it) {
3618bf80f4bSopenharmony_ci            // If we already have an operation in queue for a given object, cancel the existing operation
3628bf80f4bSopenharmony_ci            // and just add the new one
3638bf80f4bSopenharmony_ci            if ((*it).root_.lock() == object) {
3648bf80f4bSopenharmony_ci                ops.erase(it);
3658bf80f4bSopenharmony_ci                break;
3668bf80f4bSopenharmony_ci            }
3678bf80f4bSopenharmony_ci        }
3688bf80f4bSopenharmony_ci        ops.emplace_back(BASE_NS::move(operation));
3698bf80f4bSopenharmony_ci    }
3708bf80f4bSopenharmony_ci    return ProcessOps(queueId);
3718bf80f4bSopenharmony_ci}
3728bf80f4bSopenharmony_ci
3738bf80f4bSopenharmony_civoid StartableObjectController::InvalidateTickables()
3748bf80f4bSopenharmony_ci{
3758bf80f4bSopenharmony_ci    std::unique_lock lock(mutex_);
3768bf80f4bSopenharmony_ci    tickables_.clear();
3778bf80f4bSopenharmony_ci    tickablesValid_ = false;
3788bf80f4bSopenharmony_ci}
3798bf80f4bSopenharmony_ci
3808bf80f4bSopenharmony_ciBASE_NS::vector<ITickable::Ptr> StartableObjectController::GetTickables() const
3818bf80f4bSopenharmony_ci{
3828bf80f4bSopenharmony_ci    BASE_NS::vector<ITickable::WeakPtr> weaks;
3838bf80f4bSopenharmony_ci    {
3848bf80f4bSopenharmony_ci        std::unique_lock lock(tickMutex_);
3858bf80f4bSopenharmony_ci        if (!tickablesValid_) {
3868bf80f4bSopenharmony_ci            auto add = [this](const ITickable::Ptr& tickable) { tickables_.push_back(tickable); };
3878bf80f4bSopenharmony_ci            IterateTickables(target_.lock(), META_ACCESS_PROPERTY_VALUE(TickOrder), add);
3888bf80f4bSopenharmony_ci            tickablesValid_ = true;
3898bf80f4bSopenharmony_ci        }
3908bf80f4bSopenharmony_ci        weaks = tickables_;
3918bf80f4bSopenharmony_ci    }
3928bf80f4bSopenharmony_ci    BASE_NS::vector<ITickable::Ptr> tickables;
3938bf80f4bSopenharmony_ci    tickables.reserve(weaks.size());
3948bf80f4bSopenharmony_ci    for (auto&& t : weaks) {
3958bf80f4bSopenharmony_ci        if (auto tick = t.lock()) {
3968bf80f4bSopenharmony_ci            tickables.emplace_back(BASE_NS::move(tick));
3978bf80f4bSopenharmony_ci        }
3988bf80f4bSopenharmony_ci    }
3998bf80f4bSopenharmony_ci    return tickables;
4008bf80f4bSopenharmony_ci}
4018bf80f4bSopenharmony_ci
4028bf80f4bSopenharmony_civoid StartableObjectController::UpdateTicker()
4038bf80f4bSopenharmony_ci{
4048bf80f4bSopenharmony_ci    auto queue = tickQueueId_ != BASE_NS::Uid {} ? META_NS::GetTaskQueueRegistry().GetTaskQueue(tickQueueId_)
4058bf80f4bSopenharmony_ci                                                 : defaultTickerQueue_;
4068bf80f4bSopenharmony_ci    if (tickerQueue_ && tickerToken_) {
4078bf80f4bSopenharmony_ci        tickerQueue_->CancelTask(tickerToken_);
4088bf80f4bSopenharmony_ci        tickerToken_ = {};
4098bf80f4bSopenharmony_ci    }
4108bf80f4bSopenharmony_ci    tickerQueue_ = queue;
4118bf80f4bSopenharmony_ci    if (const auto interval = META_ACCESS_PROPERTY_VALUE(TickInterval); interval != TimeSpan::Infinite()) {
4128bf80f4bSopenharmony_ci        if (tickerQueue_) {
4138bf80f4bSopenharmony_ci            tickerToken_ = tickerQueue_->AddTask(tickerTask_, interval);
4148bf80f4bSopenharmony_ci        } else {
4158bf80f4bSopenharmony_ci            CORE_LOG_E("Invalid queue given for running ITickables: %s", BASE_NS::to_string(tickQueueId_).c_str());
4168bf80f4bSopenharmony_ci        }
4178bf80f4bSopenharmony_ci    }
4188bf80f4bSopenharmony_ci}
4198bf80f4bSopenharmony_ci
4208bf80f4bSopenharmony_cibool StartableObjectController::SetTickableQueueuId(const BASE_NS::Uid& queueId)
4218bf80f4bSopenharmony_ci{
4228bf80f4bSopenharmony_ci    if (queueId != tickQueueId_) {
4238bf80f4bSopenharmony_ci        tickQueueId_ = queueId;
4248bf80f4bSopenharmony_ci        UpdateTicker();
4258bf80f4bSopenharmony_ci    }
4268bf80f4bSopenharmony_ci    return true;
4278bf80f4bSopenharmony_ci}
4288bf80f4bSopenharmony_ci
4298bf80f4bSopenharmony_civoid StartableObjectController::TickAll(const TimeSpan& time)
4308bf80f4bSopenharmony_ci{
4318bf80f4bSopenharmony_ci    const auto tickables = GetTickables();
4328bf80f4bSopenharmony_ci    if (!tickables.empty()) {
4338bf80f4bSopenharmony_ci        const auto sinceLast = lastTick_ != TimeSpan::Infinite() ? time - lastTick_ : TimeSpan::Zero();
4348bf80f4bSopenharmony_ci        for (auto&& tickable : tickables) {
4358bf80f4bSopenharmony_ci            bool shouldTick = true;
4368bf80f4bSopenharmony_ci            if (const auto st = interface_cast<IStartable>(tickable)) {
4378bf80f4bSopenharmony_ci                shouldTick = GetValue(st->StartableState()) == StartableState::STARTED;
4388bf80f4bSopenharmony_ci            }
4398bf80f4bSopenharmony_ci            if (shouldTick) {
4408bf80f4bSopenharmony_ci                tickable->Tick(time, sinceLast);
4418bf80f4bSopenharmony_ci            }
4428bf80f4bSopenharmony_ci        }
4438bf80f4bSopenharmony_ci    }
4448bf80f4bSopenharmony_ci    lastTick_ = time;
4458bf80f4bSopenharmony_ci}
4468bf80f4bSopenharmony_ci
4478bf80f4bSopenharmony_ciMETA_END_NAMESPACE()
448