diff --git a/ext/mysql2/client.c b/ext/mysql2/client.c index c200b364..4f62e463 100644 --- a/ext/mysql2/client.c +++ b/ext/mysql2/client.c @@ -192,11 +192,47 @@ static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) { static void rb_mysql_client_mark(void * wrapper) { mysql_client_wrapper * w = wrapper; if (w) { - rb_gc_mark(w->encoding); - rb_gc_mark(w->active_fiber); + rb_gc_mark_movable(w->encoding); + rb_gc_mark_movable(w->active_fiber); } } +/* this is called during GC */ +static void rb_mysql_client_free(void *ptr) { + mysql_client_wrapper *wrapper = ptr; + decr_mysql2_client(wrapper); +} + +static size_t rb_mysql_client_memsize(const void * wrapper) { + const mysql_client_wrapper * w = wrapper; + return sizeof(*w); +} + +static void rb_mysql_client_compact(void * wrapper) { + mysql_client_wrapper * w = wrapper; + if (w) { + rb_mysql2_gc_location(w->encoding); + rb_mysql2_gc_location(w->active_fiber); + } +} + +const rb_data_type_t rb_mysql_client_type = { + "rb_mysql_client", + { + rb_mysql_client_mark, + rb_mysql_client_free, + rb_mysql_client_memsize, +#ifdef HAVE_RB_GC_MARK_MOVABLE + rb_mysql_client_compact, +#endif + }, + 0, + 0, +#ifdef RUBY_TYPED_FREE_IMMEDIATELY + RUBY_TYPED_FREE_IMMEDIATELY, +#endif +}; + static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) { VALUE rb_error_msg = rb_str_new2(mysql_error(wrapper->client)); VALUE rb_sql_state = rb_str_new2(mysql_sqlstate(wrapper->client)); @@ -303,12 +339,6 @@ static void *nogvl_close(void *ptr) { return NULL; } -/* this is called during GC */ -static void rb_mysql_client_free(void *ptr) { - mysql_client_wrapper *wrapper = ptr; - decr_mysql2_client(wrapper); -} - void decr_mysql2_client(mysql_client_wrapper *wrapper) { wrapper->refcount--; @@ -340,7 +370,11 @@ void decr_mysql2_client(mysql_client_wrapper *wrapper) static VALUE allocate(VALUE klass) { VALUE obj; mysql_client_wrapper * wrapper; +#ifdef NEW_TYPEDDATA_WRAPPER + obj = TypedData_Make_Struct(klass, mysql_client_wrapper, &rb_mysql_client_type, wrapper); +#else obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper); +#endif wrapper->encoding = Qnil; wrapper->active_fiber = Qnil; wrapper->automatic_close = 1; diff --git a/ext/mysql2/client.h b/ext/mysql2/client.h index 0a9faaf9..ad6ce8aa 100644 --- a/ext/mysql2/client.h +++ b/ext/mysql2/client.h @@ -17,9 +17,17 @@ typedef struct { void rb_mysql_set_server_query_flags(MYSQL *client, VALUE result); +extern const rb_data_type_t rb_mysql_client_type; + +#ifdef NEW_TYPEDDATA_WRAPPER +#define GET_CLIENT(self) \ + mysql_client_wrapper *wrapper; \ + TypedData_Get_Struct(self, mysql_client_wrapper, &rb_mysql_client_type, wrapper); +#else #define GET_CLIENT(self) \ mysql_client_wrapper *wrapper; \ Data_Get_Struct(self, mysql_client_wrapper, wrapper); +#endif void init_mysql2_client(void); void decr_mysql2_client(mysql_client_wrapper *wrapper); diff --git a/ext/mysql2/extconf.rb b/ext/mysql2/extconf.rb index 592e0248..a6417acf 100644 --- a/ext/mysql2/extconf.rb +++ b/ext/mysql2/extconf.rb @@ -36,6 +36,9 @@ def add_ssl_defines(header) have_func('rb_absint_size') have_func('rb_absint_singlebit_p') +# 2.7+ +have_func('rb_gc_mark_movable') + # Missing in RBX (https://github.com/rubinius/rubinius/issues/3771) have_func('rb_wait_for_single_fd') diff --git a/ext/mysql2/mysql2_ext.h b/ext/mysql2/mysql2_ext.h index e1ce0e94..f82c47e5 100644 --- a/ext/mysql2/mysql2_ext.h +++ b/ext/mysql2/mysql2_ext.h @@ -36,6 +36,19 @@ void Init_mysql2(void); typedef bool my_bool; #endif +// ruby 2.7+ +#ifdef HAVE_RB_GC_MARK_MOVABLE +#define rb_mysql2_gc_location(ptr) ptr = rb_gc_location(ptr) +#else +#define rb_gc_mark_movable(ptr) rb_gc_mark(ptr) +#define rb_mysql2_gc_location(ptr) +#endif + +// ruby 2.2+ +#ifdef TypedData_Make_Struct +#define NEW_TYPEDDATA_WRAPPER 1 +#endif + #include #include #include diff --git a/ext/mysql2/result.c b/ext/mysql2/result.c index 314cecda..5cacd5ac 100644 --- a/ext/mysql2/result.c +++ b/ext/mysql2/result.c @@ -31,9 +31,13 @@ static rb_encoding *binaryEncoding; #define MYSQL_TYPE_JSON 245 #endif +#ifndef NEW_TYPEDDATA_WRAPPER +#define TypedData_Get_Struct(obj, type, ignore, sval) Data_Get_Struct(obj, type, sval) +#endif + #define GET_RESULT(self) \ mysql2_result_wrapper *wrapper; \ - Data_Get_Struct(self, mysql2_result_wrapper, wrapper); + TypedData_Get_Struct(self, mysql2_result_wrapper, &rb_mysql_result_type, wrapper); typedef struct { int symbolizeKeys; @@ -61,11 +65,11 @@ static VALUE sym_symbolize_keys, sym_as, sym_array, sym_database_timezone, static void rb_mysql_result_mark(void * wrapper) { mysql2_result_wrapper * w = wrapper; if (w) { - rb_gc_mark(w->fields); - rb_gc_mark(w->rows); - rb_gc_mark(w->encoding); - rb_gc_mark(w->client); - rb_gc_mark(w->statement); + rb_gc_mark_movable(w->fields); + rb_gc_mark_movable(w->rows); + rb_gc_mark_movable(w->encoding); + rb_gc_mark_movable(w->client); + rb_gc_mark_movable(w->statement); } } @@ -127,6 +131,46 @@ static void rb_mysql_result_free(void *ptr) { xfree(wrapper); } +static size_t rb_mysql_result_memsize(const void * wrapper) { + const mysql2_result_wrapper * w = wrapper; + size_t memsize = sizeof(*w); + if (w->stmt_wrapper) { + memsize += sizeof(*w->stmt_wrapper); + } + if (w->client_wrapper) { + memsize += sizeof(*w->client_wrapper); + } + return memsize; +} + +static void rb_mysql_result_compact(void * wrapper) { + mysql2_result_wrapper * w = wrapper; + if (w) { + rb_mysql2_gc_location(w->fields); + rb_mysql2_gc_location(w->rows); + rb_mysql2_gc_location(w->encoding); + rb_mysql2_gc_location(w->client); + rb_mysql2_gc_location(w->statement); + } +} + +static const rb_data_type_t rb_mysql_result_type = { + "rb_mysql_result", + { + rb_mysql_result_mark, + rb_mysql_result_free, + rb_mysql_result_memsize, +#ifdef HAVE_RB_GC_MARK_MOVABLE + rb_mysql_result_compact, +#endif + }, + 0, + 0, +#ifdef RUBY_TYPED_FREE_IMMEDIATELY + RUBY_TYPED_FREE_IMMEDIATELY, +#endif +}; + static VALUE rb_mysql_result_free_(VALUE self) { GET_RESULT(self); rb_mysql_result_free_result(wrapper); @@ -365,7 +409,7 @@ static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_e int enc_index; enc_name = (field.charsetnr-1 < MYSQL2_CHARSETNR_SIZE) ? mysql2_mysql_enc_to_rb[field.charsetnr-1] : NULL; - + if (enc_name != NULL) { /* use the field encoding we were able to match */ enc_index = rb_enc_find_index(enc_name); @@ -1129,7 +1173,11 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_ VALUE obj; mysql2_result_wrapper * wrapper; +#ifdef NEW_TYPEDDATA_WRAPPER + obj = TypedData_Make_Struct(cMysql2Result, mysql2_result_wrapper, &rb_mysql_result_type, wrapper); +#else obj = Data_Make_Struct(cMysql2Result, mysql2_result_wrapper, rb_mysql_result_mark, rb_mysql_result_free, wrapper); +#endif wrapper->numberOfFields = 0; wrapper->numberOfRows = 0; wrapper->lastRowProcessed = 0; @@ -1176,7 +1224,7 @@ void init_mysql2_result() { cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject); rb_undef_alloc_func(cMysql2Result); rb_global_variable(&cMysql2Result); - + rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1); rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0); rb_define_method(cMysql2Result, "field_types", rb_mysql_result_fetch_field_types, 0); diff --git a/ext/mysql2/statement.c b/ext/mysql2/statement.c index fd506de0..2af1c486 100644 --- a/ext/mysql2/statement.c +++ b/ext/mysql2/statement.c @@ -6,9 +6,13 @@ static VALUE sym_stream, intern_new_with_args, intern_each, intern_to_s, intern_ static VALUE intern_sec_fraction, intern_usec, intern_sec, intern_min, intern_hour, intern_day, intern_month, intern_year, intern_query_options; +#ifndef NEW_TYPEDDATA_WRAPPER +#define TypedData_Get_Struct(obj, type, ignore, sval) Data_Get_Struct(obj, type, sval) +#endif + #define GET_STATEMENT(self) \ mysql_stmt_wrapper *stmt_wrapper; \ - Data_Get_Struct(self, mysql_stmt_wrapper, stmt_wrapper); \ + TypedData_Get_Struct(self, mysql_stmt_wrapper, &rb_mysql_statement_type, stmt_wrapper); \ if (!stmt_wrapper->stmt) { rb_raise(cMysql2Error, "Invalid statement handle"); } \ if (stmt_wrapper->closed) { rb_raise(cMysql2Error, "Statement handle already closed"); } @@ -16,9 +20,43 @@ static void rb_mysql_stmt_mark(void * ptr) { mysql_stmt_wrapper *stmt_wrapper = ptr; if (!stmt_wrapper) return; - rb_gc_mark(stmt_wrapper->client); + rb_gc_mark_movable(stmt_wrapper->client); +} + +static void rb_mysql_stmt_free(void *ptr) { + mysql_stmt_wrapper *stmt_wrapper = ptr; + decr_mysql2_stmt(stmt_wrapper); +} + +static size_t rb_mysql_stmt_memsize(const void * ptr) { + const mysql_stmt_wrapper *stmt_wrapper = ptr; + return sizeof(*stmt_wrapper); +} + +static void rb_mysql_stmt_compact(void * ptr) { + mysql_stmt_wrapper *stmt_wrapper = ptr; + if (!stmt_wrapper) return; + + rb_mysql2_gc_location(stmt_wrapper->client); } +static const rb_data_type_t rb_mysql_statement_type = { + "rb_mysql_statement", + { + rb_mysql_stmt_mark, + rb_mysql_stmt_free, + rb_mysql_stmt_memsize, +#ifdef HAVE_RB_GC_MARK_MOVABLE + rb_mysql_stmt_compact, +#endif + }, + 0, + 0, +#ifdef RUBY_TYPED_FREE_IMMEDIATELY + RUBY_TYPED_FREE_IMMEDIATELY, +#endif +}; + static void *nogvl_stmt_close(void *ptr) { mysql_stmt_wrapper *stmt_wrapper = ptr; if (stmt_wrapper->stmt) { @@ -28,11 +66,6 @@ static void *nogvl_stmt_close(void *ptr) { return NULL; } -static void rb_mysql_stmt_free(void *ptr) { - mysql_stmt_wrapper *stmt_wrapper = ptr; - decr_mysql2_stmt(stmt_wrapper); -} - void decr_mysql2_stmt(mysql_stmt_wrapper *stmt_wrapper) { stmt_wrapper->refcount--; @@ -96,7 +129,11 @@ VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) { Check_Type(sql, T_STRING); +#ifdef NEW_TYPEDDATA_WRAPPER + rb_stmt = TypedData_Make_Struct(cMysql2Statement, mysql_stmt_wrapper, &rb_mysql_statement_type, stmt_wrapper); +#else rb_stmt = Data_Make_Struct(cMysql2Statement, mysql_stmt_wrapper, rb_mysql_stmt_mark, rb_mysql_stmt_free, stmt_wrapper); +#endif { stmt_wrapper->client = rb_client; stmt_wrapper->refcount = 1;