From f2a731e0a770684b147235b0d5b8f077a5f3d647 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 5 May 2010 18:26:29 -0700 Subject: [PATCH] Fix memory leak from the result wrapper struct itself Data_Make_Struct always allocates memory for us, so we need to explicitly free the pointer in the function we pass to the GC, not just objects internal to us. Without this change, a slow but constant growth can be seen with the trivial code below: x = Mysql2::Client.new loop { x.query "select 1" } --- ext/mysql2_ext.c | 21 ++++++++++++++------- ext/mysql2_ext.h | 1 + 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/ext/mysql2_ext.c b/ext/mysql2_ext.c index 820d53f..1df155e 100644 --- a/ext/mysql2_ext.c +++ b/ext/mysql2_ext.c @@ -401,13 +401,20 @@ static VALUE rb_mysql_result_to_obj(MYSQL_RES * r) { return obj; } +/* this may be called manually or during GC */ +static void rb_mysql_result_free_result(mysql2_result_wrapper * wrapper) { + if (wrapper && wrapper->resultFreed != 1) { + mysql_free_result(wrapper->result); + wrapper->resultFreed = 1; + } +} + +/* this is called during GC */ static void rb_mysql_result_free(void * wrapper) { mysql2_result_wrapper * w = wrapper; - if (w && w->resultFreed != 1) { - /* FIXME: this may call flush_use_result, which can hit the socket */ - mysql_free_result(w->result); - w->resultFreed = 1; - } + /* FIXME: this may call flush_use_result, which can hit the socket */ + rb_mysql_result_free_result(w); + xfree(wrapper); } static void rb_mysql_result_mark(void * wrapper) { @@ -611,7 +618,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) { if (row == Qnil) { // we don't need the mysql C dataset around anymore, peace it - rb_mysql_result_free(wrapper); + rb_mysql_result_free_result(wrapper); return Qnil; } @@ -621,7 +628,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) { } if (wrapper->lastRowProcessed == wrapper->numberOfRows) { // we don't need the mysql C dataset around anymore, peace it - rb_mysql_result_free(wrapper); + rb_mysql_result_free_result(wrapper); } } diff --git a/ext/mysql2_ext.h b/ext/mysql2_ext.h index bedbb9e..b02f0dd 100644 --- a/ext/mysql2_ext.h +++ b/ext/mysql2_ext.h @@ -67,6 +67,7 @@ static VALUE rb_mysql_result_fetch_row(int argc, VALUE * argv, VALUE self); static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self); static void rb_mysql_result_free(void * wrapper); static void rb_mysql_result_mark(void * wrapper); +static void rb_mysql_result_free_result(mysql2_result_wrapper * wrapper); /* * used to pass all arguments to mysql_real_connect while inside