Skip to content

Commit 9867b8d

Browse files
authored
Merge pull request swiftlang#146 from aciidb0mb3r/fix-cancel-race
[BuildSystem] Fix a race between build() and cancel()
2 parents 0c8b7cd + ab15025 commit 9867b8d

File tree

1 file changed

+62
-1
lines changed

1 file changed

+62
-1
lines changed

lib/BuildSystem/BuildSystemFrontend.cpp

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,15 @@ class BuildSystemFrontendExecutionQueueDelegate
232232
};
233233

234234
struct BuildSystemFrontendDelegateImpl {
235+
236+
/// The status of delegate.
237+
enum class Status {
238+
Uninitialized = 0,
239+
Initialized,
240+
InitializationFailure,
241+
Cancelled,
242+
};
243+
235244
llvm::SourceMgr& sourceMgr;
236245
const BuildSystemInvocation& invocation;
237246

@@ -248,6 +257,28 @@ struct BuildSystemFrontendDelegateImpl {
248257
const BuildSystemInvocation& invocation)
249258
: sourceMgr(sourceMgr), invocation(invocation),
250259
executionQueueDelegate(*this) {}
260+
261+
private:
262+
/// The status of the delegate.
263+
std::atomic<Status> status{Status::Uninitialized};
264+
265+
public:
266+
267+
/// Set the status of delegate to the given value.
268+
///
269+
/// It is not possible to update the status once status is set to initialization failure.
270+
void setStatus(Status newStatus) {
271+
// Disallow changing status if there was an initialization failure.
272+
if (status == Status::InitializationFailure) {
273+
return;
274+
}
275+
status = newStatus;
276+
}
277+
278+
/// Returns the current status.
279+
Status getStatus() {
280+
return status;
281+
}
251282
};
252283

253284
bool BuildSystemFrontendExecutionQueueDelegate::showVerboseOutput() const {
@@ -378,6 +409,11 @@ BuildSystemFrontendDelegate::createExecutionQueue() {
378409

379410
void BuildSystemFrontendDelegate::cancel() {
380411
auto delegateImpl = static_cast<BuildSystemFrontendDelegateImpl*>(impl);
412+
assert(delegateImpl->getStatus() != BuildSystemFrontendDelegateImpl::Status::Uninitialized);
413+
414+
// Update the status to cancelled.
415+
delegateImpl->setStatus(BuildSystemFrontendDelegateImpl::Status::Cancelled);
416+
381417
auto system = delegateImpl->system;
382418
if (system) {
383419
system->cancel();
@@ -388,6 +424,12 @@ void BuildSystemFrontendDelegate::resetForBuild() {
388424
auto impl = static_cast<BuildSystemFrontendDelegateImpl*>(this->impl);
389425

390426
impl->numFailedCommands = 0;
427+
impl->numErrors = 0;
428+
429+
// Update status back to initialized on reset.
430+
if (impl->getStatus() == BuildSystemFrontendDelegateImpl::Status::Cancelled) {
431+
impl->setStatus(BuildSystemFrontendDelegateImpl::Status::Initialized);
432+
}
391433
}
392434

393435
void BuildSystemFrontendDelegate::hadCommandFailure() {
@@ -540,9 +582,28 @@ bool BuildSystemFrontend::initialize() {
540582
}
541583

542584
bool BuildSystemFrontend::build(StringRef targetToBuild) {
585+
586+
auto delegateImpl =
587+
static_cast<BuildSystemFrontendDelegateImpl*>(delegate.impl);
588+
589+
// We expect build to be called in these states only.
590+
assert(delegateImpl->getStatus() == BuildSystemFrontendDelegateImpl::Status::Uninitialized
591+
|| delegateImpl->getStatus() == BuildSystemFrontendDelegateImpl::Status::Initialized);
592+
593+
// Set the delegate status to initialized.
594+
delegateImpl->setStatus(BuildSystemFrontendDelegateImpl::Status::Initialized);
595+
543596
// Initialize the build system, if necessary
544597
if (!buildSystem.hasValue()) {
545-
if (!initialize())
598+
if (!initialize()) {
599+
// Set status to initialization failure. It is not possible to recover from this state.
600+
delegateImpl->setStatus(BuildSystemFrontendDelegateImpl::Status::InitializationFailure);
601+
return false;
602+
}
603+
}
604+
605+
// If delegate was told to cancel while we were initializing, abort now.
606+
if (delegateImpl->getStatus() == BuildSystemFrontendDelegateImpl::Status::Cancelled) {
546607
return false;
547608
}
548609

0 commit comments

Comments
 (0)