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;
|
||
|
}
|
||
|
-
|
||