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 #include "verification/thread/verifier_thread.h"
17
18 #include "verification/job_queue/job.h"
19 #include "verification/job_queue/cache.h"
20 #include "verification/job_queue/job_queue.h"
21
22 #include "verification/absint/panda_types.h"
23
24 #include "runtime/include/mem/allocator.h"
25 #include "runtime/include/mem/panda_containers.h"
26 #include "runtime/include/runtime.h"
27
28 #include "libpandabase/os/thread.h"
29
30 #include "macros.h"
31
32 namespace panda::verifier {
33
34 size_t JobQueue::num_verifier_threads = 1;
35 std::array<PandaTypes *, JobQueue::MAX_THREADS> *JobQueue::panda_types = nullptr;
36 os::memory::ConditionVariable *JobQueue::job_get_cond_var = nullptr;
37 os::memory::ConditionVariable *JobQueue::job_put_cond_var = nullptr;
38 os::memory::ConditionVariable *JobQueue::method_cond_var = nullptr;
39 Job *JobQueue::queue_head = nullptr;
40 os::memory::Mutex *JobQueue::queue_lock = nullptr;
41 os::memory::Mutex *JobQueue::method_lock = nullptr;
42 CacheOfRuntimeThings *JobQueue::cache = nullptr;
43 std::array<panda::os::thread::native_handle_type, JobQueue::MAX_THREADS> *JobQueue::verifier_thread_handle = nullptr;
44 Synchronized<PandaUnorderedSet<uint32_t>> *JobQueue::system_files = nullptr;
45 std::atomic<bool> JobQueue::shutdown {false};
46 std::atomic<bool> JobQueue::initialized {false};
47 size_t JobQueue::num_jobs {0};
48 size_t JobQueue::max_jobs_in_queue {0};
49
Initialize(size_t num_threads,size_t queue_size)50 void JobQueue::Initialize(size_t num_threads, size_t queue_size)
51 {
52 if (initialized.load()) {
53 return;
54 }
55
56 max_jobs_in_queue = queue_size;
57
58 if (queue_lock == nullptr) {
59 queue_lock = new os::memory::Mutex;
60 }
61
62 if (method_lock == nullptr) {
63 method_lock = new os::memory::Mutex;
64 }
65
66 num_verifier_threads = num_threads;
67
68 panda_types = new std::array<PandaTypes *, JobQueue::MAX_THREADS> {};
69
70 for (size_t n = 0; n < num_threads; ++n) {
71 (*panda_types)[n] = new (mem::AllocatorAdapter<PandaTypes>().allocate(1)) PandaTypes {n};
72 (*panda_types)[n]->Init();
73 }
74
75 cache = new (mem::AllocatorAdapter<CacheOfRuntimeThings>().allocate(1)) CacheOfRuntimeThings {};
76
77 cache->FastAPI().InitializePandaAssemblyRootClasses();
78
79 job_get_cond_var =
80 new (mem::AllocatorAdapter<os::memory::ConditionVariable>().allocate(1)) os::memory::ConditionVariable {};
81
82 job_put_cond_var =
83 new (mem::AllocatorAdapter<os::memory::ConditionVariable>().allocate(1)) os::memory::ConditionVariable {};
84
85 method_cond_var =
86 new (mem::AllocatorAdapter<os::memory::ConditionVariable>().allocate(1)) os::memory::ConditionVariable {};
87
88 system_files = new Synchronized<PandaUnorderedSet<uint32_t>> {};
89
90 verifier_thread_handle = new std::array<panda::os::thread::native_handle_type, JobQueue::MAX_THREADS> {};
91
92 {
93 panda::os::memory::LockHolder lck {*JobQueue::queue_lock};
94 initialized.store(true);
95 for (size_t n = 0; n < num_threads; ++n) {
96 (*verifier_thread_handle)[n] = panda::os::thread::ThreadStart(VerifierThread, n);
97 }
98 }
99 }
100
Stop(bool wait_queue_empty)101 void JobQueue::Stop(bool wait_queue_empty)
102 {
103 if (!initialized.load()) {
104 return;
105 }
106 if (wait_queue_empty) {
107 panda::os::memory::LockHolder lck {*JobQueue::queue_lock};
108 while (num_jobs > 0) {
109 constexpr uint64_t QUANTUM = 100;
110 job_put_cond_var->TimedWait(queue_lock, QUANTUM);
111 }
112 } else {
113 panda::os::memory::LockHolder lck {*JobQueue::queue_lock};
114 while (queue_head != nullptr) {
115 auto next_job = queue_head->TakeNext();
116 JobQueue::DisposeJob(queue_head);
117 queue_head = next_job;
118 }
119 }
120
121 JobQueue::shutdown.store(true);
122 job_get_cond_var->SignalAll();
123
124 void *retval = nullptr;
125 for (size_t n = 0; n < num_verifier_threads; ++n) {
126 os::thread::ThreadJoin((*verifier_thread_handle)[n], &retval);
127 }
128 }
129
Destroy()130 void JobQueue::Destroy()
131 {
132 if (!initialized.load()) {
133 return;
134 }
135
136 cache->~CacheOfRuntimeThings();
137 mem::AllocatorAdapter<CacheOfRuntimeThings>().deallocate(cache, 1);
138
139 for (size_t n = 0; n < num_verifier_threads; ++n) {
140 (*panda_types)[n]->~PandaTypes();
141 mem::AllocatorAdapter<PandaTypes>().deallocate((*panda_types)[n], 1);
142 (*panda_types)[n] = nullptr;
143 }
144
145 mem::AllocatorAdapter<os::memory::ConditionVariable>().deallocate(job_get_cond_var, 1);
146 mem::AllocatorAdapter<os::memory::ConditionVariable>().deallocate(job_put_cond_var, 1);
147 mem::AllocatorAdapter<os::memory::ConditionVariable>().deallocate(method_cond_var, 1);
148
149 delete system_files;
150 system_files = nullptr;
151
152 delete panda_types;
153 panda_types = nullptr;
154
155 delete verifier_thread_handle;
156 verifier_thread_handle = nullptr;
157
158 delete queue_lock;
159 queue_lock = nullptr;
160
161 delete method_lock;
162 method_lock = nullptr;
163
164 initialized.store(false);
165 }
166
GetPandaTypes(size_t n)167 PandaTypes &JobQueue::GetPandaTypes(size_t n)
168 {
169 ASSERT(initialized.load());
170 return *(*panda_types)[n];
171 }
172
GetCache()173 CacheOfRuntimeThings &JobQueue::GetCache()
174 {
175 ASSERT(initialized);
176 return *cache;
177 }
178
AddJob(Job & job)179 void JobQueue::AddJob(Job &job)
180 {
181 ASSERT(initialized.load());
182 {
183 panda::os::memory::LockHolder lck {*JobQueue::queue_lock};
184 while (num_jobs >= max_jobs_in_queue) {
185 constexpr uint64_t QUANTUM = 100;
186 job_put_cond_var->TimedWait(queue_lock, QUANTUM);
187 }
188 job.SetNext(queue_head);
189 queue_head = &job;
190 ++num_jobs;
191 }
192 job_get_cond_var->SignalAll();
193 }
194
GetJob()195 Job *JobQueue::GetJob()
196 {
197 ASSERT(initialized.load());
198 auto check_job = []() -> Job * {
199 if (JobQueue::queue_head != nullptr) {
200 Job *result = JobQueue::queue_head;
201 JobQueue::queue_head = result->TakeNext();
202 --num_jobs;
203 return result;
204 }
205 return nullptr;
206 };
207 Job *job = nullptr;
208 panda::os::memory::LockHolder lck {*queue_lock};
209 if (JobQueue::shutdown.load()) {
210 return nullptr;
211 }
212 while ((job = check_job()) == nullptr) {
213 constexpr uint64_t QUANTUM = 100;
214 job_get_cond_var->TimedWait(queue_lock, QUANTUM);
215 if (JobQueue::shutdown.load()) {
216 return nullptr;
217 }
218 }
219 job_put_cond_var->SignalAll();
220 return job;
221 }
222
NewJob(Method & method)223 Job &JobQueue::NewJob(Method &method)
224 {
225 ASSERT(initialized.load());
226 auto id = method.GetUniqId();
227 auto &cached_method =
228 JobQueue::GetCache().GetFromCache<CacheOfRuntimeThings::CachedMethod>(method.GetClass()->GetSourceLang(), id);
229 if (Invalid(cached_method)) {
230 return Invalid<Job>();
231 }
232 auto &runtime = *Runtime::GetCurrent();
233 auto &verif_options = runtime.GetVerificationOptions();
234 auto method_name = method.GetFullName();
235 auto config = verif_options.Debug.GetMethodOptions()[method_name];
236 Job *job = nullptr;
237 if (!config) {
238 if (!verif_options.Debug.GetMethodOptions().IsOptionsPresent("default")) {
239 LOG(FATAL, VERIFIER) << "Cannot load default options";
240 UNREACHABLE();
241 }
242 job = new (mem::AllocatorAdapter<Job>().allocate(1))
243 Job {method, cached_method, verif_options.Debug.GetMethodOptions().GetOptions("default")};
244 LOG(DEBUG, VERIFIER) << "Verification config for '" << method_name << "'"
245 << " : 'default'";
246 } else {
247 job = new (mem::AllocatorAdapter<Job>().allocate(1)) Job {method, cached_method, config->get()};
248 LOG(DEBUG, VERIFIER) << "Verification config for '" << method_name << "'"
249 << " : '" << config->get().GetName() << "'";
250 }
251 if (job == nullptr) {
252 return Invalid<Job>();
253 }
254 return *job;
255 }
256
DisposeJob(Job * job)257 void JobQueue::DisposeJob(Job *job)
258 {
259 ASSERT(initialized.load());
260 ASSERT(job != nullptr);
261 job->~Job();
262 mem::AllocatorAdapter<Job>().deallocate(job, 1);
263 }
264
IsSystemFile(const panda_file::File * file)265 bool JobQueue::IsSystemFile(const panda_file::File *file)
266 {
267 ASSERT(initialized.load());
268 ASSERT(file != nullptr);
269 const auto hash = file->GetFilenameHash();
270 return (*system_files)->count(hash) > 0;
271 }
272
AddSystemFile(const std::string & filename)273 void JobQueue::AddSystemFile(const std::string &filename)
274 {
275 ASSERT(initialized.load());
276 const auto hash = panda_file::File::CalcFilenameHash(filename);
277 (*system_files)->insert(hash);
278 }
279
SignalMethodVerified()280 void JobQueue::SignalMethodVerified()
281 {
282 if (!initialized.load()) {
283 return;
284 }
285 method_cond_var->SignalAll();
286 }
287
288 } // namespace panda::verifier
289