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
16#include "ecmascript/mem/shared_heap/shared_concurrent_sweeper.h"
17#include "ecmascript/mem/heap.h"
18
19namespace panda::ecmascript {
20SharedConcurrentSweeper::SharedConcurrentSweeper(SharedHeap *heap, EnableConcurrentSweepType type)
21    : sHeap_(heap),
22      enableType_(type)
23{
24}
25
26void SharedConcurrentSweeper::PostTask(bool isFullGC)
27{
28    auto tid = DaemonThread::GetInstance()->GetThreadId();
29    if (ConcurrentSweepEnabled()) {
30        if (!isFullGC) {
31            Taskpool::GetCurrentTaskpool()->PostTask(
32                std::make_unique<SweeperTask>(tid, this, SHARED_OLD_SPACE));
33        }
34        Taskpool::GetCurrentTaskpool()->PostTask(
35            std::make_unique<SweeperTask>(tid, this, SHARED_NON_MOVABLE));
36    }
37}
38
39void SharedConcurrentSweeper::Sweep(bool isFullGC)
40{
41    isFullGC_ = isFullGC;
42    if (ConcurrentSweepEnabled()) {
43        // Add all region to region list. Ensure all task finish
44        if (!isFullGC_) {
45            sHeap_->GetOldSpace()->PrepareSweeping();
46            for (int spaceIndex = 0; spaceIndex < SHARED_SWEEPING_SPACE_NUM; spaceIndex++) {
47                remainingTaskNum_[spaceIndex] = SHARED_SWEEPING_SPACE_NUM;
48            }
49        } else {
50            remainingTaskNum_[0] = 0;  // No need sweep shared old space in FullGC.
51            remainingTaskNum_[1] = 1;  // Need sweep nonmovable space in FullGC.
52        }
53        sHeap_->GetNonMovableSpace()->PrepareSweeping();
54        // Prepare
55        isSweeping_ = true;
56    } else {
57        if (!isFullGC_) {
58            sHeap_->GetOldSpace()->Sweep();
59        }
60        sHeap_->GetNonMovableSpace()->Sweep();
61    }
62    sHeap_->GetHugeObjectSpace()->Sweep();
63}
64
65void SharedConcurrentSweeper::AsyncSweepSpace(MemSpaceType type, bool isMain)
66{
67    auto space = sHeap_->GetSpaceWithType(type);
68    space->AsyncSweep(isMain);
69    int spaceIndex = type - SHARED_SWEEPING_SPACE_BEGIN;
70    LockHolder holder(mutexs_[spaceIndex]);
71    if (--remainingTaskNum_[spaceIndex] == 0) {
72        cvs_[spaceIndex].SignalAll();
73    }
74}
75
76void SharedConcurrentSweeper::WaitAllTaskFinished()
77{
78    if (!isSweeping_) {
79        return;
80    }
81    int spaceIndex = isFullGC_ ? 1 : 0;
82    for (; spaceIndex < SHARED_SWEEPING_SPACE_NUM; spaceIndex++) {
83        if (remainingTaskNum_[spaceIndex] > 0) {
84            LockHolder holder(mutexs_[spaceIndex]);
85            while (remainingTaskNum_[spaceIndex] > 0) {
86                cvs_[spaceIndex].Wait(&mutexs_[spaceIndex]);
87            }
88        }
89    }
90}
91
92// call in suspendAll thread.
93void SharedConcurrentSweeper::EnsureAllTaskFinished()
94{
95    if (!isSweeping_) {
96        return;
97    }
98    int spaceIndex = isFullGC_ ? 1 : 0;
99    for (; spaceIndex < SHARED_SWEEPING_SPACE_NUM; spaceIndex++) {
100        int type = spaceIndex + SHARED_SWEEPING_SPACE_BEGIN;
101        WaitingTaskFinish(static_cast<MemSpaceType>(type));
102    }
103    isSweeping_ = false;
104    if (IsRequestDisabled()) {
105        enableType_ = EnableConcurrentSweepType::DISABLE;
106    }
107}
108
109// call in mutator thread
110void SharedConcurrentSweeper::EnsureTaskFinished(MemSpaceType type)
111{
112    if (!isSweeping_) {
113        return;
114    }
115    WaitingTaskFinish(type);
116}
117
118void SharedConcurrentSweeper::WaitingTaskFinish(MemSpaceType type)
119{
120    int spaceIndex = type - SHARED_SWEEPING_SPACE_BEGIN;
121    if (remainingTaskNum_[spaceIndex] > 0) {
122        {
123            LockHolder holder(mutexs_[spaceIndex]);
124            remainingTaskNum_[spaceIndex]++;
125        }
126        AsyncSweepSpace(type, true);
127        LockHolder holder(mutexs_[spaceIndex]);
128        while (remainingTaskNum_[spaceIndex] > 0) {
129            cvs_[spaceIndex].Wait(&mutexs_[spaceIndex]);
130        }
131    }
132    SharedSparseSpace *space = sHeap_->GetSpaceWithType(type);
133    space->FinishFillSweptRegion();
134}
135
136void SharedConcurrentSweeper::TryFillSweptRegion()
137{
138    if (!isFullGC_) {
139        sHeap_->GetOldSpace()->TryFillSweptRegion();
140    }
141    sHeap_->GetNonMovableSpace()->TryFillSweptRegion();
142}
143
144bool SharedConcurrentSweeper::SweeperTask::Run([[maybe_unused]] uint32_t threadIndex)
145{
146    if (type_ == SHARED_NON_MOVABLE) {
147        sweeper_->AsyncSweepSpace(SHARED_NON_MOVABLE, false);
148        if (!sweeper_->isFullGC_) {
149            sweeper_->AsyncSweepSpace(SHARED_OLD_SPACE, false);
150        }
151    } else {
152        ASSERT(type_ == SHARED_OLD_SPACE);
153        if (!sweeper_->isFullGC_) {
154            sweeper_->AsyncSweepSpace(SHARED_OLD_SPACE, false);
155        }
156        sweeper_->AsyncSweepSpace(SHARED_NON_MOVABLE, false);
157    }
158
159    return true;
160}
161
162void SharedConcurrentSweeper::EnableConcurrentSweep(EnableConcurrentSweepType type)
163{
164    if (IsConfigDisabled()) {
165        return;
166    }
167    if (ConcurrentSweepEnabled() && isSweeping_ && type == EnableConcurrentSweepType::DISABLE) {
168        enableType_ = EnableConcurrentSweepType::REQUEST_DISABLE;
169    } else {
170        enableType_ = type;
171    }
172}
173}  // namespace panda::ecmascript
174