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#ifndef ECMASCRIPT_DAEMON_THREAD_H
17#define ECMASCRIPT_DAEMON_THREAD_H
18
19#include <atomic>
20#include <deque>
21#include <thread>
22
23#include "ecmascript/daemon/daemon_task.h"
24#include "ecmascript/js_thread.h"
25#include "ecmascript/mutator_lock.h"
26#include "ecmascript/platform/mutex.h"
27
28namespace panda::ecmascript {
29
30static constexpr uint32_t DAEMON_THREAD_INDEX = 0;
31
32class DaemonThread : public JSThread {
33public:
34    static void CreateNewInstance();
35    static DaemonThread *GetInstance();
36    static void DestroyInstance();
37
38    using ThreadId = uint32_t;
39
40    void StartRunning();
41
42    bool IsRunning() const;
43
44    void MarkTerminate();       // In daemon thread
45
46    // Wait daemon thread finished, and then call TaskPool.Destroy
47    void WaitFinished();
48
49    bool CheckAndPostTask(DaemonTask task);
50
51    /**
52     * Called in daemon thread, and manually call this in DaemonTask at the appropriate time,
53     * e.g. for GC task, call this before ResumeAll instead of the task complete ended, to prevent in some
54     * time sequence, all js_thread is resumed and one of these post another GC task, but the PostedGroup
55     * have not been cleaned, leading the task lost.
56    */
57    void FinishRunningTask();
58
59    SharedMarkStatus GetSharedMarkStatus() const
60    {
61        return markStatus_.load(std::memory_order_acquire);
62    }
63
64    void SetSharedMarkStatus(SharedMarkStatus markStatus);
65
66    bool IsReadyToConcurrentMark() const
67    {
68        return GetSharedMarkStatus() == SharedMarkStatus::READY_TO_CONCURRENT_MARK;
69    }
70
71    bool IsConcurrentMarkingOrFinished() const
72    {
73        return GetSharedMarkStatus() == SharedMarkStatus::CONCURRENT_MARKING_OR_FINISHED;
74    }
75
76#ifndef NDEBUG
77    MutatorLock::MutatorLockState GetMutatorLockState() const;
78    void SetMutatorLockState(MutatorLock::MutatorLockState newState);
79#endif
80
81private:
82    DaemonThread() : JSThread(ThreadType::DAEMON_THREAD) {}
83    ~DaemonThread() = default;
84
85    void Run();
86
87    bool AddTaskGroup(DaemonTaskGroup taskGroup);
88
89    DaemonTask PopTask();
90
91    static DaemonThread *instance_;
92
93    // In js thread, load the running need atomic, but in daemon do not need, since only daemon thread
94    // will set running_ to false
95    std::atomic<bool> running_ {false};
96
97    std::atomic<SharedMarkStatus> markStatus_ {SharedMarkStatus::READY_TO_CONCURRENT_MARK};
98
99    std::unique_ptr<std::thread> thread_;
100
101    std::deque<DaemonTask> tasks_;
102    DaemonTaskGroup runningGroup_ {DaemonTaskGroup::NONE};
103    uint32_t postedGroups_ {0};
104    Mutex mtx_;
105    ConditionVariable cv_;
106#ifndef NDEBUG
107    MutatorLock::MutatorLockState mutatorLockState_ = MutatorLock::MutatorLockState::UNLOCKED;
108#endif
109};
110}  // namespace panda::ecmascript
111#endif  // ECMASCRIPT_DAEMON_THREAD_H