mirror of
https://github.com/facebook/zstd.git
synced 2025-07-16 07:01:58 +03:00
[contrib/pzstd] Prevent hangs when there are errors
When two threads are using a WorkQueue and the reader thread exits due to an error, it must call WorkQueue::finish() to wake up the writer thread. Otherwise, if the queue is full and the writer thread is waiting for a free slot, it could hang forever. This can happen in pratice when decompressing a large, corrupted file that does not contain pzstd skippable frames.
This commit is contained in:
committed by
Nick Terrell
parent
a610550e2c
commit
80af41e08a
@ -269,7 +269,10 @@ static void compress(
|
|||||||
std::shared_ptr<BufferWorkQueue> out,
|
std::shared_ptr<BufferWorkQueue> out,
|
||||||
size_t maxInputSize) {
|
size_t maxInputSize) {
|
||||||
auto& errorHolder = state.errorHolder;
|
auto& errorHolder = state.errorHolder;
|
||||||
auto guard = makeScopeGuard([&] { out->finish(); });
|
auto guard = makeScopeGuard([&] {
|
||||||
|
in->finish();
|
||||||
|
out->finish();
|
||||||
|
});
|
||||||
// Initialize the CCtx
|
// Initialize the CCtx
|
||||||
auto ctx = state.cStreamPool->get();
|
auto ctx = state.cStreamPool->get();
|
||||||
if (!errorHolder.check(ctx != nullptr, "Failed to allocate ZSTD_CStream")) {
|
if (!errorHolder.check(ctx != nullptr, "Failed to allocate ZSTD_CStream")) {
|
||||||
@ -431,7 +434,10 @@ static void decompress(
|
|||||||
std::shared_ptr<BufferWorkQueue> in,
|
std::shared_ptr<BufferWorkQueue> in,
|
||||||
std::shared_ptr<BufferWorkQueue> out) {
|
std::shared_ptr<BufferWorkQueue> out) {
|
||||||
auto& errorHolder = state.errorHolder;
|
auto& errorHolder = state.errorHolder;
|
||||||
auto guard = makeScopeGuard([&] { out->finish(); });
|
auto guard = makeScopeGuard([&] {
|
||||||
|
in->finish();
|
||||||
|
out->finish();
|
||||||
|
});
|
||||||
// Initialize the DCtx
|
// Initialize the DCtx
|
||||||
auto ctx = state.dStreamPool->get();
|
auto ctx = state.dStreamPool->get();
|
||||||
if (!errorHolder.check(ctx != nullptr, "Failed to allocate ZSTD_DStream")) {
|
if (!errorHolder.check(ctx != nullptr, "Failed to allocate ZSTD_DStream")) {
|
||||||
@ -578,6 +584,7 @@ std::uint64_t writeFile(
|
|||||||
FILE* outputFd,
|
FILE* outputFd,
|
||||||
bool decompress) {
|
bool decompress) {
|
||||||
auto& errorHolder = state.errorHolder;
|
auto& errorHolder = state.errorHolder;
|
||||||
|
auto outsFinishGuard = makeScopeGuard([&outs] { outs.finish(); });
|
||||||
auto lineClearGuard = makeScopeGuard([&state] {
|
auto lineClearGuard = makeScopeGuard([&state] {
|
||||||
state.log.clear(kLogInfo);
|
state.log.clear(kLogInfo);
|
||||||
});
|
});
|
||||||
@ -585,6 +592,7 @@ std::uint64_t writeFile(
|
|||||||
std::shared_ptr<BufferWorkQueue> out;
|
std::shared_ptr<BufferWorkQueue> out;
|
||||||
// Grab the output queue for each decompression job (in order).
|
// Grab the output queue for each decompression job (in order).
|
||||||
while (outs.pop(out)) {
|
while (outs.pop(out)) {
|
||||||
|
auto outFinishGuard = makeScopeGuard([&out] { out->finish(); });
|
||||||
if (errorHolder.hasError()) {
|
if (errorHolder.hasError()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -115,13 +115,14 @@ class WorkQueue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Promise that `push()` won't be called again, so once the queue is empty
|
* Promise that either the reader side or the writer side is done.
|
||||||
* there will never any more work.
|
* If the writer is done, `push()` won't be called again, so once the queue
|
||||||
|
* is empty there will never be any more work. If the reader is done, `pop()`
|
||||||
|
* won't be called again, so further items pushed will just be ignored.
|
||||||
*/
|
*/
|
||||||
void finish() {
|
void finish() {
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
assert(!done_);
|
|
||||||
done_ = true;
|
done_ = true;
|
||||||
}
|
}
|
||||||
readerCv_.notify_all();
|
readerCv_.notify_all();
|
||||||
|
Reference in New Issue
Block a user