Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ jobs:
os: windows-latest
- ruby: truffleruby-head
os: windows-latest
- ruby: truffleruby-head
os: macos-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
Expand Down
10 changes: 8 additions & 2 deletions ext/zlib/zlib.c
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,7 @@ struct zstream {
#define ZSTREAM_REUSE_BUFFER_P(z) ((z)->flags & ZSTREAM_REUSE_BUFFER)

#define ZSTREAM_EXPAND_BUFFER_OK 0
#define ZSTREAM_THREAD_INTERRUPTED 9

/* I think that more better value should be found,
but I gave up finding it. B) */
Expand Down Expand Up @@ -1019,7 +1020,7 @@ zstream_run_func(void *ptr)
uInt n;

err = Z_OK;
while (!args->interrupt) {
while (1) {
n = z->stream.avail_out;
err = z->func->run(&z->stream, flush);
rb_str_set_len(z->buf, ZSTREAM_BUF_FILLED(z) + (n - z->stream.avail_out));
Expand Down Expand Up @@ -1059,6 +1060,11 @@ zstream_run_func(void *ptr)
args->jump_state = state;
break;
}

if (args->interrupt) {
err = ZSTREAM_THREAD_INTERRUPTED;
break;
}
}

return (void *)(VALUE)err;
Expand Down Expand Up @@ -1118,7 +1124,7 @@ zstream_run_try(VALUE value_arg)
#endif

/* retry if no exception is thrown */
if (err == Z_OK && args->interrupt) {
if (err == ZSTREAM_THREAD_INTERRUPTED) {
args->interrupt = 0;
goto loop;
}
Expand Down
37 changes: 37 additions & 0 deletions test/zlib/test_zlib.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1473,5 +1473,42 @@ def test_gunzip_no_memory_leak
10_000.times {Zlib.gunzip(d)}
};
end

def test_gzip_thread_interrupts
content = SecureRandom.random_bytes(5000)
stop = false
ret = nil

thr = Thread.new do
until stop
ret = Zlib.gzip(content)
end
end

10000000.times { thr.wakeup }
stop = true
thr.join

assert_equal content, Zlib.gunzip(ret)
end

def test_gzipreader_thread_interrupts
content = SecureRandom.random_bytes(5000)
gzipped = Zlib.gzip(content)
stop = false
ret = nil

thr = Thread.new do
until stop
ret = Zlib::GzipReader.new(StringIO.new(gzipped), encoding: Encoding::BINARY).read
end
end

10000000.times { thr.wakeup }
stop = true
thr.join

assert_equal content, ret
end
end
end