Skip to content

Commit

Permalink
Support ruby GC compaction
Browse files Browse the repository at this point in the history
  • Loading branch information
fatkodima committed Apr 15, 2021
1 parent 346b4a4 commit b4c1a05
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 23 deletions.
50 changes: 42 additions & 8 deletions ext/mysql2/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,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_thread);
rb_gc_mark_movable(w->encoding);
rb_gc_mark_movable(w->active_thread);
}
}

/* 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_thread);
}
}

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 @@ -290,12 +326,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 @@ -327,7 +357,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_thread = 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 @@ -18,9 +18,17 @@ typedef struct {
void rb_mysql_client_set_active_thread(VALUE self);
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 @@ -28,6 +28,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 @@ -358,7 +402,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 @@ -1122,7 +1166,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 @@ -1168,7 +1216,7 @@ void init_mysql2_result() {

cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
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

0 comments on commit b4c1a05

Please sign in to comment.