1 | // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | #include "platform/assert.h" |
6 | #include "vm/heap/safepoint.h" |
7 | #include "vm/isolate.h" |
8 | #include "vm/lockers.h" |
9 | #include "vm/profiler.h" |
10 | #include "vm/stack_frame.h" |
11 | #include "vm/symbols.h" |
12 | #include "vm/thread_pool.h" |
13 | #include "vm/unit_test.h" |
14 | |
15 | namespace dart { |
16 | |
17 | DECLARE_FLAG(bool, enable_interpreter); |
18 | |
19 | VM_UNIT_TEST_CASE(Mutex) { |
20 | // This unit test case needs a running isolate. |
21 | TestCase::CreateTestIsolate(); |
22 | Mutex* mutex = new Mutex(); |
23 | mutex->Lock(); |
24 | EXPECT_EQ(false, mutex->TryLock()); |
25 | mutex->Unlock(); |
26 | EXPECT_EQ(true, mutex->TryLock()); |
27 | mutex->Unlock(); |
28 | { |
29 | MutexLocker ml(mutex); |
30 | EXPECT_EQ(false, mutex->TryLock()); |
31 | } |
32 | // The isolate shutdown and the destruction of the mutex are out-of-order on |
33 | // purpose. |
34 | Dart_ShutdownIsolate(); |
35 | delete mutex; |
36 | } |
37 | |
38 | VM_UNIT_TEST_CASE(Monitor) { |
39 | // This unit test case needs a running isolate. |
40 | TestCase::CreateTestIsolate(); |
41 | OSThread* thread = OSThread::Current(); |
42 | // Thread interrupter interferes with this test, disable interrupts. |
43 | thread->DisableThreadInterrupts(); |
44 | Monitor* monitor = new Monitor(); |
45 | monitor->Enter(); |
46 | monitor->Exit(); |
47 | EXPECT_EQ(true, monitor->TryEnter()); |
48 | monitor->Exit(); |
49 | |
50 | const int kNumAttempts = 5; |
51 | int attempts = 0; |
52 | while (attempts < kNumAttempts) { |
53 | MonitorLocker ml(monitor); |
54 | int64_t start = OS::GetCurrentMonotonicMicros(); |
55 | int64_t wait_time = 2017; |
56 | Monitor::WaitResult wait_result = ml.Wait(wait_time); |
57 | int64_t stop = OS::GetCurrentMonotonicMicros(); |
58 | |
59 | // We expect to be timing out here. |
60 | EXPECT_EQ(Monitor::kTimedOut, wait_result); |
61 | |
62 | // Check whether this attempt falls within the expected time limits. |
63 | int64_t wakeup_time = (stop - start) / kMicrosecondsPerMillisecond; |
64 | OS::PrintErr("wakeup_time: %" Pd64 "\n" , wakeup_time); |
65 | const int kAcceptableTimeJitter = 20; // Measured in milliseconds. |
66 | const int kAcceptableWakeupDelay = 150; // Measured in milliseconds. |
67 | if (((wait_time - kAcceptableTimeJitter) <= wakeup_time) && |
68 | (wakeup_time <= (wait_time + kAcceptableWakeupDelay))) { |
69 | break; |
70 | } |
71 | |
72 | // Record the attempt. |
73 | attempts++; |
74 | } |
75 | EXPECT_LT(attempts, kNumAttempts); |
76 | |
77 | // The isolate shutdown and the destruction of the mutex are out-of-order on |
78 | // purpose. |
79 | Dart_ShutdownIsolate(); |
80 | delete monitor; |
81 | } |
82 | |
83 | class ObjectCounter : public ObjectPointerVisitor { |
84 | public: |
85 | explicit ObjectCounter(IsolateGroup* isolate_group, const Object* obj) |
86 | : ObjectPointerVisitor(isolate_group), obj_(obj), count_(0) {} |
87 | |
88 | virtual void VisitPointers(ObjectPtr* first, ObjectPtr* last) { |
89 | for (ObjectPtr* current = first; current <= last; ++current) { |
90 | if (*current == obj_->raw()) { |
91 | ++count_; |
92 | } |
93 | } |
94 | } |
95 | |
96 | intptr_t count() const { return count_; } |
97 | |
98 | private: |
99 | const Object* obj_; |
100 | intptr_t count_; |
101 | }; |
102 | |
103 | class TaskWithZoneAllocation : public ThreadPool::Task { |
104 | public: |
105 | TaskWithZoneAllocation(Isolate* isolate, |
106 | Monitor* monitor, |
107 | bool* done, |
108 | intptr_t id) |
109 | : isolate_(isolate), monitor_(monitor), done_(done), id_(id) {} |
110 | virtual void Run() { |
111 | Thread::EnterIsolateAsHelper(isolate_, Thread::kUnknownTask); |
112 | { |
113 | Thread* thread = Thread::Current(); |
114 | // Create a zone (which is also a stack resource) and exercise it a bit. |
115 | StackZone stack_zone(thread); |
116 | HANDLESCOPE(thread); |
117 | Zone* zone = thread->zone(); |
118 | EXPECT_EQ(zone, stack_zone.GetZone()); |
119 | ZoneGrowableArray<bool>* a0 = new (zone) ZoneGrowableArray<bool>(zone, 1); |
120 | GrowableArray<bool> a1(zone, 1); |
121 | for (intptr_t i = 0; i < 100000; ++i) { |
122 | a0->Add(true); |
123 | a1.Add(true); |
124 | } |
125 | // Check that we can create handles and allocate in old space. |
126 | String& str = String::Handle(zone, String::New("old" , Heap::kOld)); |
127 | EXPECT(str.Equals("old" )); |
128 | |
129 | const intptr_t unique_smi = id_ + 928327281; |
130 | Smi& smi = Smi::Handle(zone, Smi::New(unique_smi)); |
131 | EXPECT(smi.Value() == unique_smi); |
132 | { |
133 | HeapIterationScope iteration(thread); |
134 | ObjectCounter counter(isolate_->group(), &smi); |
135 | // Ensure that our particular zone is visited. |
136 | iteration.IterateStackPointers(&counter, |
137 | ValidationPolicy::kValidateFrames); |
138 | EXPECT_EQ(1, counter.count()); |
139 | } |
140 | char* unique_chars = zone->PrintToString("unique_str_%" Pd, id_); |
141 | String& unique_str = String::Handle(zone); |
142 | { |
143 | // String::New may create additional handles in the topmost scope that |
144 | // we don't want to count, so wrap this in its own scope. |
145 | HANDLESCOPE(thread); |
146 | unique_str = String::New(unique_chars, Heap::kOld); |
147 | } |
148 | EXPECT(unique_str.Equals(unique_chars)); |
149 | { |
150 | HeapIterationScope iteration(thread); |
151 | ObjectCounter str_counter(isolate_->group(), &unique_str); |
152 | // Ensure that our particular zone is visited. |
153 | iteration.IterateStackPointers(&str_counter, |
154 | ValidationPolicy::kValidateFrames); |
155 | // We should visit the string object exactly once. |
156 | EXPECT_EQ(1, str_counter.count()); |
157 | } |
158 | } |
159 | Thread::ExitIsolateAsHelper(); |
160 | { |
161 | MonitorLocker ml(monitor_); |
162 | *done_ = true; |
163 | ml.Notify(); |
164 | } |
165 | } |
166 | |
167 | private: |
168 | Isolate* isolate_; |
169 | Monitor* monitor_; |
170 | bool* done_; |
171 | intptr_t id_; |
172 | }; |
173 | |
174 | ISOLATE_UNIT_TEST_CASE(ManyTasksWithZones) { |
175 | const int kTaskCount = 100; |
176 | Monitor sync[kTaskCount]; |
177 | bool done[kTaskCount]; |
178 | Isolate* isolate = thread->isolate(); |
179 | EXPECT(isolate->heap()->GrowthControlState()); |
180 | isolate->heap()->DisableGrowthControl(); |
181 | for (int i = 0; i < kTaskCount; i++) { |
182 | done[i] = false; |
183 | Dart::thread_pool()->Run<TaskWithZoneAllocation>(isolate, &sync[i], |
184 | &done[i], i); |
185 | } |
186 | bool in_isolate = true; |
187 | for (int i = 0; i < kTaskCount; i++) { |
188 | // Check that main mutator thread can still freely use its own zone. |
189 | String& bar = String::Handle(String::New("bar" )); |
190 | if (i % 10 == 0) { |
191 | // Mutator thread is free to independently move in/out/between isolates. |
192 | Thread::ExitIsolate(); |
193 | in_isolate = false; |
194 | } |
195 | MonitorLocker ml(&sync[i]); |
196 | while (!done[i]) { |
197 | if (in_isolate) { |
198 | ml.WaitWithSafepointCheck(thread); |
199 | } else { |
200 | ml.Wait(); |
201 | } |
202 | } |
203 | EXPECT(done[i]); |
204 | if (i % 10 == 0) { |
205 | Thread::EnterIsolate(isolate); |
206 | in_isolate = true; |
207 | } |
208 | EXPECT(bar.Equals("bar" )); |
209 | } |
210 | } |
211 | |
212 | #ifndef PRODUCT |
213 | class SimpleTaskWithZoneAllocation : public ThreadPool::Task { |
214 | public: |
215 | SimpleTaskWithZoneAllocation(intptr_t id, |
216 | Isolate* isolate, |
217 | Thread** thread_ptr, |
218 | Monitor* sync, |
219 | Monitor* monitor, |
220 | intptr_t* done_count, |
221 | bool* wait) |
222 | : id_(id), |
223 | isolate_(isolate), |
224 | thread_ptr_(thread_ptr), |
225 | sync_(sync), |
226 | monitor_(monitor), |
227 | done_count_(done_count), |
228 | wait_(wait) {} |
229 | |
230 | virtual void Run() { |
231 | Thread::EnterIsolateAsHelper(isolate_, Thread::kUnknownTask); |
232 | { |
233 | Thread* thread = Thread::Current(); |
234 | *thread_ptr_ = thread; |
235 | CreateStackZones(id_); |
236 | } |
237 | Thread::ExitIsolateAsHelper(); |
238 | // Notify the main thread that this thread has exited. |
239 | { |
240 | MonitorLocker ml(monitor_); |
241 | *done_count_ += 1; |
242 | ml.Notify(); |
243 | } |
244 | } |
245 | |
246 | private: |
247 | void CreateStackZones(intptr_t num) { |
248 | Thread* thread = Thread::Current(); |
249 | *thread_ptr_ = thread; |
250 | |
251 | StackZone stack_zone(thread); |
252 | HANDLESCOPE(thread); |
253 | Zone* zone = thread->zone(); |
254 | EXPECT_EQ(zone, stack_zone.GetZone()); |
255 | |
256 | // Create a zone (which is also a stack resource) and exercise it a bit. |
257 | ZoneGrowableArray<bool>* a0 = new (zone) ZoneGrowableArray<bool>(zone, 1); |
258 | GrowableArray<bool> a1(zone, 1); |
259 | for (intptr_t i = 0; i < 1000 * num + id_; ++i) { |
260 | a0->Add(true); |
261 | a1.Add(true); |
262 | } |
263 | |
264 | num -= 1; |
265 | if (num != 0) { |
266 | CreateStackZones(num); |
267 | return; |
268 | } |
269 | { |
270 | // Let the main thread know we're done with memory ops on this thread. |
271 | MonitorLocker ml(monitor_); |
272 | *done_count_ += 1; |
273 | ml.Notify(); |
274 | } |
275 | // Wait for the go-ahead from the main thread to exit. |
276 | { |
277 | MonitorLocker sync_ml(sync_); |
278 | while (*wait_) { |
279 | sync_ml.Wait(); |
280 | } |
281 | } |
282 | } |
283 | |
284 | intptr_t id_; |
285 | Isolate* isolate_; |
286 | Thread** thread_ptr_; |
287 | Monitor* sync_; |
288 | Monitor* monitor_; |
289 | intptr_t* done_count_; |
290 | bool* wait_; |
291 | }; |
292 | |
293 | ISOLATE_UNIT_TEST_CASE(ManySimpleTasksWithZones) { |
294 | const int kTaskCount = 10; |
295 | Monitor monitor; |
296 | Monitor sync; |
297 | Thread* threads[kTaskCount]; |
298 | Isolate* isolate = Thread::Current()->isolate(); |
299 | intptr_t done_count = 0; |
300 | bool wait = true; |
301 | |
302 | EXPECT(isolate->heap()->GrowthControlState()); |
303 | isolate->heap()->DisableGrowthControl(); |
304 | for (intptr_t i = 0; i < kTaskCount; i++) { |
305 | Dart::thread_pool()->Run<SimpleTaskWithZoneAllocation>( |
306 | (i + 1), isolate, &threads[i], &sync, &monitor, &done_count, &wait); |
307 | } |
308 | // Wait until all spawned tasks finish their memory operations. |
309 | { |
310 | MonitorLocker ml(&monitor); |
311 | while (done_count < kTaskCount) { |
312 | ml.Wait(); |
313 | } |
314 | // Reset the done counter for use later. |
315 | done_count = 0; |
316 | } |
317 | |
318 | // Get the information for the current isolate. |
319 | // We only need to check the current isolate since all tasks are spawned |
320 | // inside this single isolate. |
321 | JSONStream stream; |
322 | isolate->PrintJSON(&stream, false); |
323 | const char* json = stream.ToCString(); |
324 | |
325 | Thread* current_thread = Thread::Current(); |
326 | |
327 | // Confirm all expected entries are in the JSON output. |
328 | for (intptr_t i = 0; i < kTaskCount; i++) { |
329 | Thread* thread = threads[i]; |
330 | StackZone stack_zone(current_thread); |
331 | Zone* current_zone = current_thread->zone(); |
332 | |
333 | // Check the thread exists and is the correct size. |
334 | char* thread_info_buf = OS::SCreate( |
335 | current_zone, |
336 | "\"type\":\"_Thread\"," |
337 | "\"id\":\"threads\\/%" Pd |
338 | "\"," |
339 | "\"kind\":\"%s\"," |
340 | "\"_zoneHighWatermark\":\"%" Pu |
341 | "\"," |
342 | "\"_zoneCapacity\":\"%" Pu "\"" , |
343 | OSThread::ThreadIdToIntPtr(thread->os_thread()->trace_id()), |
344 | Thread::TaskKindToCString(thread->task_kind()), |
345 | thread->zone_high_watermark(), thread->current_zone_capacity()); |
346 | EXPECT_SUBSTRING(thread_info_buf, json); |
347 | } |
348 | |
349 | // Unblock the tasks so they can finish. |
350 | { |
351 | MonitorLocker sync_ml(&sync); |
352 | wait = false; |
353 | sync_ml.NotifyAll(); |
354 | } |
355 | // Now wait for them all to exit before destroying the isolate. |
356 | { |
357 | MonitorLocker ml(&monitor); |
358 | while (done_count < kTaskCount) { |
359 | ml.Wait(); |
360 | } |
361 | } |
362 | } |
363 | #endif |
364 | |
365 | TEST_CASE(ThreadRegistry) { |
366 | Isolate* orig = Thread::Current()->isolate(); |
367 | Zone* orig_zone = Thread::Current()->zone(); |
368 | char* orig_str = orig_zone->PrintToString("foo" ); |
369 | Dart_ExitIsolate(); |
370 | // Create and enter a new isolate. |
371 | TestCase::CreateTestIsolate(); |
372 | Zone* zone0 = Thread::Current()->zone(); |
373 | EXPECT(zone0 != orig_zone); |
374 | Dart_ShutdownIsolate(); |
375 | // Create and enter yet another isolate. |
376 | TestCase::CreateTestIsolate(); |
377 | { |
378 | // Create a stack resource this time, and exercise it. |
379 | TransitionNativeToVM transition(Thread::Current()); |
380 | StackZone stack_zone(Thread::Current()); |
381 | Zone* zone1 = Thread::Current()->zone(); |
382 | EXPECT(zone1 != zone0); |
383 | EXPECT(zone1 != orig_zone); |
384 | } |
385 | Dart_ShutdownIsolate(); |
386 | Dart_EnterIsolate(reinterpret_cast<Dart_Isolate>(orig)); |
387 | // Original zone should be preserved. |
388 | EXPECT_EQ(orig_zone, Thread::Current()->zone()); |
389 | EXPECT_STREQ("foo" , orig_str); |
390 | } |
391 | |
392 | // A helper thread that repeatedly reads ICData |
393 | class ICDataTestTask : public ThreadPool::Task { |
394 | public: |
395 | static const intptr_t kTaskCount; |
396 | |
397 | ICDataTestTask(Isolate* isolate, |
398 | const Array& ic_datas, |
399 | Monitor* monitor, |
400 | intptr_t* exited, |
401 | std::atomic<bool>* done) |
402 | : isolate_(isolate), |
403 | ic_datas_(ic_datas), |
404 | len_(ic_datas.Length()), |
405 | monitor_(monitor), |
406 | exited_(exited), |
407 | done_(done) {} |
408 | |
409 | virtual void Run() { |
410 | Thread::EnterIsolateAsHelper(isolate_, Thread::kUnknownTask); |
411 | |
412 | Thread* thread = Thread::Current(); |
413 | |
414 | { |
415 | StackZone stack_zone(thread); |
416 | HANDLESCOPE(thread); |
417 | |
418 | ICData& ic_data = ICData::Handle(); |
419 | Array& arr = Array::Handle(); |
420 | while (true) { |
421 | for (intptr_t cnt = 0; cnt < 0x1000; cnt++) { |
422 | for (intptr_t i = 0; i < len_; i++) { |
423 | ic_data ^= ic_datas_.AtAcquire(i); |
424 | arr = ic_data.entries(); |
425 | intptr_t num_checks = arr.Length() / 3; |
426 | if (num_checks < 0 || num_checks > 5) { |
427 | OS::PrintErr("Failure: %" Pd " checks!\n" , num_checks); |
428 | abort(); |
429 | } |
430 | } |
431 | } |
432 | |
433 | if (done_->load(std::memory_order_acquire)) { |
434 | break; |
435 | } |
436 | |
437 | TransitionVMToBlocked blocked(thread); |
438 | } |
439 | } |
440 | |
441 | Thread::ExitIsolateAsHelper(); |
442 | { |
443 | MonitorLocker ml(monitor_); |
444 | ++*exited_; |
445 | ml.Notify(); |
446 | } |
447 | } |
448 | |
449 | private: |
450 | Isolate* isolate_; |
451 | const Array& ic_datas_; |
452 | const intptr_t len_; |
453 | Monitor* monitor_; |
454 | intptr_t* exited_; // # tasks that are no longer running. |
455 | std::atomic<bool>* done_; // Signal that helper threads can stop working. |
456 | }; |
457 | |
458 | static Function* CreateFunction(const char* name) { |
459 | const String& class_name = |
460 | String::Handle(Symbols::New(Thread::Current(), "ownerClass" )); |
461 | const Script& script = Script::Handle(); |
462 | const Library& lib = Library::Handle(Library::New(class_name)); |
463 | const Class& owner_class = Class::Handle( |
464 | Class::New(lib, class_name, script, TokenPosition::kNoSource)); |
465 | const String& function_name = |
466 | String::ZoneHandle(Symbols::New(Thread::Current(), name)); |
467 | Function& function = Function::ZoneHandle(Function::New( |
468 | function_name, FunctionLayout::kRegularFunction, true, false, false, |
469 | false, false, owner_class, TokenPosition::kNoSource)); |
470 | return &function; |
471 | } |
472 | |
473 | const intptr_t ICDataTestTask::kTaskCount = 1; |
474 | |
475 | // Test that checks that other threads only see a fully initialized ICData |
476 | // whenever ICData is updated. |
477 | ISOLATE_UNIT_TEST_CASE(ICDataTest) { |
478 | Isolate* isolate = thread->isolate(); |
479 | USE(isolate); |
480 | Monitor monitor; |
481 | intptr_t exited = 0; |
482 | std::atomic<bool> done = {false}; |
483 | |
484 | const intptr_t kNumICData = 0x10; |
485 | |
486 | const Array& ic_datas = Array::Handle(Array::New(kNumICData)); |
487 | ICData& ic_data = ICData::Handle(); |
488 | Function& owner = *CreateFunction("DummyFunction" ); |
489 | String& name = String::Handle(String::New("foo" )); |
490 | const Array& args_desc = |
491 | Array::Handle(ArgumentsDescriptor::NewBoxed(0, 0, Object::empty_array())); |
492 | for (intptr_t i = 0; i < kNumICData; i++) { |
493 | ic_data = ICData::New(owner, name, args_desc, /*deopt_id=*/0, |
494 | /*num_args_tested=*/1, ICData::kInstance, |
495 | Object::null_abstract_type()); |
496 | ic_datas.SetAtRelease(i, ic_data); |
497 | } |
498 | |
499 | for (int i = 0; i < ICDataTestTask::kTaskCount; i++) { |
500 | Dart::thread_pool()->Run<ICDataTestTask>(isolate, ic_datas, &monitor, |
501 | &exited, &done); |
502 | } |
503 | |
504 | for (int i = 0; i < 0x10000; i++) { |
505 | for (intptr_t i = 0; i < kNumICData; i++) { |
506 | ic_data ^= ic_datas.At(i); |
507 | if (ic_data.NumberOfChecks() < 4) { |
508 | ic_data.AddReceiverCheck(kInstanceCid + ic_data.NumberOfChecks(), owner, |
509 | 1); |
510 | } else { |
511 | ic_data = ICData::New(owner, name, args_desc, /*deopt_id=*/0, |
512 | /*num_args_tested=*/1, ICData::kInstance, |
513 | Object::null_abstract_type()); |
514 | ic_datas.SetAtRelease(i, ic_data); |
515 | } |
516 | } |
517 | } |
518 | // Ensure we looped long enough to allow all helpers to succeed and exit. |
519 | { |
520 | done.store(true, std::memory_order_release); |
521 | MonitorLocker ml(&monitor); |
522 | while (exited != ICDataTestTask::kTaskCount) { |
523 | ml.Wait(); |
524 | } |
525 | EXPECT_EQ(ICDataTestTask::kTaskCount, exited); |
526 | } |
527 | } |
528 | |
529 | // A helper thread that alternatingly cooperates and organizes |
530 | // safepoint rendezvous. At rendezvous, it explicitly visits the |
531 | // stacks looking for a specific marker (Smi) to verify that the expected |
532 | // number threads are actually visited. The task is "done" when it has |
533 | // successfully made all other tasks and the main thread rendezvous (may |
534 | // not happen in the first rendezvous, since tasks are still starting up). |
535 | class SafepointTestTask : public ThreadPool::Task { |
536 | public: |
537 | static const intptr_t kTaskCount; |
538 | |
539 | SafepointTestTask(Isolate* isolate, |
540 | Monitor* monitor, |
541 | intptr_t* expected_count, |
542 | intptr_t* total_done, |
543 | intptr_t* exited) |
544 | : isolate_(isolate), |
545 | monitor_(monitor), |
546 | expected_count_(expected_count), |
547 | total_done_(total_done), |
548 | exited_(exited), |
549 | local_done_(false) {} |
550 | |
551 | virtual void Run() { |
552 | Thread::EnterIsolateAsHelper(isolate_, Thread::kUnknownTask); |
553 | { |
554 | MonitorLocker ml(monitor_); |
555 | ++*expected_count_; |
556 | } |
557 | Thread* thread = Thread::Current(); |
558 | for (int i = reinterpret_cast<intptr_t>(thread);; ++i) { |
559 | StackZone stack_zone(thread); |
560 | Zone* zone = thread->zone(); |
561 | HANDLESCOPE(thread); |
562 | const intptr_t kUniqueSmi = 928327281; |
563 | Smi& smi = Smi::Handle(zone, Smi::New(kUniqueSmi)); |
564 | if ((i % 100) != 0) { |
565 | // Usually, we just cooperate. |
566 | TransitionVMToBlocked transition(thread); |
567 | } else { |
568 | // But occasionally, organize a rendezvous. |
569 | HeapIterationScope iteration(thread); // Establishes a safepoint. |
570 | ASSERT(thread->IsAtSafepoint()); |
571 | ObjectCounter counter(isolate_->group(), &smi); |
572 | iteration.IterateStackPointers(&counter, |
573 | ValidationPolicy::kValidateFrames); |
574 | { |
575 | MonitorLocker ml(monitor_); |
576 | EXPECT_EQ(*expected_count_, counter.count()); |
577 | } |
578 | UserTag& tag = UserTag::Handle(zone, isolate_->current_tag()); |
579 | if (tag.raw() != isolate_->default_tag()) { |
580 | String& label = String::Handle(zone, tag.label()); |
581 | EXPECT(label.Equals("foo" )); |
582 | MonitorLocker ml(monitor_); |
583 | if (*expected_count_ == kTaskCount && !local_done_) { |
584 | // Success for the first time! Remember that we are done, and |
585 | // update the total count. |
586 | local_done_ = true; |
587 | ++*total_done_; |
588 | } |
589 | } |
590 | } |
591 | // Check whether everyone is done. |
592 | { |
593 | MonitorLocker ml(monitor_); |
594 | if (*total_done_ == kTaskCount) { |
595 | // Another task might be at SafepointThreads when resuming. Ensure its |
596 | // expectation reflects reality, since we pop our handles here. |
597 | --*expected_count_; |
598 | break; |
599 | } |
600 | } |
601 | } |
602 | Thread::ExitIsolateAsHelper(); |
603 | { |
604 | MonitorLocker ml(monitor_); |
605 | ++*exited_; |
606 | ml.Notify(); |
607 | } |
608 | } |
609 | |
610 | private: |
611 | Isolate* isolate_; |
612 | Monitor* monitor_; |
613 | intptr_t* expected_count_; // # copies of kUniqueSmi we expect to visit. |
614 | intptr_t* total_done_; // # tasks that successfully safepointed once. |
615 | intptr_t* exited_; // # tasks that are no longer running. |
616 | bool local_done_; // this task has successfully safepointed >= once. |
617 | }; |
618 | |
619 | const intptr_t SafepointTestTask::kTaskCount = 5; |
620 | |
621 | // Test rendezvous of: |
622 | // - helpers in VM code, |
623 | // - main thread in pure Dart, |
624 | // organized by |
625 | // - helpers. |
626 | TEST_CASE(SafepointTestDart) { |
627 | Isolate* isolate = Thread::Current()->isolate(); |
628 | Monitor monitor; |
629 | intptr_t expected_count = 0; |
630 | intptr_t total_done = 0; |
631 | intptr_t exited = 0; |
632 | for (int i = 0; i < SafepointTestTask::kTaskCount; i++) { |
633 | Dart::thread_pool()->Run<SafepointTestTask>( |
634 | isolate, &monitor, &expected_count, &total_done, &exited); |
635 | } |
636 | // Run Dart code on the main thread long enough to allow all helpers |
637 | // to get their verification done and exit. Use a specific UserTag |
638 | // to enable the helpers to verify that the main thread is |
639 | // successfully interrupted in the pure Dart loop. |
640 | #if defined(USING_SIMULATOR) |
641 | const intptr_t kLoopCount = 12345678; |
642 | #else |
643 | const intptr_t kLoopCount = FLAG_enable_interpreter ? 12345678 : 1234567890; |
644 | #endif // defined(USING_SIMULATOR) |
645 | char buffer[1024]; |
646 | Utils::SNPrint(buffer, sizeof(buffer), |
647 | "import 'dart:developer';\n" |
648 | "int dummy = 0;\n" |
649 | "main() {\n" |
650 | " new UserTag('foo').makeCurrent();\n" |
651 | " for (dummy = 0; dummy < %" Pd |
652 | "; ++dummy) {\n" |
653 | " dummy += (dummy & 1);\n" |
654 | " }\n" |
655 | "}\n" , |
656 | kLoopCount); |
657 | Dart_Handle lib = TestCase::LoadTestScript(buffer, NULL); |
658 | EXPECT_VALID(lib); |
659 | Dart_Handle result = Dart_Invoke(lib, NewString("main" ), 0, NULL); |
660 | EXPECT_VALID(result); |
661 | // Ensure we looped long enough to allow all helpers to succeed and exit. |
662 | { |
663 | MonitorLocker ml(&monitor); |
664 | while (exited != SafepointTestTask::kTaskCount) { |
665 | ml.Wait(); |
666 | } |
667 | EXPECT_EQ(SafepointTestTask::kTaskCount, total_done); |
668 | EXPECT_EQ(SafepointTestTask::kTaskCount, exited); |
669 | } |
670 | } |
671 | |
672 | // Test rendezvous of: |
673 | // - helpers in VM code, and |
674 | // - main thread in VM code, |
675 | // organized by |
676 | // - helpers. |
677 | ISOLATE_UNIT_TEST_CASE(SafepointTestVM) { |
678 | Isolate* isolate = thread->isolate(); |
679 | Monitor monitor; |
680 | intptr_t expected_count = 0; |
681 | intptr_t total_done = 0; |
682 | intptr_t exited = 0; |
683 | for (int i = 0; i < SafepointTestTask::kTaskCount; i++) { |
684 | Dart::thread_pool()->Run<SafepointTestTask>( |
685 | isolate, &monitor, &expected_count, &total_done, &exited); |
686 | } |
687 | String& label = String::Handle(String::New("foo" )); |
688 | UserTag& tag = UserTag::Handle(UserTag::New(label)); |
689 | isolate->set_current_tag(tag); |
690 | MonitorLocker ml(&monitor); |
691 | while (exited != SafepointTestTask::kTaskCount) { |
692 | ml.WaitWithSafepointCheck(thread); |
693 | } |
694 | } |
695 | |
696 | // Test case for recursive safepoint operations. |
697 | ISOLATE_UNIT_TEST_CASE(RecursiveSafepointTest1) { |
698 | intptr_t count = 0; |
699 | { |
700 | SafepointOperationScope safepoint_scope(thread); |
701 | count += 1; |
702 | { |
703 | SafepointOperationScope safepoint_scope(thread); |
704 | count += 1; |
705 | { |
706 | SafepointOperationScope safepoint_scope(thread); |
707 | count += 1; |
708 | } |
709 | } |
710 | } |
711 | EXPECT(count == 3); |
712 | } |
713 | |
714 | ISOLATE_UNIT_TEST_CASE(ThreadIterator_Count) { |
715 | intptr_t thread_count_0 = 0; |
716 | intptr_t thread_count_1 = 0; |
717 | |
718 | { |
719 | OSThreadIterator ti; |
720 | while (ti.HasNext()) { |
721 | OSThread* thread = ti.Next(); |
722 | EXPECT(thread != NULL); |
723 | thread_count_0++; |
724 | } |
725 | } |
726 | |
727 | { |
728 | OSThreadIterator ti; |
729 | while (ti.HasNext()) { |
730 | OSThread* thread = ti.Next(); |
731 | EXPECT(thread != NULL); |
732 | thread_count_1++; |
733 | } |
734 | } |
735 | |
736 | EXPECT(thread_count_0 > 0); |
737 | EXPECT(thread_count_1 > 0); |
738 | EXPECT(thread_count_0 >= thread_count_1); |
739 | } |
740 | |
741 | ISOLATE_UNIT_TEST_CASE(ThreadIterator_FindSelf) { |
742 | OSThread* current = OSThread::Current(); |
743 | EXPECT(OSThread::IsThreadInList(current->id())); |
744 | } |
745 | |
746 | struct ThreadIteratorTestParams { |
747 | ThreadId spawned_thread_id; |
748 | ThreadJoinId spawned_thread_join_id; |
749 | Monitor* monitor; |
750 | }; |
751 | |
752 | void ThreadIteratorTestMain(uword parameter) { |
753 | ThreadIteratorTestParams* params = |
754 | reinterpret_cast<ThreadIteratorTestParams*>(parameter); |
755 | OSThread* thread = OSThread::Current(); |
756 | EXPECT(thread != NULL); |
757 | |
758 | MonitorLocker ml(params->monitor); |
759 | params->spawned_thread_id = thread->id(); |
760 | params->spawned_thread_join_id = OSThread::GetCurrentThreadJoinId(thread); |
761 | EXPECT(params->spawned_thread_id != OSThread::kInvalidThreadId); |
762 | EXPECT(OSThread::IsThreadInList(thread->id())); |
763 | ml.Notify(); |
764 | } |
765 | |
766 | // NOTE: This test case also verifies that known TLS destructors are called |
767 | // on Windows. See |OnDartThreadExit| in os_thread_win.cc for more details. |
768 | TEST_CASE(ThreadIterator_AddFindRemove) { |
769 | ThreadIteratorTestParams params; |
770 | params.spawned_thread_id = OSThread::kInvalidThreadId; |
771 | params.monitor = new Monitor(); |
772 | |
773 | { |
774 | MonitorLocker ml(params.monitor); |
775 | EXPECT(params.spawned_thread_id == OSThread::kInvalidThreadId); |
776 | // Spawn thread and wait to receive the thread id. |
777 | OSThread::Start("ThreadIteratorTest" , ThreadIteratorTestMain, |
778 | reinterpret_cast<uword>(¶ms)); |
779 | while (params.spawned_thread_id == OSThread::kInvalidThreadId) { |
780 | ml.Wait(); |
781 | } |
782 | EXPECT(params.spawned_thread_id != OSThread::kInvalidThreadId); |
783 | EXPECT(params.spawned_thread_join_id != OSThread::kInvalidThreadJoinId); |
784 | OSThread::Join(params.spawned_thread_join_id); |
785 | } |
786 | |
787 | EXPECT(!OSThread::IsThreadInList(params.spawned_thread_id)) |
788 | |
789 | delete params.monitor; |
790 | } |
791 | |
792 | // Test rendezvous of: |
793 | // - helpers in VM code, and |
794 | // - main thread in VM code, |
795 | // organized by |
796 | // - main thread, and |
797 | // - helpers. |
798 | ISOLATE_UNIT_TEST_CASE(SafepointTestVM2) { |
799 | Isolate* isolate = thread->isolate(); |
800 | Monitor monitor; |
801 | intptr_t expected_count = 0; |
802 | intptr_t total_done = 0; |
803 | intptr_t exited = 0; |
804 | for (int i = 0; i < SafepointTestTask::kTaskCount; i++) { |
805 | Dart::thread_pool()->Run<SafepointTestTask>( |
806 | isolate, &monitor, &expected_count, &total_done, &exited); |
807 | } |
808 | bool all_helpers = false; |
809 | do { |
810 | SafepointOperationScope safepoint_scope(thread); |
811 | { |
812 | MonitorLocker ml(&monitor); |
813 | if (expected_count == SafepointTestTask::kTaskCount) { |
814 | all_helpers = true; |
815 | } |
816 | } |
817 | } while (!all_helpers); |
818 | String& label = String::Handle(String::New("foo" )); |
819 | UserTag& tag = UserTag::Handle(UserTag::New(label)); |
820 | isolate->set_current_tag(tag); |
821 | MonitorLocker ml(&monitor); |
822 | while (exited != SafepointTestTask::kTaskCount) { |
823 | ml.WaitWithSafepointCheck(thread); |
824 | } |
825 | } |
826 | |
827 | // Test recursive safepoint operation scopes with other threads trying |
828 | // to also start a safepoint operation scope. |
829 | ISOLATE_UNIT_TEST_CASE(RecursiveSafepointTest2) { |
830 | Isolate* isolate = thread->isolate(); |
831 | Monitor monitor; |
832 | intptr_t expected_count = 0; |
833 | intptr_t total_done = 0; |
834 | intptr_t exited = 0; |
835 | for (int i = 0; i < SafepointTestTask::kTaskCount; i++) { |
836 | Dart::thread_pool()->Run<SafepointTestTask>( |
837 | isolate, &monitor, &expected_count, &total_done, &exited); |
838 | } |
839 | bool all_helpers = false; |
840 | do { |
841 | SafepointOperationScope safepoint_scope(thread); |
842 | { |
843 | SafepointOperationScope safepoint_scope(thread); |
844 | MonitorLocker ml(&monitor); |
845 | if (expected_count == SafepointTestTask::kTaskCount) { |
846 | all_helpers = true; |
847 | } |
848 | } |
849 | } while (!all_helpers); |
850 | String& label = String::Handle(String::New("foo" )); |
851 | UserTag& tag = UserTag::Handle(UserTag::New(label)); |
852 | isolate->set_current_tag(tag); |
853 | bool all_exited = false; |
854 | do { |
855 | SafepointOperationScope safepoint_scope(thread); |
856 | { |
857 | SafepointOperationScope safepoint_scope(thread); |
858 | MonitorLocker ml(&monitor); |
859 | if (exited == SafepointTestTask::kTaskCount) { |
860 | all_exited = true; |
861 | } |
862 | } |
863 | } while (!all_exited); |
864 | } |
865 | |
866 | class AllocAndGCTask : public ThreadPool::Task { |
867 | public: |
868 | AllocAndGCTask(Isolate* isolate, Monitor* done_monitor, bool* done) |
869 | : isolate_(isolate), done_monitor_(done_monitor), done_(done) {} |
870 | |
871 | virtual void Run() { |
872 | Thread::EnterIsolateAsHelper(isolate_, Thread::kUnknownTask); |
873 | { |
874 | Thread* thread = Thread::Current(); |
875 | StackZone stack_zone(thread); |
876 | Zone* zone = stack_zone.GetZone(); |
877 | HANDLESCOPE(thread); |
878 | String& old_str = String::Handle(zone, String::New("old" , Heap::kOld)); |
879 | isolate_->heap()->CollectAllGarbage(); |
880 | EXPECT(old_str.Equals("old" )); |
881 | } |
882 | Thread::ExitIsolateAsHelper(); |
883 | // Tell main thread that we are ready. |
884 | { |
885 | MonitorLocker ml(done_monitor_); |
886 | ASSERT(!*done_); |
887 | *done_ = true; |
888 | ml.Notify(); |
889 | } |
890 | } |
891 | |
892 | private: |
893 | Isolate* isolate_; |
894 | Monitor* done_monitor_; |
895 | bool* done_; |
896 | }; |
897 | |
898 | ISOLATE_UNIT_TEST_CASE(HelperAllocAndGC) { |
899 | Monitor done_monitor; |
900 | bool done = false; |
901 | Isolate* isolate = thread->isolate(); |
902 | Dart::thread_pool()->Run<AllocAndGCTask>(isolate, &done_monitor, &done); |
903 | { |
904 | while (true) { |
905 | TransitionVMToBlocked transition(thread); |
906 | MonitorLocker ml(&done_monitor); |
907 | if (done) { |
908 | break; |
909 | } |
910 | } |
911 | } |
912 | } |
913 | |
914 | class AllocateGlobsOfMemoryTask : public ThreadPool::Task { |
915 | public: |
916 | AllocateGlobsOfMemoryTask(Isolate* isolate, Monitor* done_monitor, bool* done) |
917 | : isolate_(isolate), done_monitor_(done_monitor), done_(done) {} |
918 | |
919 | virtual void Run() { |
920 | Thread::EnterIsolateAsHelper(isolate_, Thread::kUnknownTask); |
921 | { |
922 | Thread* thread = Thread::Current(); |
923 | StackZone stack_zone(thread); |
924 | Zone* zone = stack_zone.GetZone(); |
925 | HANDLESCOPE(thread); |
926 | int count = 100 * 1000; |
927 | while (count-- > 0) { |
928 | String::Handle(zone, String::New("abc" )); |
929 | } |
930 | } |
931 | Thread::ExitIsolateAsHelper(); |
932 | // Tell main thread that we are ready. |
933 | { |
934 | MonitorLocker ml(done_monitor_); |
935 | ASSERT(!*done_); |
936 | *done_ = true; |
937 | ml.Notify(); |
938 | } |
939 | } |
940 | |
941 | private: |
942 | Isolate* isolate_; |
943 | Monitor* done_monitor_; |
944 | bool* done_; |
945 | }; |
946 | |
947 | ISOLATE_UNIT_TEST_CASE(ExerciseTLABs) { |
948 | const int NUMBER_TEST_THREADS = 10; |
949 | Monitor done_monitor[NUMBER_TEST_THREADS]; |
950 | bool done[NUMBER_TEST_THREADS]; |
951 | Isolate* isolate = thread->isolate(); |
952 | for (int i = 0; i < NUMBER_TEST_THREADS; i++) { |
953 | done[i] = false; |
954 | Dart::thread_pool()->Run<AllocateGlobsOfMemoryTask>( |
955 | isolate, &done_monitor[i], &done[i]); |
956 | } |
957 | |
958 | for (int i = 0; i < NUMBER_TEST_THREADS; i++) { |
959 | MonitorLocker ml(&done_monitor[i]); |
960 | while (!done[i]) { |
961 | ml.WaitWithSafepointCheck(thread); |
962 | } |
963 | } |
964 | } |
965 | |
966 | } // namespace dart |
967 | |