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 
31 namespace panda::ecmascript {
32 class EcmaVM;
33 class 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.
37 enum class EnableConcurrentMarkType : uint8_t {
38     ENABLE,
39     CONFIG_DISABLE,
40     DISABLE,
41     REQUEST_DISABLE
42 };
43 
44 class ConcurrentMarker {
45 public:
46     ConcurrentMarker(Heap *heap, EnableConcurrentMarkType type);
47     ~ConcurrentMarker() = default;
48 
TryIncreaseTaskCounts()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 
DecreaseTaskCounts()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 
IsEnabled() const75     bool IsEnabled() const
76     {
77         return !IsDisabled();
78     }
79 
IsDisabled() const80     bool IsDisabled() const
81     {
82         return enableMarkType_ == EnableConcurrentMarkType::DISABLE ||
83             enableMarkType_ == EnableConcurrentMarkType::CONFIG_DISABLE;
84     }
85 
ConfigConcurrentMark(bool enabled)86     void ConfigConcurrentMark(bool enabled)
87     {
88         enableMarkType_ = enabled ? EnableConcurrentMarkType::ENABLE :
89                           EnableConcurrentMarkType::CONFIG_DISABLE;
90     }
91 
IsRequestDisabled() const92     bool IsRequestDisabled() const
93     {
94         return enableMarkType_ == EnableConcurrentMarkType::REQUEST_DISABLE;
95     }
96 
IsConfigDisabled() const97     bool IsConfigDisabled() const
98     {
99         return enableMarkType_ == EnableConcurrentMarkType::CONFIG_DISABLE;
100     }
101 
IsTriggeredConcurrentMark() const102     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 
GetDuration() const115     double GetDuration() const
116     {
117         return duration_;
118     }
119 
GetHeapObjectSize() const120     double GetHeapObjectSize() const
121     {
122         return heapObjectSize_;
123     }
124 
125 private:
126     NO_COPY_SEMANTIC(ConcurrentMarker);
127     NO_MOVE_SEMANTIC(ConcurrentMarker);
128 
129     class RecursionScope {
130     public:
RecursionScope(ConcurrentMarker* marker)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         }
~RecursionScope()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 
SetDuration(double duration)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