Flutter Engine
The Flutter Engine
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:
25 enum State {
31 };
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:
92 enum State {
95 };
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 {
104 DeoptSafepointOperationScope safepoint_operation(thread_);
105 }
106 data_->MarkAndNotify(kFinishedDeoptOperation);
107 }
108};
109
111 public:
112 enum State {
117 };
118
119 explicit GcWithoutDeoptTask(std::shared_ptr<Data> data)
120 : StateMachineTask(std::move(data)) {}
121
122 protected:
123 virtual void RunInternal() {
125 {
128 GcSafepointOperationScope safepoint_operation(thread_);
129 }
130 data_->MarkAndNotify(kEndSafepointOperation);
131
132 data_->WaitUntil(kJoinDeoptOperation);
135 data_->MarkAndNotify(kDeoptOperationDone);
136 }
137};
138
139// This test ensures that while a "deopt safepoint operation" is about to start
140// but is still waiting for some threads to hit a "deopt safepoint" another
141// safepoint operation can successfully start and finish.
143 SafepointOperation_SafepointOpWhileDeoptSafepointOpBlocked) {
144 auto isolate_group = thread->isolate_group();
145
146 std::shared_ptr<DeoptTask::Data> deopt(new DeoptTask::Data(isolate_group));
147 std::shared_ptr<GcWithoutDeoptTask::Data> gc(
148 new GcWithoutDeoptTask::Data(isolate_group));
149
150 thread->EnterSafepoint();
151 {
152 // Will join outstanding threads on destruction.
154
155 pool.Run<DeoptTask>(deopt);
157
158 // Wait until both threads entered the isolate group.
159 deopt->WaitUntil(DeoptTask::kEntered);
161
162 // Let deopt task start deopt operation scope (it will block in
163 // [SafepointOperationScope] constructor until all threads have checked-in).
164 deopt->MarkAndNotify(DeoptTask::kStartDeoptOperation);
165 OS::Sleep(200); // Give it time to actually start the deopt operation
166
167 // Now let the other thread do a full safepoint operation and wait until
168 // it's done: We want to ensure that we can do normal safepoint operations
169 // while a deopt operation is being started and is waiting for all mutators
170 // to reach an appropriate place where they can be deopted.
173
174 // We were successfully doing a safepoint operation, now let's ensure the
175 // first thread is still stuck in the starting of deopt operation.
176 deopt->AssertIsIn(DeoptTask::kStartDeoptOperation);
177
178 // Now we'll let the other thread check-in and ensure the deopt operation
179 // proceeded and finished.
182 deopt->WaitUntil(DeoptTask::kFinishedDeoptOperation);
183
184 // Make both threads exit the isolate group.
185 deopt->MarkAndNotify(DeoptTask::kPleaseExit);
186 gc->MarkAndNotify(GcWithoutDeoptTask::kPleaseExit);
187
188 deopt->WaitUntil(DeoptTask::kExited);
190 }
191 thread->ExitSafepoint();
192}
193
195 public:
196 enum State {
201 };
202
203 explicit LongDeoptTask(std::shared_ptr<Data> data)
204 : StateMachineTask(std::move(data)) {}
205
206 protected:
207 virtual void RunInternal() {
208 data_->WaitUntil(kStartDeoptOperation);
209 {
210 DeoptSafepointOperationScope safepoint_operation(thread_);
211 data_->MarkAndNotify(kInsideDeoptOperation);
212 data_->WaitUntil(kFinishDeoptOperation);
213 }
214 data_->MarkAndNotify(kFinishedDeoptOperation);
215 }
216};
217
219 public:
220 enum State {
225 };
226
227 explicit WaiterTask(std::shared_ptr<Data> data)
228 : StateMachineTask(std::move(data)) {}
229
230 protected:
231 virtual void RunInternal() {
232 data_->WaitUntil(kEnterSafepoint);
234 data_->MarkAndNotify(kInsideSafepoint);
235 data_->WaitUntil(kPleaseExitSafepoint);
237 data_->MarkAndNotify(kExitedSafepoint);
238 }
239};
240
241// This test ensures that while a "deopt safepoint operation" is in-progress
242// other threads cannot perform a normal "safepoint operation".
244 SafepointOperation_SafepointOpBlockedWhileDeoptSafepointOp) {
245 auto isolate_group = thread->isolate_group();
246
247 std::shared_ptr<LongDeoptTask::Data> deopt(
248 new LongDeoptTask::Data(isolate_group));
249 std::shared_ptr<WaiterTask::Data> gc(new WaiterTask::Data(isolate_group));
250
251 thread->EnterSafepoint();
252 {
253 // Will join outstanding threads on destruction.
255
256 pool.Run<LongDeoptTask>(deopt);
257 pool.Run<WaiterTask>(gc);
258
259 // Wait until both threads entered the isolate group.
260 deopt->WaitUntil(LongDeoptTask::kEntered);
261 gc->WaitUntil(WaiterTask::kEntered);
262
263 // Let gc task enter safepoint.
264 gc->MarkAndNotify(WaiterTask::kEnterSafepoint);
266
267 // Now let the "deopt operation" run and block.
268 deopt->MarkAndNotify(LongDeoptTask::kStartDeoptOperation);
269 deopt->WaitUntil(LongDeoptTask::kInsideDeoptOperation);
270
271 // Now let the gc task try to exit safepoint and do it's own safepoint
272 // operation: We expect it to block on exiting safepoint, since the deopt
273 // operation is still ongoing.
274 gc->MarkAndNotify(WaiterTask::kPleaseExitSafepoint);
275 OS::Sleep(200);
276 gc->AssertIsNotIn(WaiterTask::kExitedSafepoint);
277
278 // Now let's finish the deopt operation & ensure the waiter thread made
279 // progress.
280 deopt->MarkAndNotify(LongDeoptTask::kFinishDeoptOperation);
282
283 // Make both threads exit the isolate group.
284 deopt->MarkAndNotify(LongDeoptTask::kPleaseExit);
285 gc->MarkAndNotify(WaiterTask::kPleaseExit);
286
287 deopt->WaitUntil(LongDeoptTask::kExited);
288 gc->WaitUntil(WaiterTask::kExited);
289 }
290 thread->ExitSafepoint();
291}
292
294 public:
295 enum State {
297 };
298
299 struct Data : public StateMachineTask::Data {
300 Data(IsolateGroup* isolate_group,
302 std::atomic<intptr_t>* gc_only_checkins,
303 std::atomic<intptr_t>* deopt_checkin,
304 std::atomic<intptr_t>* reload_checkin,
305 std::atomic<intptr_t>* timeout_checkin)
306 : StateMachineTask::Data(isolate_group),
307 level(level),
312
314 std::atomic<intptr_t>* gc_only_checkins;
315 std::atomic<intptr_t>* deopt_checkin;
316 std::atomic<intptr_t>* reload_checkin;
317 std::atomic<intptr_t>* timeout_checkin;
318 };
319
320 explicit CheckinTask(std::shared_ptr<Data> data) : StateMachineTask(data) {}
321
322 protected:
323 Data* data() { return reinterpret_cast<Data*>(data_.get()); }
324
325 virtual void RunInternal() {
326 data_->WaitUntil(kStartLoop);
327
328 uword last_sync = OS::GetCurrentTimeMillis();
329 while (!data()->IsIn(kPleaseExit)) {
330 OS::SleepMicros(100); // Make test avoid consuming 100% CPU x kTaskCount.
331 switch (data()->level) {
332 case SafepointLevel::kGC: {
333 // This thread should join only GC safepoint operations.
334 RuntimeCallDeoptScope no_deopt(
336 if (SafepointIfRequested(thread_, data()->gc_only_checkins)) {
337 last_sync = OS::GetCurrentTimeMillis();
338 }
339 break;
340 }
342 // This thread should join only GC and Deopt safepoint operations.
343 if (SafepointIfRequested(thread_, data()->deopt_checkin)) {
344 last_sync = OS::GetCurrentTimeMillis();
345 }
346 break;
347 }
349 // This thread should join any safepoint operations.
350 ReloadParticipationScope allow_reload(thread_);
351 if (SafepointIfRequested(thread_, data()->reload_checkin)) {
352 last_sync = OS::GetCurrentTimeMillis();
353 }
354 break;
355 }
358 UNREACHABLE();
359 }
360
361 // If the main thread asks us to join a deopt safepoint but we are
362 // instructed to only really collaborate with GC safepoints we won't
363 // participate in the above cases (and therefore not register our
364 // check-in by increasing the checkin counts).
365 //
366 // After being quite sure to not have joined deopt safepoint if we only
367 // support GC safepoints, we will eventually comply here to make main
368 // thread continue.
369 const auto now = OS::GetCurrentTimeMillis();
370 if ((now - last_sync) > 1000) {
371 ReloadParticipationScope allow_reload(thread_);
372 if (SafepointIfRequested(thread_, data()->timeout_checkin)) {
373 last_sync = now;
374 }
375 }
376 }
377 }
378
379 bool SafepointIfRequested(Thread* thread, std::atomic<intptr_t>* checkins) {
380 if (thread->IsSafepointRequested()) {
381 // Collaborates by checking into the safepoint.
382 thread->BlockForSafepoint();
383 (*checkins)++;
384 return true;
385 }
386 return false;
387 }
388};
389
390// Test that mutators will not check-in to "deopt safepoint operations" at
391// at places where the mutator cannot depot (which is indicated by the
392// [Thread::runtime_call_deopt_ability_] value).
393#if !defined(PRODUCT)
394ISOLATE_UNIT_TEST_CASE(SafepointOperation_SafepointPointTest) {
395 auto isolate_group = thread->isolate_group();
396
397 const intptr_t kTaskCount = 6;
398 std::atomic<intptr_t> gc_only_checkins[kTaskCount];
399 std::atomic<intptr_t> deopt_checkins[kTaskCount];
400 std::atomic<intptr_t> reload_checkins[kTaskCount];
401 std::atomic<intptr_t> timeout_checkins[kTaskCount];
402 for (intptr_t i = 0; i < kTaskCount; ++i) {
403 gc_only_checkins[i] = 0;
404 deopt_checkins[i] = 0;
405 reload_checkins[i] = 0;
406 timeout_checkins[i] = 0;
407 }
408
409 auto task_to_level = [](intptr_t task_id) -> SafepointLevel {
410 switch (task_id) {
411 case 0:
412 case 1:
413 return SafepointLevel::kGC;
414 case 2:
415 case 3:
417 case 4:
418 case 5:
420 default:
421 UNREACHABLE();
422 return SafepointLevel::kGC;
423 }
424 };
425 auto wait_for_sync = [&](intptr_t syncs) {
426 while (true) {
427 bool ready = true;
428 for (intptr_t i = 0; i < kTaskCount; ++i) {
429 const intptr_t all = gc_only_checkins[i] + deopt_checkins[i] +
430 reload_checkins[i] + timeout_checkins[i];
431 if (all != syncs) {
432 ready = false;
433 break;
434 }
435 }
436 if (ready) {
437 return;
438 }
439 OS::SleepMicros(1000);
440 }
441 };
442
443 std::vector<std::shared_ptr<CheckinTask::Data>> threads;
444 for (intptr_t i = 0; i < kTaskCount; ++i) {
445 const auto level = task_to_level(i);
446 std::unique_ptr<CheckinTask::Data> data(new CheckinTask::Data(
447 isolate_group, level, &gc_only_checkins[i], &deopt_checkins[i],
448 &reload_checkins[i], &timeout_checkins[i]));
449 threads.push_back(std::move(data));
450 }
451
452 {
453 // Will join outstanding threads on destruction.
455
456 for (intptr_t i = 0; i < kTaskCount; i++) {
457 pool.Run<CheckinTask>(threads[i]);
458 }
459 for (intptr_t i = 0; i < kTaskCount; i++) {
460 threads[i]->WaitUntil(CheckinTask::kEntered);
461 }
462 for (intptr_t i = 0; i < kTaskCount; i++) {
463 threads[i]->MarkAndNotify(CheckinTask::kStartLoop);
464 }
465 {
466 {
467 GcSafepointOperationScope safepoint_operation(thread);
468 }
469 wait_for_sync(1); // Wait for threads to exit safepoint
470 {
471 DeoptSafepointOperationScope safepoint_operation(thread);
472 }
473 wait_for_sync(2); // Wait for threads to exit safepoint
474 {
476 }
477 wait_for_sync(3); // Wait for threads to exit safepoint
478 {
479 GcSafepointOperationScope safepoint_operation(thread);
480 }
481 wait_for_sync(4); // Wait for threads to exit safepoint
482 {
483 DeoptSafepointOperationScope safepoint_operation(thread);
484 }
485 wait_for_sync(5); // Wait for threads to exit safepoint
486 {
488 }
489 }
490 for (intptr_t i = 0; i < kTaskCount; i++) {
491 threads[i]->MarkAndNotify(CheckinTask::kPleaseExit);
492 }
493 for (intptr_t i = 0; i < kTaskCount; i++) {
494 threads[i]->WaitUntil(CheckinTask::kExited);
495 }
496 for (intptr_t i = 0; i < kTaskCount; ++i) {
497 switch (task_to_level(i)) {
499 EXPECT_EQ(2, gc_only_checkins[i]);
500 EXPECT_EQ(0, deopt_checkins[i]);
501 EXPECT_EQ(0, reload_checkins[i]);
502 EXPECT_EQ(4, timeout_checkins[i]);
503 break;
505 EXPECT_EQ(0, gc_only_checkins[i]);
506 EXPECT_EQ(4, deopt_checkins[i]);
507 EXPECT_EQ(0, reload_checkins[i]);
508 EXPECT_EQ(2, timeout_checkins[i]);
509 break;
511 EXPECT_EQ(0, gc_only_checkins[i]);
512 EXPECT_EQ(0, deopt_checkins[i]);
513 EXPECT_EQ(6, reload_checkins[i]);
514 EXPECT_EQ(0, timeout_checkins[i]);
515 break;
518 UNREACHABLE();
519 }
520 }
521 }
522}
523#endif // !defined(PRODUCT)
524
526 public:
527 enum State {
529 };
530
531 explicit StressTask(std::shared_ptr<Data> data) : StateMachineTask(data) {}
532
533 protected:
534 Data* data() { return reinterpret_cast<Data*>(data_.get()); }
535
536 virtual void RunInternal() {
537 data_->WaitUntil(kStart);
538
540 while (!data()->IsIn(kPleaseExit)) {
541 const auto us = random.NextUInt32() % 3;
542 switch (random.NextUInt32() % 5) {
543 case 0: {
545 OS::SleepMicros(us);
546 break;
547 }
548 case 1: {
550 OS::SleepMicros(us);
551 break;
552 }
553 case 2: {
554 const bool kBypassSafepoint = false;
555 Thread::ExitIsolateGroupAsHelper(kBypassSafepoint);
556 OS::SleepMicros(us);
558 data_->isolate_group_, Thread::kUnknownTask, kBypassSafepoint);
560 break;
561 }
562 case 3: {
564 OS::SleepMicros(us);
566 break;
567 }
568 case 4: {
571 }
572 break;
573 }
574 }
575 }
576 }
577};
578
579ISOLATE_UNIT_TEST_CASE(SafepointOperation_StressTest) {
580 auto isolate_group = thread->isolate_group();
581
582 const intptr_t kTaskCount = 5;
583
584 std::vector<std::shared_ptr<StressTask::Data>> threads;
585 for (intptr_t i = 0; i < kTaskCount; ++i) {
586 std::unique_ptr<StressTask::Data> data(new StressTask::Data(isolate_group));
587 threads.push_back(std::move(data));
588 }
589
590 thread->EnterSafepoint();
591 {
592 // Will join outstanding threads on destruction.
594
595 for (intptr_t i = 0; i < kTaskCount; i++) {
596 pool.Run<StressTask>(threads[i]);
597 }
598 for (intptr_t i = 0; i < kTaskCount; i++) {
599 threads[i]->WaitUntil(StressTask::kEntered);
600 }
601 for (intptr_t i = 0; i < kTaskCount; i++) {
602 threads[i]->MarkAndNotify(StressTask::kStart);
603 }
604 OS::Sleep(3 * 1000);
605 for (intptr_t i = 0; i < kTaskCount; i++) {
606 threads[i]->MarkAndNotify(StressTask::kPleaseExit);
607 }
608 for (intptr_t i = 0; i < kTaskCount; i++) {
609 threads[i]->WaitUntil(StressTask::kExited);
610 }
611 }
612 thread->ExitSafepoint();
613}
614
615ISOLATE_UNIT_TEST_CASE(SafepointOperation_DeoptAndNonDeoptNesting) {
616 auto safepoint_handler = thread->isolate_group()->safepoint_handler();
617 {
618 DeoptSafepointOperationScope safepoint_scope(thread);
619 EXPECT(safepoint_handler->InnermostSafepointOperation(thread) ==
621 DeoptSafepointOperationScope safepoint_scope2(thread);
622 EXPECT(safepoint_handler->InnermostSafepointOperation(thread) ==
624 GcSafepointOperationScope safepoint_scope3(thread);
625 EXPECT(safepoint_handler->InnermostSafepointOperation(thread) ==
627 GcSafepointOperationScope safepoint_scope4(thread);
628 EXPECT(safepoint_handler->InnermostSafepointOperation(thread) ==
630 }
631 {
632 DeoptSafepointOperationScope safepoint_scope(thread);
633 EXPECT(safepoint_handler->InnermostSafepointOperation(thread) ==
635 GcSafepointOperationScope safepoint_scope2(thread);
636 EXPECT(safepoint_handler->InnermostSafepointOperation(thread) ==
638 }
639 {
640 GcSafepointOperationScope safepoint_scope1(thread);
641 EXPECT(safepoint_handler->InnermostSafepointOperation(thread) ==
643 GcSafepointOperationScope safepoint_scope2(thread);
644 EXPECT(safepoint_handler->InnermostSafepointOperation(thread) ==
646 }
647}
648
650 SafepointOperation_NonDeoptAndDeoptNesting,
651 "Crash") {
652 GcSafepointOperationScope safepoint_scope(thread);
653 DeoptSafepointOperationScope safepoint_scope2(thread);
654}
655
657 public:
658 IsolateExitScope() : saved_isolate_(Dart_CurrentIsolate()) {
660 }
662
663 private:
664 Dart_Isolate saved_isolate_;
665};
666
667ISOLATE_UNIT_TEST_CASE(ReloadScopes_Test) {
668 // Unscheduling an isolate will enter a safepoint that is reloadable.
669 {
670 TransitionVMToNative transition(thread);
671 IsolateExitScope isolate_leave_scope;
672 EXPECT(thread->IsAtSafepoint(SafepointLevel::kGCAndDeoptAndReload));
673 }
674
675 // Unscheduling an isolate with active [NoReloadScope] will enter a safepoint
676 // that is not reloadable.
677 {
678 // [NoReloadScope] only works if reload is supported.
679#if !defined(PRODUCT)
680 NoReloadScope no_reload_scope(thread);
681 TransitionVMToNative transition(thread);
682 IsolateExitScope isolate_leave_scope;
683 EXPECT(!thread->IsAtSafepoint(SafepointLevel::kGCAndDeoptAndReload));
684 EXPECT(thread->IsAtSafepoint(SafepointLevel::kGCAndDeopt));
685#endif // !defined(PRODUCT)
686 }
687
688 // Transitioning to native doesn't mean we enter a safepoint that is
689 // reloadable.
690 // => We may want to allow this in the future (so e.g. isolates that perform
691 // blocking FFI call can be reloaded while being blocked).
692 {
693 TransitionVMToNative transition(thread);
694 EXPECT(!thread->IsAtSafepoint(SafepointLevel::kGCAndDeoptAndReload));
695 EXPECT(thread->IsAtSafepoint(SafepointLevel::kGCAndDeopt));
696 }
697
698 // Transitioning to native with explicit [ReloadParticipationScope] will
699 // enter a safepoint that is reloadable.
700 {
701 ReloadParticipationScope enable_reload(thread);
702 TransitionVMToNative transition(thread);
703 EXPECT(thread->IsAtSafepoint(SafepointLevel::kGCAndDeoptAndReload));
704 }
705}
706
707#if !defined(PRODUCT)
709 public:
711
712 explicit ReloadTask(std::shared_ptr<Data> data) : StateMachineTask(data) {}
713
714 protected:
716};
717
718ISOLATE_UNIT_TEST_CASE(Reload_AtReloadSafepoint) {
719 auto isolate = thread->isolate();
720 auto messages = isolate->message_handler();
721
723
724 {
725 ReloadParticipationScope allow_reload(thread);
726
727 // We are not at a safepoint.
728 ASSERT(!thread->IsAtSafepoint());
729
730 // Enter a reload safepoint.
731 thread->EnterSafepoint();
732 {
733 // The [ReloadTask] will trigger a reload safepoint operation, sees that
734 // we are at reload safepoint & finishes without sending OOB message.
735 std::shared_ptr<ReloadTask::Data> task(
736 new ReloadTask::Data(isolate->group()));
737 pool.Run<ReloadTask>(task);
738 task->WaitUntil(ReloadTask::kEntered);
739 task->MarkAndNotify(ReloadTask::kPleaseExit);
740 task->WaitUntil(ReloadTask::kExited);
741 }
742 thread->ExitSafepoint();
743
744 EXPECT(!messages->HasOOBMessages());
745 }
746}
747
748static void EnsureValidOOBMessage(Thread* thread,
749 Isolate* isolate,
750 std::unique_ptr<Message> message) {
751 EXPECT(message->IsOOB());
752 EXPECT(message->dest_port() == isolate->main_port());
753
754 const auto& msg = Object::Handle(ReadMessage(thread, message.get()));
755 EXPECT(msg.IsArray());
756
757 const auto& array = Array::Cast(msg);
758 EXPECT(array.Length() == 3);
761 // 3rd value is ignored.
762}
763
764ISOLATE_UNIT_TEST_CASE(Reload_NotAtSafepoint) {
765 auto isolate = thread->isolate();
766 auto messages = isolate->message_handler();
767
769
770 std::shared_ptr<ReloadTask::Data> task(
771 new ReloadTask::Data(isolate->group()));
772
773 {
774 // Even if we are not running with an active isolate (e.g. due to being in
775 // GC / Compiler) the reload safepoint operation should still send us an OOB
776 // message (it should know this thread belongs to an isolate).
777 NoActiveIsolateScope no_active_isolate(thread);
778
779 pool.Run<ReloadTask>(task);
780 task->WaitUntil(ReloadTask::kEntered);
781
782 // We are not at a safepoint. The [ReloadTask] will trigger a reload
783 // safepoint operation, sees that we are not at reload safepoint and instead
784 // sends us an OOB.
785 ASSERT(!thread->IsAtSafepoint());
786 while (!messages->HasOOBMessages()) {
787 OS::Sleep(1000);
788 }
789 }
790
791 // Examine the OOB message for it's content.
792 std::unique_ptr<Message> message = messages->StealOOBMessage();
793 EnsureValidOOBMessage(thread, isolate, std::move(message));
794
795 // Finally participate in the reload safepoint and finish.
796 {
797 ReloadParticipationScope allow_reload(thread);
798 thread->BlockForSafepoint();
799 }
800
801 task->MarkAndNotify(ReloadTask::kPleaseExit);
802 task->WaitUntil(ReloadTask::kExited);
803}
804
805ISOLATE_UNIT_TEST_CASE(Reload_AtNonReloadSafepoint) {
806 auto isolate = thread->isolate();
807 auto messages = isolate->message_handler();
808
810
811 // The [ReloadTask] will trigger a reload safepoint operation, sees that
812 // we are at not at reload safepoint & sends us an OOB and waits for us to
813 // check-in.
814 std::shared_ptr<ReloadTask::Data> task(
815 new ReloadTask::Data(isolate->group()));
816 pool.Run<ReloadTask>(task);
817 task->WaitUntil(ReloadTask::kEntered);
818
819 {
820 NoReloadScope no_reload(thread);
821
822 // We are not at a safepoint.
823 ASSERT(!thread->IsAtSafepoint());
824
825 // Enter a non-reload safepoint.
826 thread->EnterSafepoint();
827 {
828 // We are at a safepoint but not a reload safepoint. So we'll get an OOM.
829 ASSERT(thread->IsAtSafepoint());
830 while (!messages->HasOOBMessages()) {
831 OS::Sleep(1000);
832 }
833 // Ensure we got a valid OOM
834 std::unique_ptr<Message> message = messages->StealOOBMessage();
835 EnsureValidOOBMessage(thread, isolate, std::move(message));
836 }
837 thread->ExitSafepoint();
838
839 EXPECT(!messages->HasOOBMessages());
840 }
841
842 // We left the [NoReloadScope] which in it's destructor should detect
843 // that a reload safepoint operation is requested and re-send OOM message to
844 // current isolate.
845 EXPECT(messages->HasOOBMessages());
846 std::unique_ptr<Message> message = messages->StealOOBMessage();
847 EnsureValidOOBMessage(thread, isolate, std::move(message));
848
849 // Finally participate in the reload safepoint and finish.
850 {
851 ReloadParticipationScope allow_reload(thread);
852 thread->BlockForSafepoint();
853 }
854
855 task->MarkAndNotify(ReloadTask::kPleaseExit);
856 task->WaitUntil(ReloadTask::kExited);
857}
858#endif // !defined(PRODUCT)
859
860} // 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:411
@ kCheckForReload
Definition: isolate.h:975
Dart_Port main_port() const
Definition: isolate.h:1048
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:9990
StateMachineTask(std::shared_ptr< Data > data)
virtual void RunInternal()=0
std::shared_ptr< Data > data_
StressTask(std::shared_ptr< Data > data)
virtual void RunInternal()
@ kUnknownTask
Definition: thread.h:346
static Thread * Current()
Definition: thread.h:362
bool IsSafepointRequested() const
Definition: thread.h:937
void ExitSafepoint()
Definition: thread.h:1094
static void ExitIsolateGroupAsHelper(bool bypass_safepoint)
Definition: thread.cc:499
void EnterSafepoint()
Definition: thread.h:1076
IsolateGroup * isolate_group() const
Definition: thread.h:541
void BlockForSafepoint()
Definition: thread.cc:1348
static bool EnterIsolateGroupAsHelper(IsolateGroup *isolate_group, TaskKind kind, bool bypass_safepoint)
Definition: thread.cc:481
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
Definition: dart_vm.cc:33
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)
ISOLATE_UNIT_TEST_CASE_WITH_EXPECTATION(SafepointOperation_NonDeoptAndDeoptNesting, "Crash")
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)
ISOLATE_UNIT_TEST_CASE(StackAllocatedDestruction)
DART_EXPORT void Dart_ExitIsolate()
static int8_t data[kExtLength]
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial gc
Definition: switches.h:238
struct PathData * Data(SkPath *path)
Definition: path_ops.cc:52
SIT bool all(const Vec< 1, T > &x)
Definition: SkVx.h:582
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)