Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
safepoint_test.cc
Go to the documentation of this file.
1// Copyright (c) 2021, 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 <memory>
6#include <utility>
7#include <vector>
8
9#include "platform/assert.h"
10
11#include "vm/heap/safepoint.h"
12#include "vm/isolate.h"
13#include "vm/isolate_reload.h"
14#include "vm/lockers.h"
15#include "vm/message_handler.h"
16#include "vm/message_snapshot.h"
17#include "vm/random.h"
18#include "vm/thread_pool.h"
19#include "vm/unit_test.h"
20
21namespace dart {
22
24 public:
32 struct Data {
33 explicit Data(IsolateGroup* isolate_group)
34 : isolate_group_(isolate_group) {}
35
36 void WaitUntil(intptr_t target_state) {
37 MonitorLocker ml(&monitor_);
38 while (state != target_state) {
39 ml.Wait();
40 }
41 }
42 void MarkAndNotify(intptr_t target_state) {
43 MonitorLocker ml(&monitor_);
44 state = target_state;
45 ml.Notify();
46 }
47 void AssertIsIn(intptr_t expected_state) {
48 MonitorLocker ml(&monitor_);
49 EXPECT_EQ(expected_state, state);
50 }
51 void AssertIsNotIn(intptr_t expected_state) {
52 MonitorLocker ml(&monitor_);
53 EXPECT_NE(expected_state, state);
54 }
55 bool IsIn(intptr_t expected_state) {
56 MonitorLocker ml(&monitor_);
57 return expected_state == state;
58 }
59
60 intptr_t state = kInitialized;
62
63 private:
64 Monitor monitor_;
65 };
66
67 explicit StateMachineTask(std::shared_ptr<Data> data)
68 : data_(std::move(data)) {}
69
70 virtual void Run() {
71 const bool kBypassSafepoint = false;
73 Thread::kUnknownTask, kBypassSafepoint);
75 data_->MarkAndNotify(kEntered);
77 data_->WaitUntil(kPleaseExit);
78 Thread::ExitIsolateGroupAsHelper(kBypassSafepoint);
79 thread_ = nullptr;
80 data_->MarkAndNotify(kExited);
81 }
82
83 protected:
84 virtual void RunInternal() = 0;
85
86 std::shared_ptr<Data> data_;
87 Thread* thread_ = nullptr;
88};
89
91 public:
96
97 explicit DeoptTask(std::shared_ptr<Data> data)
98 : StateMachineTask(std::move(data)) {}
99
100 protected:
101 virtual void RunInternal() {
102 data_->WaitUntil(kStartDeoptOperation);
103 { DeoptSafepointOperationScope safepoint_operation(thread_); }
104 data_->MarkAndNotify(kFinishedDeoptOperation);
105 }
106};
107
109 public:
116
117 explicit GcWithoutDeoptTask(std::shared_ptr<Data> data)
118 : StateMachineTask(std::move(data)) {}
119
120 protected:
121 virtual void RunInternal() {
123 {
126 GcSafepointOperationScope safepoint_operation(thread_);
127 }
128 data_->MarkAndNotify(kEndSafepointOperation);
129
130 data_->WaitUntil(kJoinDeoptOperation);
133 data_->MarkAndNotify(kDeoptOperationDone);
134 }
135};
136
137// This test ensures that while a "deopt safepoint operation" is about to start
138// but is still waiting for some threads to hit a "deopt safepoint" another
139// safepoint operation can successfully start and finish.
141 SafepointOperation_SafepointOpWhileDeoptSafepointOpBlocked) {
142 auto isolate_group = thread->isolate_group();
143
144 std::shared_ptr<DeoptTask::Data> deopt(new DeoptTask::Data(isolate_group));
145 std::shared_ptr<GcWithoutDeoptTask::Data> gc(
146 new GcWithoutDeoptTask::Data(isolate_group));
147
148 thread->EnterSafepoint();
149 {
150 // Will join outstanding threads on destruction.
152
153 pool.Run<DeoptTask>(deopt);
154 pool.Run<GcWithoutDeoptTask>(gc);
155
156 // Wait until both threads entered the isolate group.
157 deopt->WaitUntil(DeoptTask::kEntered);
158 gc->WaitUntil(GcWithoutDeoptTask::kEntered);
159
160 // Let deopt task start deopt operation scope (it will block in
161 // [SafepointOperationScope] constructor until all threads have checked-in).
162 deopt->MarkAndNotify(DeoptTask::kStartDeoptOperation);
163 OS::Sleep(200); // Give it time to actually start the deopt operation
164
165 // Now let the other thread do a full safepoint operation and wait until
166 // it's done: We want to ensure that we can do normal safepoint operations
167 // while a deopt operation is being started and is waiting for all mutators
168 // to reach an appropriate place where they can be deopted.
171
172 // We were successfully doing a safepoint operation, now let's ensure the
173 // first thread is still stuck in the starting of deopt operation.
174 deopt->AssertIsIn(DeoptTask::kStartDeoptOperation);
175
176 // Now we'll let the other thread check-in and ensure the deopt operation
177 // proceeded and finished.
180 deopt->WaitUntil(DeoptTask::kFinishedDeoptOperation);
181
182 // Make both threads exit the isolate group.
183 deopt->MarkAndNotify(DeoptTask::kPleaseExit);
184 gc->MarkAndNotify(GcWithoutDeoptTask::kPleaseExit);
185
186 deopt->WaitUntil(DeoptTask::kExited);
187 gc->WaitUntil(GcWithoutDeoptTask::kExited);
188 }
189 thread->ExitSafepoint();
190}
191
193 public:
200
201 explicit LongDeoptTask(std::shared_ptr<Data> data)
202 : StateMachineTask(std::move(data)) {}
203
204 protected:
205 virtual void RunInternal() {
206 data_->WaitUntil(kStartDeoptOperation);
207 {
208 DeoptSafepointOperationScope safepoint_operation(thread_);
209 data_->MarkAndNotify(kInsideDeoptOperation);
210 data_->WaitUntil(kFinishDeoptOperation);
211 }
212 data_->MarkAndNotify(kFinishedDeoptOperation);
213 }
214};
215
217 public:
224
225 explicit WaiterTask(std::shared_ptr<Data> data)
226 : StateMachineTask(std::move(data)) {}
227
228 protected:
229 virtual void RunInternal() {
230 data_->WaitUntil(kEnterSafepoint);
232 data_->MarkAndNotify(kInsideSafepoint);
233 data_->WaitUntil(kPleaseExitSafepoint);
235 data_->MarkAndNotify(kExitedSafepoint);
236 }
237};
238
239// This test ensures that while a "deopt safepoint operation" is in-progress
240// other threads cannot perform a normal "safepoint operation".
242 SafepointOperation_SafepointOpBlockedWhileDeoptSafepointOp) {
243 auto isolate_group = thread->isolate_group();
244
245 std::shared_ptr<LongDeoptTask::Data> deopt(
246 new LongDeoptTask::Data(isolate_group));
247 std::shared_ptr<WaiterTask::Data> gc(new WaiterTask::Data(isolate_group));
248
249 thread->EnterSafepoint();
250 {
251 // Will join outstanding threads on destruction.
253
254 pool.Run<LongDeoptTask>(deopt);
255 pool.Run<WaiterTask>(gc);
256
257 // Wait until both threads entered the isolate group.
258 deopt->WaitUntil(LongDeoptTask::kEntered);
259 gc->WaitUntil(WaiterTask::kEntered);
260
261 // Let gc task enter safepoint.
262 gc->MarkAndNotify(WaiterTask::kEnterSafepoint);
263 gc->WaitUntil(WaiterTask::kInsideSafepoint);
264
265 // Now let the "deopt operation" run and block.
266 deopt->MarkAndNotify(LongDeoptTask::kStartDeoptOperation);
267 deopt->WaitUntil(LongDeoptTask::kInsideDeoptOperation);
268
269 // Now let the gc task try to exit safepoint and do it's own safepoint
270 // operation: We expect it to block on exiting safepoint, since the deopt
271 // operation is still ongoing.
272 gc->MarkAndNotify(WaiterTask::kPleaseExitSafepoint);
273 OS::Sleep(200);
274 gc->AssertIsNotIn(WaiterTask::kExitedSafepoint);
275
276 // Now let's finish the deopt operation & ensure the waiter thread made
277 // progress.
278 deopt->MarkAndNotify(LongDeoptTask::kFinishDeoptOperation);
279 gc->WaitUntil(WaiterTask::kExitedSafepoint);
280
281 // Make both threads exit the isolate group.
282 deopt->MarkAndNotify(LongDeoptTask::kPleaseExit);
283 gc->MarkAndNotify(WaiterTask::kPleaseExit);
284
285 deopt->WaitUntil(LongDeoptTask::kExited);
286 gc->WaitUntil(WaiterTask::kExited);
287 }
288 thread->ExitSafepoint();
289}
290
292 public:
296
297 struct Data : public StateMachineTask::Data {
298 Data(IsolateGroup* isolate_group,
300 std::atomic<intptr_t>* gc_only_checkins,
301 std::atomic<intptr_t>* deopt_checkin,
302 std::atomic<intptr_t>* reload_checkin,
303 std::atomic<intptr_t>* timeout_checkin)
304 : StateMachineTask::Data(isolate_group),
305 level(level),
310
312 std::atomic<intptr_t>* gc_only_checkins;
313 std::atomic<intptr_t>* deopt_checkin;
314 std::atomic<intptr_t>* reload_checkin;
315 std::atomic<intptr_t>* timeout_checkin;
316 };
317
318 explicit CheckinTask(std::shared_ptr<Data> data) : StateMachineTask(data) {}
319
320 protected:
321 Data* data() { return reinterpret_cast<Data*>(data_.get()); }
322
323 virtual void RunInternal() {
324 data_->WaitUntil(kStartLoop);
325
326 uword last_sync = OS::GetCurrentTimeMillis();
327 while (!data()->IsIn(kPleaseExit)) {
328 OS::SleepMicros(100); // Make test avoid consuming 100% CPU x kTaskCount.
329 switch (data()->level) {
330 case SafepointLevel::kGC: {
331 // This thread should join only GC safepoint operations.
332 RuntimeCallDeoptScope no_deopt(
334 if (SafepointIfRequested(thread_, data()->gc_only_checkins)) {
335 last_sync = OS::GetCurrentTimeMillis();
336 }
337 break;
338 }
340 // This thread should join only GC and Deopt safepoint operations.
341 if (SafepointIfRequested(thread_, data()->deopt_checkin)) {
342 last_sync = OS::GetCurrentTimeMillis();
343 }
344 break;
345 }
347 // This thread should join any safepoint operations.
348 ReloadParticipationScope allow_reload(thread_);
349 if (SafepointIfRequested(thread_, data()->reload_checkin)) {
350 last_sync = OS::GetCurrentTimeMillis();
351 }
352 break;
353 }
356 UNREACHABLE();
357 }
358
359 // If the main thread asks us to join a deopt safepoint but we are
360 // instructed to only really collaborate with GC safepoints we won't
361 // participate in the above cases (and therefore not register our
362 // check-in by increasing the checkin counts).
363 //
364 // After being quite sure to not have joined deopt safepoint if we only
365 // support GC safepoints, we will eventually comply here to make main
366 // thread continue.
367 const auto now = OS::GetCurrentTimeMillis();
368 if ((now - last_sync) > 1000) {
369 ReloadParticipationScope allow_reload(thread_);
370 if (SafepointIfRequested(thread_, data()->timeout_checkin)) {
371 last_sync = now;
372 }
373 }
374 }
375 }
376
377 bool SafepointIfRequested(Thread* thread, std::atomic<intptr_t>* checkins) {
378 if (thread->IsSafepointRequested()) {
379 // Collaborates by checking into the safepoint.
380 thread->BlockForSafepoint();
381 (*checkins)++;
382 return true;
383 }
384 return false;
385 }
386};
387
388// Test that mutators will not check-in to "deopt safepoint operations" at
389// at places where the mutator cannot depot (which is indicated by the
390// [Thread::runtime_call_deopt_ability_] value).
391#if !defined(PRODUCT)
392ISOLATE_UNIT_TEST_CASE(SafepointOperation_SafepointPointTest) {
393 auto isolate_group = thread->isolate_group();
394
395 const intptr_t kTaskCount = 6;
396 std::atomic<intptr_t> gc_only_checkins[kTaskCount];
397 std::atomic<intptr_t> deopt_checkins[kTaskCount];
398 std::atomic<intptr_t> reload_checkins[kTaskCount];
399 std::atomic<intptr_t> timeout_checkins[kTaskCount];
400 for (intptr_t i = 0; i < kTaskCount; ++i) {
401 gc_only_checkins[i] = 0;
402 deopt_checkins[i] = 0;
403 reload_checkins[i] = 0;
404 timeout_checkins[i] = 0;
405 }
406
407 auto task_to_level = [](intptr_t task_id) -> SafepointLevel {
408 switch (task_id) {
409 case 0:
410 case 1:
411 return SafepointLevel::kGC;
412 case 2:
413 case 3:
415 case 4:
416 case 5:
418 default:
419 UNREACHABLE();
420 return SafepointLevel::kGC;
421 }
422 };
423 auto wait_for_sync = [&](intptr_t syncs) {
424 while (true) {
425 bool ready = true;
426 for (intptr_t i = 0; i < kTaskCount; ++i) {
427 const intptr_t all = gc_only_checkins[i] + deopt_checkins[i] +
428 reload_checkins[i] + timeout_checkins[i];
429 if (all != syncs) {
430 ready = false;
431 break;
432 }
433 }
434 if (ready) {
435 return;
436 }
437 OS::SleepMicros(1000);
438 }
439 };
440
441 std::vector<std::shared_ptr<CheckinTask::Data>> threads;
442 for (intptr_t i = 0; i < kTaskCount; ++i) {
443 const auto level = task_to_level(i);
444 std::unique_ptr<CheckinTask::Data> data(new CheckinTask::Data(
445 isolate_group, level, &gc_only_checkins[i], &deopt_checkins[i],
446 &reload_checkins[i], &timeout_checkins[i]));
447 threads.push_back(std::move(data));
448 }
449
450 {
451 // Will join outstanding threads on destruction.
453
454 for (intptr_t i = 0; i < kTaskCount; i++) {
455 pool.Run<CheckinTask>(threads[i]);
456 }
457 for (intptr_t i = 0; i < kTaskCount; i++) {
458 threads[i]->WaitUntil(CheckinTask::kEntered);
459 }
460 for (intptr_t i = 0; i < kTaskCount; i++) {
461 threads[i]->MarkAndNotify(CheckinTask::kStartLoop);
462 }
463 {
464 { GcSafepointOperationScope safepoint_operation(thread); }
465 wait_for_sync(1); // Wait for threads to exit safepoint
466 { DeoptSafepointOperationScope safepoint_operation(thread); }
467 wait_for_sync(2); // Wait for threads to exit safepoint
468 { RELOAD_OPERATION_SCOPE(thread); }
469 wait_for_sync(3); // Wait for threads to exit safepoint
470 { GcSafepointOperationScope safepoint_operation(thread); }
471 wait_for_sync(4); // Wait for threads to exit safepoint
472 { DeoptSafepointOperationScope safepoint_operation(thread); }
473 wait_for_sync(5); // Wait for threads to exit safepoint
474 { RELOAD_OPERATION_SCOPE(thread); }
475 }
476 for (intptr_t i = 0; i < kTaskCount; i++) {
477 threads[i]->MarkAndNotify(CheckinTask::kPleaseExit);
478 }
479 for (intptr_t i = 0; i < kTaskCount; i++) {
480 threads[i]->WaitUntil(CheckinTask::kExited);
481 }
482 for (intptr_t i = 0; i < kTaskCount; ++i) {
483 switch (task_to_level(i)) {
485 EXPECT_EQ(2, gc_only_checkins[i]);
486 EXPECT_EQ(0, deopt_checkins[i]);
487 EXPECT_EQ(0, reload_checkins[i]);
488 EXPECT_EQ(4, timeout_checkins[i]);
489 break;
491 EXPECT_EQ(0, gc_only_checkins[i]);
492 EXPECT_EQ(4, deopt_checkins[i]);
493 EXPECT_EQ(0, reload_checkins[i]);
494 EXPECT_EQ(2, timeout_checkins[i]);
495 break;
497 EXPECT_EQ(0, gc_only_checkins[i]);
498 EXPECT_EQ(0, deopt_checkins[i]);
499 EXPECT_EQ(6, reload_checkins[i]);
500 EXPECT_EQ(0, timeout_checkins[i]);
501 break;
504 UNREACHABLE();
505 }
506 }
507 }
508}
509#endif // !defined(PRODUCT)
510
512 public:
516
517 explicit StressTask(std::shared_ptr<Data> data) : StateMachineTask(data) {}
518
519 protected:
520 Data* data() { return reinterpret_cast<Data*>(data_.get()); }
521
522 virtual void RunInternal() {
523 data_->WaitUntil(kStart);
524
526 while (!data()->IsIn(kPleaseExit)) {
527 const auto us = random.NextUInt32() % 3;
528 switch (random.NextUInt32() % 5) {
529 case 0: {
531 OS::SleepMicros(us);
532 break;
533 }
534 case 1: {
536 OS::SleepMicros(us);
537 break;
538 }
539 case 2: {
540 const bool kBypassSafepoint = false;
541 Thread::ExitIsolateGroupAsHelper(kBypassSafepoint);
542 OS::SleepMicros(us);
544 data_->isolate_group_, Thread::kUnknownTask, kBypassSafepoint);
546 break;
547 }
548 case 3: {
550 OS::SleepMicros(us);
552 break;
553 }
554 case 4: {
557 }
558 break;
559 }
560 }
561 }
562 }
563};
564
565ISOLATE_UNIT_TEST_CASE(SafepointOperation_StressTest) {
566 auto isolate_group = thread->isolate_group();
567
568 const intptr_t kTaskCount = 5;
569
570 std::vector<std::shared_ptr<StressTask::Data>> threads;
571 for (intptr_t i = 0; i < kTaskCount; ++i) {
572 std::unique_ptr<StressTask::Data> data(new StressTask::Data(isolate_group));
573 threads.push_back(std::move(data));
574 }
575
576 thread->EnterSafepoint();
577 {
578 // Will join outstanding threads on destruction.
580
581 for (intptr_t i = 0; i < kTaskCount; i++) {
582 pool.Run<StressTask>(threads[i]);
583 }
584 for (intptr_t i = 0; i < kTaskCount; i++) {
585 threads[i]->WaitUntil(StressTask::kEntered);
586 }
587 for (intptr_t i = 0; i < kTaskCount; i++) {
588 threads[i]->MarkAndNotify(StressTask::kStart);
589 }
590 OS::Sleep(3 * 1000);
591 for (intptr_t i = 0; i < kTaskCount; i++) {
592 threads[i]->MarkAndNotify(StressTask::kPleaseExit);
593 }
594 for (intptr_t i = 0; i < kTaskCount; i++) {
595 threads[i]->WaitUntil(StressTask::kExited);
596 }
597 }
598 thread->ExitSafepoint();
599}
600
601ISOLATE_UNIT_TEST_CASE(SafepointOperation_DeoptAndNonDeoptNesting) {
602 auto safepoint_handler = thread->isolate_group()->safepoint_handler();
603 {
604 DeoptSafepointOperationScope safepoint_scope(thread);
605 EXPECT(safepoint_handler->InnermostSafepointOperation(thread) ==
607 DeoptSafepointOperationScope safepoint_scope2(thread);
608 EXPECT(safepoint_handler->InnermostSafepointOperation(thread) ==
610 GcSafepointOperationScope safepoint_scope3(thread);
611 EXPECT(safepoint_handler->InnermostSafepointOperation(thread) ==
613 GcSafepointOperationScope safepoint_scope4(thread);
614 EXPECT(safepoint_handler->InnermostSafepointOperation(thread) ==
616 }
617 {
618 DeoptSafepointOperationScope safepoint_scope(thread);
619 EXPECT(safepoint_handler->InnermostSafepointOperation(thread) ==
621 GcSafepointOperationScope safepoint_scope2(thread);
622 EXPECT(safepoint_handler->InnermostSafepointOperation(thread) ==
624 }
625 {
626 GcSafepointOperationScope safepoint_scope1(thread);
627 EXPECT(safepoint_handler->InnermostSafepointOperation(thread) ==
629 GcSafepointOperationScope safepoint_scope2(thread);
630 EXPECT(safepoint_handler->InnermostSafepointOperation(thread) ==
632 }
633}
634
636 SafepointOperation_NonDeoptAndDeoptNesting,
637 "Crash") {
638 GcSafepointOperationScope safepoint_scope(thread);
639 DeoptSafepointOperationScope safepoint_scope2(thread);
640}
641
643 public:
644 IsolateExitScope() : saved_isolate_(Dart_CurrentIsolate()) {
646 }
648
649 private:
650 Dart_Isolate saved_isolate_;
651};
652
653ISOLATE_UNIT_TEST_CASE(ReloadScopes_Test) {
654 // Unscheduling an isolate will enter a safepoint that is reloadable.
655 {
656 TransitionVMToNative transition(thread);
657 IsolateExitScope isolate_leave_scope;
658 EXPECT(thread->IsAtSafepoint(SafepointLevel::kGCAndDeoptAndReload));
659 }
660
661 // Unscheduling an isolate with active [NoReloadScope] will enter a safepoint
662 // that is not reloadable.
663 {
664 // [NoReloadScope] only works if reload is supported.
665#if !defined(PRODUCT)
666 NoReloadScope no_reload_scope(thread);
667 TransitionVMToNative transition(thread);
668 IsolateExitScope isolate_leave_scope;
669 EXPECT(!thread->IsAtSafepoint(SafepointLevel::kGCAndDeoptAndReload));
670 EXPECT(thread->IsAtSafepoint(SafepointLevel::kGCAndDeopt));
671#endif // !defined(PRODUCT)
672 }
673
674 // Transitioning to native doesn't mean we enter a safepoint that is
675 // reloadable.
676 // => We may want to allow this in the future (so e.g. isolates that perform
677 // blocking FFI call can be reloaded while being blocked).
678 {
679 TransitionVMToNative transition(thread);
680 EXPECT(!thread->IsAtSafepoint(SafepointLevel::kGCAndDeoptAndReload));
681 EXPECT(thread->IsAtSafepoint(SafepointLevel::kGCAndDeopt));
682 }
683
684 // Transitioning to native with explicit [ReloadParticipationScope] will
685 // enter a safepoint that is reloadable.
686 {
687 ReloadParticipationScope enable_reload(thread);
688 TransitionVMToNative transition(thread);
689 EXPECT(thread->IsAtSafepoint(SafepointLevel::kGCAndDeoptAndReload));
690 }
691}
692
693#if !defined(PRODUCT)
695 public:
697
698 explicit ReloadTask(std::shared_ptr<Data> data) : StateMachineTask(data) {}
699
700 protected:
702};
703
704ISOLATE_UNIT_TEST_CASE(Reload_AtReloadSafepoint) {
705 auto isolate = thread->isolate();
706 auto messages = isolate->message_handler();
707
709
710 {
711 ReloadParticipationScope allow_reload(thread);
712
713 // We are not at a safepoint.
714 ASSERT(!thread->IsAtSafepoint());
715
716 // Enter a reload safepoint.
717 thread->EnterSafepoint();
718 {
719 // The [ReloadTask] will trigger a reload safepoint operation, sees that
720 // we are at reload safepoint & finishes without sending OOB message.
721 std::shared_ptr<ReloadTask::Data> task(
722 new ReloadTask::Data(isolate->group()));
723 pool.Run<ReloadTask>(task);
724 task->WaitUntil(ReloadTask::kEntered);
725 task->MarkAndNotify(ReloadTask::kPleaseExit);
726 task->WaitUntil(ReloadTask::kExited);
727 }
728 thread->ExitSafepoint();
729
730 EXPECT(!messages->HasOOBMessages());
731 }
732}
733
734static void EnsureValidOOBMessage(Thread* thread,
735 Isolate* isolate,
736 std::unique_ptr<Message> message) {
737 EXPECT(message->IsOOB());
738 EXPECT(message->dest_port() == isolate->main_port());
739
740 const auto& msg = Object::Handle(ReadMessage(thread, message.get()));
741 EXPECT(msg.IsArray());
742
743 const auto& array = Array::Cast(msg);
744 EXPECT(array.Length() == 3);
747 // 3rd value is ignored.
748}
749
750ISOLATE_UNIT_TEST_CASE(Reload_NotAtSafepoint) {
751 auto isolate = thread->isolate();
752 auto messages = isolate->message_handler();
753
755
756 std::shared_ptr<ReloadTask::Data> task(
757 new ReloadTask::Data(isolate->group()));
758
759 {
760 // Even if we are not running with an active isolate (e.g. due to being in
761 // GC / Compiler) the reload safepoint operation should still send us an OOB
762 // message (it should know this thread belongs to an isolate).
763 NoActiveIsolateScope no_active_isolate(thread);
764
765 pool.Run<ReloadTask>(task);
766 task->WaitUntil(ReloadTask::kEntered);
767
768 // We are not at a safepoint. The [ReloadTask] will trigger a reload
769 // safepoint operation, sees that we are not at reload safepoint and instead
770 // sends us an OOB.
771 ASSERT(!thread->IsAtSafepoint());
772 while (!messages->HasOOBMessages()) {
773 OS::Sleep(1000);
774 }
775 }
776
777 // Examine the OOB message for it's content.
778 std::unique_ptr<Message> message = messages->StealOOBMessage();
779 EnsureValidOOBMessage(thread, isolate, std::move(message));
780
781 // Finally participate in the reload safepoint and finish.
782 {
783 ReloadParticipationScope allow_reload(thread);
784 thread->BlockForSafepoint();
785 }
786
787 task->MarkAndNotify(ReloadTask::kPleaseExit);
788 task->WaitUntil(ReloadTask::kExited);
789}
790
791ISOLATE_UNIT_TEST_CASE(Reload_AtNonReloadSafepoint) {
792 auto isolate = thread->isolate();
793 auto messages = isolate->message_handler();
794
796
797 // The [ReloadTask] will trigger a reload safepoint operation, sees that
798 // we are at not at reload safepoint & sends us an OOB and waits for us to
799 // check-in.
800 std::shared_ptr<ReloadTask::Data> task(
801 new ReloadTask::Data(isolate->group()));
802 pool.Run<ReloadTask>(task);
803 task->WaitUntil(ReloadTask::kEntered);
804
805 {
806 NoReloadScope no_reload(thread);
807
808 // We are not at a safepoint.
809 ASSERT(!thread->IsAtSafepoint());
810
811 // Enter a non-reload safepoint.
812 thread->EnterSafepoint();
813 {
814 // We are at a safepoint but not a reload safepoint. So we'll get an OOM.
815 ASSERT(thread->IsAtSafepoint());
816 while (!messages->HasOOBMessages()) {
817 OS::Sleep(1000);
818 }
819 // Ensure we got a valid OOM
820 std::unique_ptr<Message> message = messages->StealOOBMessage();
821 EnsureValidOOBMessage(thread, isolate, std::move(message));
822 }
823 thread->ExitSafepoint();
824
825 EXPECT(!messages->HasOOBMessages());
826 }
827
828 // We left the [NoReloadScope] which in it's destructor should detect
829 // that a reload safepoint operation is requested and re-send OOM message to
830 // current isolate.
831 EXPECT(messages->HasOOBMessages());
832 std::unique_ptr<Message> message = messages->StealOOBMessage();
833 EnsureValidOOBMessage(thread, isolate, std::move(message));
834
835 // Finally participate in the reload safepoint and finish.
836 {
837 ReloadParticipationScope allow_reload(thread);
838 thread->BlockForSafepoint();
839 }
840
841 task->MarkAndNotify(ReloadTask::kPleaseExit);
842 task->WaitUntil(ReloadTask::kExited);
843}
844#endif // !defined(PRODUCT)
845
846} // namespace dart
AutoreleasePool pool
#define EXPECT(type, expectedAlignment, expectedSize)
#define UNREACHABLE()
Definition assert.h:248
CheckinTask(std::shared_ptr< Data > data)
bool SafepointIfRequested(Thread *thread, std::atomic< intptr_t > *checkins)
virtual void RunInternal()
DeoptTask(std::shared_ptr< Data > data)
virtual void RunInternal()
GcWithoutDeoptTask(std::shared_ptr< Data > data)
Random * random()
Definition isolate.h:410
Dart_Port main_port() const
Definition isolate.h:1001
virtual void RunInternal()
LongDeoptTask(std::shared_ptr< Data > data)
@ kIsolateLibOOBMsg
Definition message.h:42
Monitor::WaitResult Wait(int64_t millis=Monitor::kNoTimeout)
Definition lockers.h:172
static void SleepMicros(int64_t micros)
static int64_t GetCurrentTimeMillis()
static void Sleep(int64_t millis)
static Object & Handle()
Definition object.h:407
static ObjectPtr RawCast(ObjectPtr obj)
Definition object.h:325
uint64_t NextUInt64()
Definition random.h:26
uint32_t NextUInt32()
Definition random.cc:73
virtual void RunInternal()
ReloadTask(std::shared_ptr< Data > data)
intptr_t Value() const
Definition object.h:9969
StateMachineTask(std::shared_ptr< Data > data)
virtual void RunInternal()=0
std::shared_ptr< Data > data_
StressTask(std::shared_ptr< Data > data)
virtual void RunInternal()
static Thread * Current()
Definition thread.h:361
bool IsSafepointRequested() const
Definition thread.h:924
void ExitSafepoint()
Definition thread.h:1081
static void ExitIsolateGroupAsHelper(bool bypass_safepoint)
Definition thread.cc:494
void EnterSafepoint()
Definition thread.h:1063
IsolateGroup * isolate_group() const
Definition thread.h:540
void BlockForSafepoint()
Definition thread.cc:1282
static bool EnterIsolateGroupAsHelper(IsolateGroup *isolate_group, TaskKind kind, bool bypass_safepoint)
Definition thread.cc:476
virtual void RunInternal()
WaiterTask(std::shared_ptr< Data > data)
struct _Dart_Isolate * Dart_Isolate
Definition dart_api.h:88
#define ASSERT(E)
#define RELOAD_OPERATION_SCOPE(thread_expr)
Win32Message message
SafepointLevel
Definition thread.h:289
@ kGC
Definition thread.h:291
@ kNumLevels
Definition thread.h:297
@ kNoSafepoint
Definition thread.h:300
@ kGCAndDeoptAndReload
Definition thread.h:295
@ kGCAndDeopt
Definition thread.h:293
ObjectPtr ReadMessage(Thread *thread, Message *message)
DART_EXPORT void Dart_EnterIsolate(Dart_Isolate isolate)
uintptr_t uword
Definition globals.h:501
DART_EXPORT Dart_Isolate Dart_CurrentIsolate()
static void EnsureValidOOBMessage(Thread *thread, Isolate *isolate, std::unique_ptr< Message > message)
DART_EXPORT void Dart_ExitIsolate()
static int8_t data[kExtLength]
Definition ref_ptr.h:256
std::atomic< intptr_t > * timeout_checkin
Data(IsolateGroup *isolate_group, SafepointLevel level, std::atomic< intptr_t > *gc_only_checkins, std::atomic< intptr_t > *deopt_checkin, std::atomic< intptr_t > *reload_checkin, std::atomic< intptr_t > *timeout_checkin)
std::atomic< intptr_t > * deopt_checkin
std::atomic< intptr_t > * gc_only_checkins
std::atomic< intptr_t > * reload_checkin
void AssertIsNotIn(intptr_t expected_state)
void AssertIsIn(intptr_t expected_state)
void MarkAndNotify(intptr_t target_state)
void WaitUntil(intptr_t target_state)
bool IsIn(intptr_t expected_state)
Data(IsolateGroup *isolate_group)
#define ISOLATE_UNIT_TEST_CASE(name)
Definition unit_test.h:64
#define ISOLATE_UNIT_TEST_CASE_WITH_EXPECTATION(name, expectation)
Definition unit_test.h:51