1/*
2 * Copyright (c) 2023 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 "core/version_ctx.h"
17#include "core/entity.h"
18#include "util/slab.h"
19#include "ffrt_trace.h"
20
21namespace ffrt {
22static inline void BuildConsumeRelationship(VersionCtx* version, SCPUEUTask* consumer)
23{
24    if (version->status == DataStatus::IDLE) {
25        consumer->IncDepRef();
26    }
27    version->consumers.insert(consumer);
28    if (version->status == DataStatus::CONSUMED) {
29        version->status = DataStatus::READY;
30    }
31}
32
33static inline void BuildProducerProducerRelationship(VersionCtx* preVersion, SCPUEUTask* nextProducer)
34{
35    if (preVersion->status != DataStatus::CONSUMED) {
36        preVersion->nextProducer = nextProducer;
37        nextProducer->IncDepRef();
38    }
39}
40
41void VersionCtx::AddConsumer(SCPUEUTask* consumer, NestType nestType)
42{
43    FFRT_TRACE_SCOPE(2, AddConsumer);
44    // Parent's VersionCtx
45    VersionCtx* beConsumeVersion = this;
46    if (nestType == NestType::PARENTOUT || nestType == NestType::DEFAULT) {
47        // Create READY version when last is nullptr
48        if (last == nullptr) {
49            CreateChildVersion(consumer, DataStatus::READY);
50        }
51        beConsumeVersion = last;
52    }
53    BuildConsumeRelationship(beConsumeVersion, consumer);
54    consumer->ins.insert(beConsumeVersion);
55}
56
57void VersionCtx::AddProducer(SCPUEUTask* producer)
58{
59    FFRT_TRACE_SCOPE(2, AddAddProducer);
60    // Parent's VersionCtx
61    auto parentVersion = this;
62    if (parentVersion->last != nullptr) {
63        VersionCtx* preVersion = parentVersion->last;
64        BuildProducerProducerRelationship(preVersion, producer);
65    }
66    parentVersion->CreateChildVersion(producer, DataStatus::IDLE);
67    producer->outs.insert(parentVersion->last);
68    parentVersion->last->myProducer = producer;
69}
70
71void VersionCtx::onProduced()
72{
73    /* No merge operation, merge operation can only occur when the parent version is produced and
74     * the last child version is not in the CONSUMED state
75     */
76    if (last == nullptr || last->status == DataStatus::CONSUMED) {
77        // No consumers, directly into CONSUMED after being produced
78        if (consumers.empty()) {
79            status = DataStatus::CONSUMED;
80            NotifyNextProducer();
81            Entity::Instance()->versionTrashcan.push_back(this);
82        } else { // if have consumers,notify them
83            status = DataStatus::READY;
84            NotifyConsumers();
85        }
86        NotifyDataWaitTask();
87    } else { // Merge previous VersionCtx
88        MergeChildVersion();
89    }
90}
91
92void VersionCtx::onConsumed(SCPUEUTask* consumer)
93{
94    auto it = std::as_const(consumers).find(consumer);
95    if (it != consumers.end()) {
96        consumers.erase(it);
97    }
98    if (consumers.empty()) {
99        status = DataStatus::CONSUMED;
100        NotifyNextProducer();
101        Entity::Instance()->versionTrashcan.push_back(this);
102    }
103}
104
105void VersionCtx::CreateChildVersion(SCPUEUTask* task __attribute__((unused)), DataStatus dataStatus)
106{
107    // Add VersionCtx
108    auto prev = last;
109    last = new (SimpleAllocator<VersionCtx>::AllocMem()) VersionCtx(this->signature, this, prev);
110    last->status = dataStatus;
111    if (prev != nullptr) {
112        prev->next = last;
113    }
114}
115
116void VersionCtx::MergeChildVersion()
117{
118    // Merge VersionCtx
119    auto versionToMerge = last;
120    status = versionToMerge->status;
121    if (status == DataStatus::READY) {
122        NotifyConsumers();
123        NotifyDataWaitTask();
124    }
125    MergeConsumerInDep(versionToMerge);
126    if (status == DataStatus::IDLE) {
127        consumers.insert(versionToMerge->consumers.cbegin(), versionToMerge->consumers.cend());
128        MergeProducerOutDep(versionToMerge);
129        myProducer = versionToMerge->myProducer;
130    }
131    Entity::Instance()->versionTrashcan.push_back(versionToMerge);
132}
133} /* namespace ffrt */