1/*
2 * Copyright (c) 2021 - 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 "compileQueue.h"
17
18#include "varbinder/varbinder.h"
19#include "varbinder/scope.h"
20#include "compiler/core/emitter.h"
21#include "compiler/core/function.h"
22#include "compiler/core/pandagen.h"
23#include "public/public.h"
24
25namespace ark::es2panda::compiler {
26CompileQueue::CompileQueue(size_t threadCount)
27{
28    threads_.reserve(threadCount);
29
30    for (size_t i = 0; i < threadCount; i++) {
31        threads_.push_back(os::thread::ThreadStart(Worker, this));
32    }
33}
34
35CompileQueue::~CompileQueue()
36{
37    void *retval = nullptr;
38
39    std::unique_lock<std::mutex> lock(m_);
40    terminate_ = true;
41    lock.unlock();
42    jobsAvailable_.notify_all();
43
44    for (const auto handleId : threads_) {
45        os::thread::ThreadJoin(handleId, &retval);
46    }
47}
48
49void CompileQueue::Schedule(public_lib::Context *context)
50{
51    ASSERT(jobsCount_ == 0);
52    std::unique_lock<std::mutex> lock(m_);
53    const auto &functions = context->parserProgram->VarBinder()->Functions();
54    jobs_ = new CompileJob[functions.size()]();
55
56    for (auto *function : functions) {
57        jobs_[jobsCount_++].SetContext(context, function);  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
58    }
59
60    totalJobsCount_ = jobsCount_;
61
62    lock.unlock();
63    jobsAvailable_.notify_all();
64}
65
66void CompileQueue::Worker(CompileQueue *queue)
67{
68    while (true) {
69        std::unique_lock<std::mutex> lock(queue->m_);
70        queue->jobsAvailable_.wait(lock, [queue]() { return queue->terminate_ || queue->jobsCount_ != 0; });
71
72        if (queue->terminate_) {
73            return;
74        }
75
76        lock.unlock();
77
78        queue->Consume();
79        queue->jobsFinished_.notify_one();
80    }
81}
82
83void CompileQueue::Consume()
84{
85    std::unique_lock<std::mutex> lock(m_);
86    activeWorkers_++;
87
88    while (jobsCount_ > 0) {
89        --jobsCount_;
90        auto &job = jobs_[jobsCount_];  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
91
92        lock.unlock();
93
94        try {
95            job.Run();
96        } catch (const Error &e) {
97            lock.lock();
98            errors_.push_back(e);
99            lock.unlock();
100        }
101
102        lock.lock();
103    }
104
105    activeWorkers_--;
106}
107
108void CompileQueue::Wait(const JobsFinishedCb &onFinishedCb)
109{
110    std::unique_lock<std::mutex> lock(m_);
111    jobsFinished_.wait(lock, [this]() { return activeWorkers_ == 0 && jobsCount_ == 0; });
112
113    if (!errors_.empty()) {
114        delete[] jobs_;
115        // NOLINTNEXTLINE
116        throw errors_.front();
117    }
118
119    for (uint32_t i = 0; i < totalJobsCount_; i++) {
120        onFinishedCb(jobs_ + i);  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
121    }
122
123    delete[] jobs_;
124}
125}  // namespace ark::es2panda::compiler
126