Skip to content

Commit 2c7038b

Browse files
committed
Add pending count overflow protection to TaskGroup
1 parent 028ab32 commit 2c7038b

File tree

1 file changed

+51
-0
lines changed

1 file changed

+51
-0
lines changed

stdlib/public/Concurrency/TaskGroup.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,19 @@ struct TaskGroupStatus {
467467
static const uint64_t maskDiscardingPending = 0b0011111111111111111111111111111111111111111111111111111111111111;
468468
static const uint64_t onePendingTask = 0b0000000000000000000000000000000000000000000000000000000000000001;
469469

470+
/// Depending on kind of task group, we can either support 2^31 or 2^62 pending tasks.
471+
///
472+
/// While a discarding task group's max pending count is unrealistic to be exceeded, the lower
473+
/// maximum number used in an accumulating task group has potential to be exceeded, and thus we must crash
474+
/// rather than start overflowing status if this were to happen.
475+
static uint64_t maximumPendingTasks(TaskGroupBase* group) {
476+
if (group->isAccumulatingResults()) {
477+
return maskAccumulatingPending;
478+
} else {
479+
return maskDiscardingPending;
480+
}
481+
}
482+
470483
uint64_t status;
471484

472485
bool isCancelled() {
@@ -525,6 +538,39 @@ struct TaskGroupStatus {
525538
return TaskGroupStatus{status | (cancel ? cancelled : 0)};
526539
}
527540

541+
static void reportPendingTaskOverflow(TaskGroupBase* group, TaskGroupStatus status) {
542+
char *message;
543+
swift_asprintf(
544+
&message,
545+
"error: %sTaskGroup: detected pending task count overflow, in task group %p! Status: %s",
546+
group->isDiscardingResults() ? "Discarding" : "", group, status.to_string(group).c_str());
547+
548+
if (_swift_shouldReportFatalErrorsToDebugger()) {
549+
RuntimeErrorDetails details = {
550+
.version = RuntimeErrorDetails::currentVersion,
551+
.errorType = "task-group-violation",
552+
.currentStackDescription = "TaskGroup exceeded supported pending task count",
553+
.framesToSkip = 1,
554+
};
555+
_swift_reportToDebugger(RuntimeErrorFlagFatal, message, &details);
556+
}
557+
558+
#if defined(_WIN32)
559+
#define STDERR_FILENO 2
560+
_write(STDERR_FILENO, message, strlen(message));
561+
#else
562+
write(STDERR_FILENO, message, strlen(message));
563+
#endif
564+
#if defined(__APPLE__)
565+
asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "%s", message);
566+
#elif defined(__ANDROID__)
567+
__android_log_print(ANDROID_LOG_FATAL, "SwiftRuntime", "%s", message);
568+
#endif
569+
570+
free(message);
571+
abort();
572+
}
573+
528574
/// Pretty prints the status, as follows:
529575
/// If accumulating results:
530576
/// TaskGroupStatus{ C:{cancelled} W:{waiting task} R:{ready tasks} P:{pending tasks} {binary repr} }
@@ -620,6 +666,11 @@ TaskGroupStatus TaskGroupBase::statusAddPendingTaskAssumeRelaxed(bool unconditio
620666
std::memory_order_relaxed);
621667
auto s = TaskGroupStatus{old + TaskGroupStatus::onePendingTask};
622668

669+
if (s.pendingTasks(this) == TaskGroupStatus::maximumPendingTasks(this)) {
670+
TaskGroupStatus::reportPendingTaskOverflow(this, s);
671+
llvm_unreachable(); // the above call will abort();
672+
}
673+
623674
if (!unconditionally && s.isCancelled()) {
624675
// revert that add, it was meaningless
625676
auto o = status.fetch_sub(TaskGroupStatus::onePendingTask,

0 commit comments

Comments
 (0)