Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support ruby GC compaction #1192

Merged
merged 1 commit into from
Jan 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 42 additions & 8 deletions ext/mysql2/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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--;
Expand Down Expand Up @@ -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;
Expand Down
8 changes: 8 additions & 0 deletions ext/mysql2/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions ext/mysql2/extconf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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')

Expand Down
13 changes: 13 additions & 0 deletions ext/mysql2/mysql2_ext.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <client.h>
#include <statement.h>
#include <result.h>
Expand Down
64 changes: 56 additions & 8 deletions ext/mysql2/result.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
51 changes: 44 additions & 7 deletions ext/mysql2/statement.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,57 @@ 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"); }

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) {
Expand All @@ -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--;

Expand Down Expand Up @@ -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;
Expand Down