Project

General

Profile

Feature #6615 » zlib.release_gvl.3.patch

drbrain (Eric Hodel), 06/26/2012 08:27 AM

View differences:

ext/zlib/zlib.c (working copy)
struct zstream;
struct zstream_funcs;
struct zstream_run_args;
static void zstream_init(struct zstream*, const struct zstream_funcs*);
static void zstream_expand_buffer(struct zstream*);
static void zstream_expand_buffer_into(struct zstream*, unsigned long);
......
inflateReset, inflateEnd, inflate,
};
struct zstream_run_args {
struct zstream * z;
int flush;
int interrupt;
};
static voidpf
zlib_mem_alloc(voidpf opaque, uInt items, uInt size)
......
}
}
static int
zstream_expand_buffer_without_gvl(struct zstream *z)
{
char * new_str;
long inc, len;
if (RSTRING_LEN(z->buf) - z->buf_filled >= ZSTREAM_AVAIL_OUT_STEP_MAX) {
z->stream.avail_out = ZSTREAM_AVAIL_OUT_STEP_MAX;
}
else {
inc = z->buf_filled / 2;
if (inc < ZSTREAM_AVAIL_OUT_STEP_MIN) {
inc = ZSTREAM_AVAIL_OUT_STEP_MIN;
}
len = z->buf_filled + inc;
new_str = realloc(RSTRING(z->buf)->as.heap.ptr, len + 1);
if (!new_str)
return 0;
/* from rb_str_resize */
RSTRING(z->buf)->as.heap.ptr = new_str;
RSTRING(z->buf)->as.heap.ptr[len] = '\0'; /* sentinel */
RSTRING(z->buf)->as.heap.len =
RSTRING(z->buf)->as.heap.aux.capa = len;
z->stream.avail_out = (inc < ZSTREAM_AVAIL_OUT_STEP_MAX) ?
(int)inc : ZSTREAM_AVAIL_OUT_STEP_MAX;
}
z->stream.next_out = (Bytef*)RSTRING_PTR(z->buf) + z->buf_filled;
return 1;
}
static void
zstream_append_buffer(struct zstream *z, const Bytef *src, long len)
{
......
return Qnil;
}
static VALUE
zstream_run_func(void *ptr) {
struct zstream_run_args *args = (struct zstream_run_args *)ptr;
int err, flush = args->flush;
struct zstream *z = args->z;
uInt n;
while (!args->interrupt) {
n = z->stream.avail_out;
err = z->func->run(&z->stream, flush);
z->buf_filled += n - z->stream.avail_out;
if (err == Z_STREAM_END) {
z->flags &= ~ZSTREAM_FLAG_IN_STREAM;
z->flags |= ZSTREAM_FLAG_FINISHED;
break;
}
if (err != Z_OK)
break;
if (z->stream.avail_out > 0) {
z->flags |= ZSTREAM_FLAG_IN_STREAM;
break;
}
if (!zstream_expand_buffer_without_gvl(z)) {
err = Z_MEM_ERROR; /* realloc failed */
break;
}
}
return (VALUE)err;
}
/*
* There is no safe way to interrupt z->run->func().
*/
static void
zstream_unblock_func(void *ptr) {
struct zstream_run_args *args = (struct zstream_run_args *)ptr;
args->interrupt = 1;
}
static void
zstream_run(struct zstream *z, Bytef *src, long len, int flush)
{
uInt n;
struct zstream_run_args args;
int err;
volatile VALUE guard = Qnil;
args.z = z;
args.flush = flush;
args.interrupt = 0;
if (NIL_P(z->input) && len == 0) {
z->stream.next_in = (Bytef*)"";
z->stream.avail_in = 0;
......
zstream_expand_buffer(z);
}
for (;;) {
/* VC allocates err and guard to same address. accessing err and guard
in same scope prevents it. */
RB_GC_GUARD(guard);
n = z->stream.avail_out;
err = z->func->run(&z->stream, flush);
z->buf_filled += n - z->stream.avail_out;
rb_thread_schedule();
loop:
err = (int)rb_thread_blocking_region(
zstream_run_func, (void *)&args,
zstream_unblock_func, (void *)&args);
if (flush != Z_FINISH && err == Z_BUF_ERROR
&& z->stream.avail_out > 0) {
z->flags |= ZSTREAM_FLAG_IN_STREAM;
}
if (err == Z_STREAM_END) {
z->flags &= ~ZSTREAM_FLAG_IN_STREAM;
z->flags |= ZSTREAM_FLAG_FINISHED;
break;
zstream_reset_input(z);
if (err != Z_OK && err != Z_STREAM_END) {
if (z->stream.avail_in > 0) {
zstream_append_input(z, z->stream.next_in, z->stream.avail_in);
}
if (err != Z_OK) {
if (flush != Z_FINISH && err == Z_BUF_ERROR
&& z->stream.avail_out > 0) {
z->flags |= ZSTREAM_FLAG_IN_STREAM;
break;
if (err == Z_NEED_DICT) {
VALUE self = (VALUE)z->stream.opaque;
VALUE dicts = rb_ivar_get(self, id_dictionaries);
VALUE dict = rb_hash_aref(dicts, rb_uint2inum(z->stream.adler));
if (!NIL_P(dict)) {
rb_inflate_set_dictionary(self, dict);
goto loop;
}
zstream_reset_input(z);
if (z->stream.avail_in > 0) {
zstream_append_input(z, z->stream.next_in, z->stream.avail_in);
}
if (err == Z_NEED_DICT) {
VALUE self = (VALUE)z->stream.opaque;
VALUE dicts = rb_ivar_get(self, id_dictionaries);
VALUE dict = rb_hash_aref(dicts, rb_uint2inum(z->stream.adler));
if (!NIL_P(dict)) {
rb_inflate_set_dictionary(self, dict);
continue;
}
}
raise_zlib_error(err, z->stream.msg);
}
if (z->stream.avail_out > 0) {
z->flags |= ZSTREAM_FLAG_IN_STREAM;
break;
}
zstream_expand_buffer(z);
raise_zlib_error(err, z->stream.msg);
}
zstream_reset_input(z);
if (z->stream.avail_in > 0) {
zstream_append_input(z, z->stream.next_in, z->stream.avail_in);
guard = Qnil; /* prevent tail call to make guard effective */
(3-3/3)