Skip to content

Commit 3d32a60

Browse files
rmacnak-googleCommit Queue
authored andcommitted
[vm] Fix race between VM shutdown and in-flight API call.
TEST=vm/cc/DartAPI_TimelineEvents_Loop Change-Id: I4175fb50991f8bf842437507cc4bf18fc926889d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/428622 Reviewed-by: Siva Annamalai <[email protected]> Commit-Queue: Ryan Macnak <[email protected]>
1 parent ba064bd commit 3d32a60

File tree

1 file changed

+78
-43
lines changed

1 file changed

+78
-43
lines changed

runtime/vm/dart.cc

Lines changed: 78 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -103,68 +103,102 @@ class ReadOnlyHandles {
103103

104104
class DartInitializationState : public AllStatic {
105105
public:
106-
static bool SetInitializing() {
107-
ASSERT(in_use_count_.load() == 0);
108-
uint8_t expected = kUnInitialized;
109-
return state_.compare_exchange_strong(expected, kInitializing);
106+
static bool StartInit() {
107+
uword expected = PhaseField::encode(kUnInitialized) | CountField::encode(0);
108+
uword desired = PhaseField::encode(kInitializing) | CountField::encode(0);
109+
return state_.compare_exchange_strong(expected, desired,
110+
std::memory_order_acquire);
110111
}
111112

112-
static void ResetInitializing() {
113-
ASSERT(in_use_count_.load() == 0);
114-
uint8_t expected = kInitializing;
115-
bool result = state_.compare_exchange_strong(expected, kUnInitialized);
113+
static void AbandonInit() {
114+
uword expected = PhaseField::encode(kInitializing) | CountField::encode(0);
115+
uword desired = PhaseField::encode(kUnInitialized) | CountField::encode(0);
116+
bool result = state_.compare_exchange_strong(expected, desired,
117+
std::memory_order_release);
116118
ASSERT(result);
117119
}
118120

119-
static void SetInitialized() {
120-
ASSERT(in_use_count_.load() == 0);
121-
uint8_t expected = kInitializing;
122-
bool result = state_.compare_exchange_strong(expected, kInitialized);
121+
static void FinishInit() {
122+
uword expected = PhaseField::encode(kInitializing) | CountField::encode(0);
123+
uword desired = PhaseField::encode(kInitialized) | CountField::encode(0);
124+
bool result = state_.compare_exchange_strong(expected, desired,
125+
std::memory_order_release);
123126
ASSERT(result);
124127
}
125128

126-
static bool IsInitialized() { return state_.load() == kInitialized; }
127-
static bool IsShuttingDown() { return state_.load() == kCleaningup; }
128-
129-
static bool SetCleaningup() {
130-
uint8_t expected = kInitialized;
131-
return state_.compare_exchange_strong(expected, kCleaningup);
129+
static bool IsInitialized() {
130+
return PhaseField::decode(state_.load()) == kInitialized;
131+
}
132+
static bool IsShuttingDown() {
133+
return PhaseField::decode(state_.load()) == kCleaningup;
132134
}
133135

134-
static void SetUnInitialized() {
135-
while (in_use_count_.load() > 0) {
136-
OS::Sleep(1); // Sleep for 1 millis waiting for it to not be in use.
136+
static bool StartCleanup() {
137+
uword expected = state_.load(std::memory_order_acquire);
138+
uword desired;
139+
do {
140+
if (PhaseField::decode(expected) != kInitialized) {
141+
return false;
142+
}
143+
desired = PhaseField::update(kCleaningup, expected);
144+
} while (!state_.compare_exchange_weak(expected, desired,
145+
std::memory_order_relaxed));
146+
147+
while (CountField::decode(expected) != 0) {
148+
OS::Sleep(1);
149+
expected = state_.load(std::memory_order_acquire);
137150
}
138-
uint8_t expected = kCleaningup;
139-
bool result = state_.compare_exchange_strong(expected, kUnInitialized);
151+
return true;
152+
}
153+
154+
static void FinishCleanup() {
155+
uword expected = PhaseField::encode(kCleaningup) | CountField::encode(0);
156+
uword desired = PhaseField::encode(kUnInitialized) | CountField::encode(0);
157+
bool result = state_.compare_exchange_strong(expected, desired,
158+
std::memory_order_release);
140159
ASSERT(result);
141160
}
142161

143162
static bool SetInUse() {
144-
if (state_.load() != kInitialized) {
145-
return false;
146-
}
147-
in_use_count_ += 1;
163+
uword expected = state_.load(std::memory_order_relaxed);
164+
uword desired;
165+
do {
166+
if (PhaseField::decode(expected) != kInitialized) {
167+
return false;
168+
}
169+
desired = PhaseField::encode(kInitialized) |
170+
CountField::encode(CountField::decode(expected) + 1);
171+
} while (!state_.compare_exchange_weak(expected, desired,
172+
std::memory_order_relaxed));
148173
return true;
149174
}
150175

151176
static void ResetInUse() {
152-
uint8_t value = state_.load();
153-
ASSERT((value == kInitialized) || (value == kCleaningup));
154-
in_use_count_ -= 1;
177+
uword expected = state_.load(std::memory_order_relaxed);
178+
uword desired;
179+
do {
180+
ASSERT(PhaseField::decode(expected) == kInitialized ||
181+
PhaseField::decode(expected) == kCleaningup);
182+
desired = CountField::update(CountField::decode(expected) - 1, expected);
183+
} while (!state_.compare_exchange_weak(expected, desired,
184+
std::memory_order_release));
155185
}
156186

157187
private:
158-
static constexpr uint8_t kUnInitialized = 0;
159-
static constexpr uint8_t kInitializing = 1;
160-
static constexpr uint8_t kInitialized = 2;
161-
static constexpr uint8_t kCleaningup = 3;
188+
static constexpr uword kUnInitialized = 0;
189+
static constexpr uword kInitializing = 1;
190+
static constexpr uword kInitialized = 2;
191+
static constexpr uword kCleaningup = 3;
162192

163-
static std::atomic<uint8_t> state_;
164-
static std::atomic<uint64_t> in_use_count_;
193+
using PhaseField = BitField<uword, uword, 0, 2>;
194+
using CountField =
195+
BitField<uword, uword, PhaseField::kNextBit, kBitsPerWord - 2>;
196+
197+
static std::atomic<uword> state_;
165198
};
166-
std::atomic<uint8_t> DartInitializationState::state_ = {kUnInitialized};
167-
std::atomic<uint64_t> DartInitializationState::in_use_count_ = {0};
199+
200+
std::atomic<uword> DartInitializationState::state_ = {
201+
PhaseField::encode(kUnInitialized) | CountField::encode(0)};
168202

169203
#if defined(DART_PRECOMPILER) || defined(DART_PRECOMPILED_RUNTIME)
170204
static void CheckOffsets() {
@@ -505,18 +539,18 @@ char* Dart::DartInit(const Dart_InitializeParams* params) {
505539
}
506540

507541
char* Dart::Init(const Dart_InitializeParams* params) {
508-
if (!DartInitializationState::SetInitializing()) {
542+
if (!DartInitializationState::StartInit()) {
509543
return Utils::StrDup(
510544
"Bad VM initialization state, "
511545
"already initialized or "
512546
"multiple threads initializing the VM.");
513547
}
514548
char* retval = DartInit(params);
515549
if (retval != nullptr) {
516-
DartInitializationState::ResetInitializing();
550+
DartInitializationState::AbandonInit();
517551
return retval;
518552
}
519-
DartInitializationState::SetInitialized();
553+
DartInitializationState::FinishInit();
520554

521555
// The service and kernel isolates require the VM state to be initialized.
522556
// The embedder, not the VM, should trigger creation of the service and kernel
@@ -625,7 +659,7 @@ void Dart::WaitForIsolateShutdown() {
625659

626660
char* Dart::Cleanup() {
627661
ASSERT(Isolate::Current() == nullptr);
628-
if (!DartInitializationState::SetCleaningup()) {
662+
if (!DartInitializationState::StartCleanup()) {
629663
return Utils::StrDup("VM already terminated.");
630664
}
631665
ASSERT(vm_isolate_ != nullptr);
@@ -698,7 +732,6 @@ char* Dart::Cleanup() {
698732
OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Deleting thread pool\n",
699733
UptimeMillis());
700734
}
701-
DartInitializationState::SetUnInitialized();
702735

703736
NativeMessageHandler::Cleanup();
704737
PortMap::Shutdown();
@@ -794,6 +827,8 @@ char* Dart::Cleanup() {
794827
Service::SetEmbedderStreamCallbacks(nullptr, nullptr);
795828
#endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
796829
VirtualMemory::Cleanup();
830+
831+
DartInitializationState::FinishCleanup();
797832
return nullptr;
798833
}
799834

0 commit comments

Comments
 (0)