|
39 | 39 | #include <mutex>
|
40 | 40 | #endif
|
41 | 41 |
|
| 42 | +#if SWIFT_STDLIB_HAS_ASL |
| 43 | +#include <asl.h> |
| 44 | +#elif defined(__ANDROID__) |
| 45 | +#include <android/log.h> |
| 46 | +#endif |
| 47 | + |
42 | 48 | #include <assert.h>
|
43 | 49 | #if SWIFT_CONCURRENCY_ENABLE_DISPATCH
|
44 | 50 | #include <dispatch/dispatch.h>
|
@@ -467,6 +473,19 @@ struct TaskGroupStatus {
|
467 | 473 | static const uint64_t maskDiscardingPending = 0b0011111111111111111111111111111111111111111111111111111111111111;
|
468 | 474 | static const uint64_t onePendingTask = 0b0000000000000000000000000000000000000000000000000000000000000001;
|
469 | 475 |
|
| 476 | + /// Depending on kind of task group, we can either support 2^31 or 2^62 pending tasks. |
| 477 | + /// |
| 478 | + /// While a discarding task group's max pending count is unrealistic to be exceeded, the lower |
| 479 | + /// maximum number used in an accumulating task group has potential to be exceeded, and thus we must crash |
| 480 | + /// rather than start overflowing status if this were to happen. |
| 481 | + static uint64_t maximumPendingTasks(TaskGroupBase* group) { |
| 482 | + if (group->isAccumulatingResults()) { |
| 483 | + return maskAccumulatingPending; |
| 484 | + } else { |
| 485 | + return maskDiscardingPending; |
| 486 | + } |
| 487 | + } |
| 488 | + |
470 | 489 | uint64_t status;
|
471 | 490 |
|
472 | 491 | bool isCancelled() {
|
@@ -525,6 +544,39 @@ struct TaskGroupStatus {
|
525 | 544 | return TaskGroupStatus{status | (cancel ? cancelled : 0)};
|
526 | 545 | }
|
527 | 546 |
|
| 547 | + static void reportPendingTaskOverflow(TaskGroupBase* group, TaskGroupStatus status) { |
| 548 | + char *message; |
| 549 | + swift_asprintf( |
| 550 | + &message, |
| 551 | + "error: %sTaskGroup: detected pending task count overflow, in task group %p! Status: %s", |
| 552 | + group->isDiscardingResults() ? "Discarding" : "", group, status.to_string(group).c_str()); |
| 553 | + |
| 554 | + if (_swift_shouldReportFatalErrorsToDebugger()) { |
| 555 | + RuntimeErrorDetails details = { |
| 556 | + .version = RuntimeErrorDetails::currentVersion, |
| 557 | + .errorType = "task-group-violation", |
| 558 | + .currentStackDescription = "TaskGroup exceeded supported pending task count", |
| 559 | + .framesToSkip = 1, |
| 560 | + }; |
| 561 | + _swift_reportToDebugger(RuntimeErrorFlagFatal, message, &details); |
| 562 | + } |
| 563 | + |
| 564 | +#if defined(_WIN32) |
| 565 | + #define STDERR_FILENO 2 |
| 566 | + _write(STDERR_FILENO, message, strlen(message)); |
| 567 | +#else |
| 568 | + write(STDERR_FILENO, message, strlen(message)); |
| 569 | +#endif |
| 570 | +#if defined(__APPLE__) |
| 571 | + asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "%s", message); |
| 572 | +#elif defined(__ANDROID__) |
| 573 | + __android_log_print(ANDROID_LOG_FATAL, "SwiftRuntime", "%s", message); |
| 574 | +#endif |
| 575 | + |
| 576 | + free(message); |
| 577 | + abort(); |
| 578 | + } |
| 579 | + |
528 | 580 | /// Pretty prints the status, as follows:
|
529 | 581 | /// If accumulating results:
|
530 | 582 | /// TaskGroupStatus{ C:{cancelled} W:{waiting task} R:{ready tasks} P:{pending tasks} {binary repr} }
|
@@ -620,6 +672,10 @@ TaskGroupStatus TaskGroupBase::statusAddPendingTaskAssumeRelaxed(bool unconditio
|
620 | 672 | std::memory_order_relaxed);
|
621 | 673 | auto s = TaskGroupStatus{old + TaskGroupStatus::onePendingTask};
|
622 | 674 |
|
| 675 | + if (s.pendingTasks(this) == TaskGroupStatus::maximumPendingTasks(this)) { |
| 676 | + TaskGroupStatus::reportPendingTaskOverflow(this, s); // this will abort() |
| 677 | + } |
| 678 | + |
623 | 679 | if (!unconditionally && s.isCancelled()) {
|
624 | 680 | // revert that add, it was meaningless
|
625 | 681 | auto o = status.fetch_sub(TaskGroupStatus::onePendingTask,
|
|
0 commit comments