Bug #14929 ยป 0001-thread.c-do_select-fix-leak-on-exception.patch
test/ruby/test_io.rb | ||
---|---|---|
assert_equal 'done', noex.value ,'r63216'
|
||
end
|
||
end
|
||
def test_select_leak
|
||
assert_no_memory_leak([], <<-"end;", <<-"end;", rss: true, timeout: 30)
|
||
r, w = IO.pipe
|
||
rset = [r]
|
||
wset = [w]
|
||
Thread.new { IO.select(rset, wset, nil, 0) }.join
|
||
end;
|
||
200000.times do
|
||
th = Thread.new { IO.select(rset, wset) }
|
||
Thread.pass until th.stop?
|
||
th.kill
|
||
th.join
|
||
end
|
||
end;
|
||
end
|
||
end
|
thread.c | ||
---|---|---|
#define restore_fdset(fds1, fds2) \
|
||
((fds1) ? rb_fd_dup(fds1, fds2) : (void)0)
|
||
struct select_set {
|
||
rb_fdset_t read;
|
||
rb_fdset_t write;
|
||
rb_fdset_t except;
|
||
};
|
||
static size_t
|
||
select_set_memsize(const void *p)
|
||
{
|
||
return sizeof(struct select_set);
|
||
}
|
||
static void
|
||
select_set_free(void *p)
|
||
{
|
||
struct select_set *orig = p;
|
||
rb_fd_term(&orig->read);
|
||
rb_fd_term(&orig->write);
|
||
rb_fd_term(&orig->except);
|
||
xfree(orig);
|
||
}
|
||
static const rb_data_type_t select_set_type = {
|
||
"select_set",
|
||
{NULL, select_set_free, select_set_memsize,},
|
||
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
||
};
|
||
static int
|
||
do_select(int n, rb_fdset_t *const readfds, rb_fdset_t *const writefds,
|
||
rb_fdset_t *const exceptfds, struct timeval *timeout)
|
||
{
|
||
int MAYBE_UNUSED(result);
|
||
int lerrno;
|
||
rb_fdset_t MAYBE_UNUSED(orig_read);
|
||
rb_fdset_t MAYBE_UNUSED(orig_write);
|
||
rb_fdset_t MAYBE_UNUSED(orig_except);
|
||
struct timespec ts, end, *tsp;
|
||
rb_thread_t *th = GET_THREAD();
|
||
VALUE o;
|
||
struct select_set *orig;
|
||
o = TypedData_Make_Struct(0, struct select_set, &select_set_type, orig);
|
||
timeout_prepare(&tsp, &ts, &end, timeout);
|
||
#define do_select_update() \
|
||
(restore_fdset(readfds, &orig_read), \
|
||
restore_fdset(writefds, &orig_write), \
|
||
restore_fdset(exceptfds, &orig_except), \
|
||
(restore_fdset(readfds, &orig->read), \
|
||
restore_fdset(writefds, &orig->write), \
|
||
restore_fdset(exceptfds, &orig->except), \
|
||
TRUE)
|
||
#define fd_init_copy(f) \
|
||
(f##fds) ? rb_fd_init_copy(&orig_##f, f##fds) : rb_fd_no_init(&orig_##f)
|
||
(f##fds) ? rb_fd_init_copy(&orig->f, f##fds) : rb_fd_no_init(&orig->f)
|
||
fd_init_copy(read);
|
||
fd_init_copy(write);
|
||
fd_init_copy(except);
|
||
... | ... | |
if (result < 0) lerrno = errno;
|
||
}, ubf_select, th, FALSE);
|
||
RUBY_VM_CHECK_INTS_BLOCKING(th->ec);
|
||
RUBY_VM_CHECK_INTS_BLOCKING(th->ec); /* may raise */
|
||
} while (wait_retryable(&result, lerrno, tsp, &end) && do_select_update());
|
||
#define fd_term(f) if (f##fds) rb_fd_term(&orig_##f)
|
||
fd_term(read);
|
||
fd_term(write);
|
||
fd_term(except);
|
||
#undef fd_term
|
||
/* didn't raise, perform cleanup ourselves */
|
||
select_set_free(orig);
|
||
rb_gc_force_recycle(o);
|
||
if (result < 0) {
|
||
errno = lerrno;
|
||
}
|
||
-
|