mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-18 23:07:07 -05:00
fix(libSceJobManager): prevent wait deadlock on node exhaustion and clean up lifecycle
This commit is contained in:
1 parent
a62c724c38
commit
68f0acf4f7
2 files changed
+46
-14
No files matched your search
@@ -1,3 +1,4 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <condition_variable>
|
||||
@@ -198,7 +199,6 @@ private:
|
||||
std::uint32_t outstanding = 0;
|
||||
std::size_t cursor = 0;
|
||||
std::deque<Item> items;
|
||||
std::mutex waiters;
|
||||
std::condition_variable passed;
|
||||
Label label{};
|
||||
std::uint64_t labelSeen = 0;
|
||||
@@ -221,7 +221,6 @@ public:
|
||||
struct Manager {
|
||||
std::mutex lock;
|
||||
std::condition_variable work;
|
||||
std::condition_variable nodeFreed;
|
||||
bool running = false;
|
||||
std::uint32_t blockedWaiters = 0;
|
||||
std::uint32_t freeNodes = 0;
|
||||
@@ -251,7 +250,6 @@ bool TakeNode(Manager& manager) {
|
||||
|
||||
void ReturnNodes(Manager& manager, std::uint32_t count) {
|
||||
manager.freeNodes += count;
|
||||
manager.nodeFreed.notify_all();
|
||||
}
|
||||
|
||||
int Sequence::Latch(int code) {
|
||||
@@ -303,8 +301,8 @@ void Sequence::Advance() {
|
||||
continue;
|
||||
}
|
||||
bool* const waiter = item.passed;
|
||||
const auto retired = static_cast<std::uint32_t>(cursor) + 1;
|
||||
items.erase(items.begin(), items.begin() + static_cast<std::ptrdiff_t>(retired));
|
||||
const auto retired = static_cast<std::uint32_t>(cursor);
|
||||
items.erase(items.begin(), items.begin() + static_cast<std::ptrdiff_t>(retired + 1));
|
||||
cursor = 0;
|
||||
commands.clear();
|
||||
batchStarted = false;
|
||||
@@ -439,7 +437,8 @@ int Sequence::Wait() {
|
||||
int Sequence::WaitAndClose(std::uint32_t index) {
|
||||
std::unique_lock guard(manager->lock);
|
||||
const int result = WaitLocked(guard);
|
||||
ReturnNodes(*manager, static_cast<std::uint32_t>(items.size()) + 1);
|
||||
const auto remaining = static_cast<std::uint32_t>(std::count_if(items.begin(), items.end(), [](const Item& item) { return item.type != ItemType::Wait; }));
|
||||
ReturnNodes(*manager, remaining + 1);
|
||||
items.clear();
|
||||
commands.clear();
|
||||
cursor = 0;
|
||||
@@ -457,13 +456,7 @@ int Sequence::WaitLocked(std::unique_lock<std::mutex>& guard) {
|
||||
} else if (submitted.waits++ >= limits.waits) {
|
||||
result = Latch(SequenceLimitExceeded);
|
||||
}
|
||||
if (manager->freeNodes == 0 && result == 0) result = Latch(SequenceNoNodes);
|
||||
++manager->blockedWaiters;
|
||||
manager->nodeFreed.wait(guard, [this] { return manager->freeNodes != 0; });
|
||||
--manager->freeNodes;
|
||||
guard.unlock();
|
||||
const std::lock_guard serial(waiters);
|
||||
guard.lock();
|
||||
bool done = false;
|
||||
items.push_back({ItemType::Wait, this, nullptr, nullptr, 0, 0, 0, &done});
|
||||
Advance();
|
||||
@@ -700,10 +693,16 @@ void APS5_VABI _ZN3sce3Job10JobManagerC2Ev(void* self) {
|
||||
Implementation(self) = nullptr;
|
||||
}
|
||||
|
||||
void APS5_VABI _ZN3sce3Job10JobManagerD1Ev(void*) {
|
||||
int APS5_VABI _ZN3sce3Job10JobManager8shutdownEv(void* self);
|
||||
|
||||
void APS5_VABI _ZN3sce3Job10JobManagerD1Ev(void* self) {
|
||||
if (Implementation(self) != nullptr) {
|
||||
static_cast<void>(_ZN3sce3Job10JobManager8shutdownEv(self));
|
||||
}
|
||||
}
|
||||
|
||||
void APS5_VABI _ZN3sce3Job10JobManagerD2Ev(void*) {
|
||||
void APS5_VABI _ZN3sce3Job10JobManagerD2Ev(void* self) {
|
||||
_ZN3sce3Job10JobManagerD1Ev(self);
|
||||
}
|
||||
|
||||
std::uint64_t APS5_VABI _ZN3sce3Job10JobManager27calculateRequiredMemorySizeEPKNS1_21MemorySizeQueryParamsE(const MemorySizeQueryParams* params) {
|
||||
|
||||
@@ -354,6 +354,34 @@ static void LimitsCase() {
|
||||
while (accepted < 0x3ff && (result = sequence->Dispatch(Nothing, nullptr, 1, nullptr)) == 0) ++accepted;
|
||||
Require(result == NoNodes && accepted == 33);
|
||||
Require(sequence->Dispatch(Nothing, nullptr, 1, nullptr) == Invalid);
|
||||
Require(sequence->Wait() == Invalid);
|
||||
Require(factory->Destroy(sequence) == 0);
|
||||
Require(factory->Create(&sequence, 0x7fff, 0x3ff, 0xff, 10, "recycle") == 0);
|
||||
for (int cycle = 0; cycle < 5; ++cycle) {
|
||||
for (int index = 0; index < 33; ++index) {
|
||||
Require(sequence->Dispatch(Nothing, nullptr, 1, nullptr) == 0);
|
||||
}
|
||||
Require(sequence->Wait() == 0);
|
||||
}
|
||||
int finalAccepted = 0;
|
||||
int finalResult = 0;
|
||||
while (finalAccepted < 0x3ff && (finalResult = sequence->Dispatch(Nothing, nullptr, 1, nullptr)) == 0) ++finalAccepted;
|
||||
Require(finalResult == NoNodes && finalAccepted == 33);
|
||||
Require(sequence->Wait() == Invalid);
|
||||
Require(factory->Destroy(sequence) == 0);
|
||||
for (int round = 0; round < 5; ++round) {
|
||||
Require(factory->Create(&sequence, 0x7fff, 0x3ff, 0xff, 2, "recycle_round") == 0);
|
||||
for (int index = 0; index < 33; ++index) {
|
||||
Require(sequence->Dispatch(Nothing, nullptr, 1, nullptr) == 0);
|
||||
}
|
||||
Require(sequence->Wait() == 0);
|
||||
int acceptedInRound = 0;
|
||||
int resultInRound = 0;
|
||||
while (acceptedInRound < 0x3ff && (resultInRound = sequence->Dispatch(Nothing, nullptr, 1, nullptr)) == 0) ++acceptedInRound;
|
||||
Require(resultInRound == NoNodes && acceptedInRound == 33);
|
||||
Require(sequence->Wait() == Invalid);
|
||||
Require(factory->Destroy(sequence) == 0);
|
||||
}
|
||||
Require(Shutdown(manager.storage) == 0);
|
||||
}
|
||||
|
||||
@@ -418,6 +446,11 @@ static void ShutdownCase() {
|
||||
Require(Shutdown(manager.storage) == NotInitialized);
|
||||
Require(Factories(manager.storage, 0) == nullptr);
|
||||
}
|
||||
{
|
||||
Block scopedBlock(SizeFor(2, 2, 8, 2, 2));
|
||||
ManagerObject scoped;
|
||||
Require(Initialize(scoped.storage, scopedBlock.memory, scopedBlock.bytes, 2, 2, nullptr) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
Reference in new issue
Block a user