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
15namespace dart {
16
17DECLARE_FLAG(bool, enable_interpreter);
18
19VM_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
38VM_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
83class 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
103class 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
174ISOLATE_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
213class 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
293ISOLATE_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
365TEST_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
393class 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
458static 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
473const intptr_t ICDataTestTask::kTaskCount = 1;
474
475// Test that checks that other threads only see a fully initialized ICData
476// whenever ICData is updated.
477ISOLATE_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).
535class 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
619const 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.
626TEST_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.
677ISOLATE_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.
697ISOLATE_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
714ISOLATE_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
741ISOLATE_UNIT_TEST_CASE(ThreadIterator_FindSelf) {
742 OSThread* current = OSThread::Current();
743 EXPECT(OSThread::IsThreadInList(current->id()));
744}
745
746struct ThreadIteratorTestParams {
747 ThreadId spawned_thread_id;
748 ThreadJoinId spawned_thread_join_id;
749 Monitor* monitor;
750};
751
752void 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.
768TEST_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>(&params));
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.
798ISOLATE_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.
829ISOLATE_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
866class 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
898ISOLATE_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
914class 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
947ISOLATE_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