1/*
2 * Copyright (c) 2021 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#ifndef ECMASCRIPT_MEM_CONCURRENT_MARKER_H
17#define ECMASCRIPT_MEM_CONCURRENT_MARKER_H
18
19#include <array>
20#include <atomic>
21
22#include "ecmascript/common.h"
23#include "ecmascript/mem/clock_scope.h"
24#include "ecmascript/mem/space.h"
25#include "ecmascript/mem/visitor.h"
26#include "ecmascript/mem/work_manager.h"
27#include "ecmascript/taskpool/task.h"
28
29#include "ecmascript/platform/mutex.h"
30
31namespace panda::ecmascript {
32class EcmaVM;
33class Heap;
34// CONFIG_DISABLE means concurrent marker is disabled by options or macros and cannot be changed.
35// REQUEST_DISABLE means we want to disable concurrent sweeper while it is marking.
36// REQUEST_DISABLE can be ragarded as enable and will be changed into disable after this GC.
37enum class EnableConcurrentMarkType : uint8_t {
38    ENABLE,
39    CONFIG_DISABLE,
40    DISABLE,
41    REQUEST_DISABLE
42};
43
44class ConcurrentMarker {
45public:
46    ConcurrentMarker(Heap *heap, EnableConcurrentMarkType type);
47    ~ConcurrentMarker() = default;
48
49    static bool TryIncreaseTaskCounts()
50    {
51        size_t taskPoolSize = Taskpool::GetCurrentTaskpool()->GetTotalThreadNum();
52        {
53            LockHolder holder(taskCountMutex_);
54            // total counts of running concurrent mark tasks should be less than taskPoolSize
55            if (taskCounts_ + 1 < taskPoolSize) {
56                taskCounts_++;
57                return true;
58            }
59        }
60        LOG_FULL(INFO) << "Concurrent mark tasks in taskPool are full";
61        return false;
62    }
63
64    static void DecreaseTaskCounts()
65    {
66        LockHolder holder(taskCountMutex_);
67        taskCounts_--;
68    }
69
70    /*
71     * Concurrent marking related configurations and utilities.
72     */
73    void EnableConcurrentMarking(EnableConcurrentMarkType type);
74
75    bool IsEnabled() const
76    {
77        return !IsDisabled();
78    }
79
80    bool IsDisabled() const
81    {
82        return enableMarkType_ == EnableConcurrentMarkType::DISABLE ||
83            enableMarkType_ == EnableConcurrentMarkType::CONFIG_DISABLE;
84    }
85
86    void ConfigConcurrentMark(bool enabled)
87    {
88        enableMarkType_ = enabled ? EnableConcurrentMarkType::ENABLE :
89                          EnableConcurrentMarkType::CONFIG_DISABLE;
90    }
91
92    bool IsRequestDisabled() const
93    {
94        return enableMarkType_ == EnableConcurrentMarkType::REQUEST_DISABLE;
95    }
96
97    bool IsConfigDisabled() const
98    {
99        return enableMarkType_ == EnableConcurrentMarkType::CONFIG_DISABLE;
100    }
101
102    bool IsTriggeredConcurrentMark() const
103    {
104        return isConcurrentMarking_;
105    }
106    void ProcessConcurrentMarkTask(uint32_t threadId);
107    void Mark();
108    void Finish();
109    void ReMark();
110
111    void HandleMarkingFinished(GCReason gcReason = GCReason::ALLOCATION_LIMIT);  // call in vm thread.
112    void WaitMarkingFinished();  // call in main thread
113    void Reset(bool revertCSet = true);
114
115    double GetDuration() const
116    {
117        return duration_;
118    }
119
120    double GetHeapObjectSize() const
121    {
122        return heapObjectSize_;
123    }
124
125private:
126    NO_COPY_SEMANTIC(ConcurrentMarker);
127    NO_MOVE_SEMANTIC(ConcurrentMarker);
128
129    class RecursionScope {
130    public:
131        explicit RecursionScope(ConcurrentMarker* marker) : marker_(marker)
132        {
133            if (marker_->recursionDepth_++ != 0) {
134                LOG_GC(FATAL) << "Recursion in ConcurrentMarker Constructor, depth: " << marker_->recursionDepth_;
135            }
136        }
137        ~RecursionScope()
138        {
139            if (--marker_->recursionDepth_ != 0) {
140                LOG_GC(FATAL) << "Recursion in ConcurrentMarker Destructor, depth: " << marker_->recursionDepth_;
141            }
142        }
143    private:
144        ConcurrentMarker* marker_ {nullptr};
145    };
146
147    void SetDuration(double duration)
148    {
149        duration_ = duration;
150    }
151
152    void InitializeMarking();
153    bool ShouldNotifyMarkingFinished();  // call in GC thread.
154    void FinishMarking();   // call in GC thread.
155    bool VerifyAllRegionsNonFresh();
156
157    static size_t taskCounts_;
158    static Mutex taskCountMutex_;
159
160    Heap *heap_ {nullptr};
161    EcmaVM *vm_ {nullptr};
162    JSThread *thread_ {nullptr};
163
164    // obtained from the shared heap instance.
165    WorkManager *workManager_ {nullptr};
166    size_t heapObjectSize_ {0};
167    double duration_ {0.0};
168    EnableConcurrentMarkType enableMarkType_ {EnableConcurrentMarkType::CONFIG_DISABLE};
169
170    std::atomic<int> runningTaskCount_ {0};
171    bool notifyMarkingFinished_ {false};    // Use different value from markingFinished_ to prevent JSThread waking up
172                                            // before FinishMarking finishes.
173    bool markingFinished_ {false};
174    bool isConcurrentMarking_ {false};
175    Mutex waitMarkingFinishedMutex_;
176    ConditionVariable waitMarkingFinishedCV_;
177    int32_t recursionDepth_ {0};
178    ClockScope clockScope_;
179
180    friend class Heap;
181};
182}  // namespace panda::ecmascript
183#endif  // ECMASCRIPT_MEM_CONCURRENT_MARKER_H
184