Skip to content

Commit 7d81704

Browse files
committed
Support ruby GC compaction
1 parent 346b4a4 commit 7d81704

File tree

6 files changed

+160
-23
lines changed

6 files changed

+160
-23
lines changed

ext/mysql2/client.c

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -179,11 +179,45 @@ static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
179179
static void rb_mysql_client_mark(void * wrapper) {
180180
mysql_client_wrapper * w = wrapper;
181181
if (w) {
182-
rb_gc_mark(w->encoding);
183-
rb_gc_mark(w->active_thread);
182+
rb_gc_mark_movable(w->encoding);
183+
rb_gc_mark_movable(w->active_thread);
184184
}
185185
}
186186

187+
/* this is called during GC */
188+
static void rb_mysql_client_free(void *ptr) {
189+
mysql_client_wrapper *wrapper = ptr;
190+
decr_mysql2_client(wrapper);
191+
}
192+
193+
static size_t rb_mysql_client_memsize(const void * wrapper) {
194+
const mysql_client_wrapper * w = wrapper;
195+
return sizeof(*w);
196+
}
197+
198+
static void rb_mysql_client_compact(void * wrapper) {
199+
mysql_client_wrapper * w = wrapper;
200+
if (w) {
201+
rb_mysql2_gc_location(w->encoding);
202+
rb_mysql2_gc_location(w->active_thread);
203+
}
204+
}
205+
206+
const rb_data_type_t rb_mysql_client_type = {
207+
"rb_mysql_client",
208+
{
209+
rb_mysql_client_mark,
210+
rb_mysql_client_free,
211+
rb_mysql_client_memsize,
212+
#ifdef HAVE_RB_GC_MARK_MOVABLE
213+
rb_mysql_client_compact,
214+
#endif
215+
},
216+
0,
217+
0,
218+
RUBY_TYPED_FREE_IMMEDIATELY,
219+
};
220+
187221
static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
188222
VALUE rb_error_msg = rb_str_new2(mysql_error(wrapper->client));
189223
VALUE rb_sql_state = rb_str_new2(mysql_sqlstate(wrapper->client));
@@ -290,12 +324,6 @@ static void *nogvl_close(void *ptr) {
290324
return NULL;
291325
}
292326

293-
/* this is called during GC */
294-
static void rb_mysql_client_free(void *ptr) {
295-
mysql_client_wrapper *wrapper = ptr;
296-
decr_mysql2_client(wrapper);
297-
}
298-
299327
void decr_mysql2_client(mysql_client_wrapper *wrapper)
300328
{
301329
wrapper->refcount--;
@@ -327,7 +355,11 @@ void decr_mysql2_client(mysql_client_wrapper *wrapper)
327355
static VALUE allocate(VALUE klass) {
328356
VALUE obj;
329357
mysql_client_wrapper * wrapper;
358+
#ifdef NEW_TYPEDDATA_WRAPPER
359+
obj = TypedData_Make_Struct(klass, mysql_client_wrapper, &rb_mysql_client_type, wrapper);
360+
#else
330361
obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
362+
#endif
331363
wrapper->encoding = Qnil;
332364
wrapper->active_thread = Qnil;
333365
wrapper->automatic_close = 1;

ext/mysql2/client.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,17 @@ typedef struct {
1818
void rb_mysql_client_set_active_thread(VALUE self);
1919
void rb_mysql_set_server_query_flags(MYSQL *client, VALUE result);
2020

21+
extern const rb_data_type_t rb_mysql_client_type;
22+
23+
#ifdef NEW_TYPEDDATA_WRAPPER
24+
#define GET_CLIENT(self) \
25+
mysql_client_wrapper *wrapper; \
26+
TypedData_Get_Struct(self, mysql_client_wrapper, &rb_mysql_client_type, wrapper);
27+
#else
2128
#define GET_CLIENT(self) \
2229
mysql_client_wrapper *wrapper; \
2330
Data_Get_Struct(self, mysql_client_wrapper, wrapper);
31+
#endif
2432

2533
void init_mysql2_client(void);
2634
void decr_mysql2_client(mysql_client_wrapper *wrapper);

ext/mysql2/extconf.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ def add_ssl_defines(header)
2828
have_func('rb_absint_size')
2929
have_func('rb_absint_singlebit_p')
3030

31+
# 2.7+
32+
have_func('rb_gc_mark_movable')
33+
3134
# Missing in RBX (https://github.com/rubinius/rubinius/issues/3771)
3235
have_func('rb_wait_for_single_fd')
3336

ext/mysql2/mysql2_ext.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,19 @@ void Init_mysql2(void);
3636
typedef bool my_bool;
3737
#endif
3838

39+
// ruby 2.7+
40+
#ifdef HAVE_RB_GC_MARK_MOVABLE
41+
#define rb_mysql2_gc_location(ptr) ptr = rb_gc_location(ptr)
42+
#else
43+
#define rb_gc_mark_movable(ptr) rb_gc_mark(ptr)
44+
#define rb_mysql2_gc_location(ptr)
45+
#endif
46+
47+
// ruby 2.2+
48+
#ifdef TypedData_Make_Struct
49+
#define NEW_TYPEDDATA_WRAPPER 1
50+
#endif
51+
3952
#include <client.h>
4053
#include <statement.h>
4154
#include <result.h>

ext/mysql2/result.c

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,13 @@ static rb_encoding *binaryEncoding;
3131
#define MYSQL_TYPE_JSON 245
3232
#endif
3333

34+
#ifndef NEW_TYPEDDATA_WRAPPER
35+
#define TypedData_Get_Struct(obj, type, ignore, sval) Data_Get_Struct(obj, type, sval)
36+
#endif
37+
3438
#define GET_RESULT(self) \
3539
mysql2_result_wrapper *wrapper; \
36-
Data_Get_Struct(self, mysql2_result_wrapper, wrapper);
40+
TypedData_Get_Struct(self, mysql2_result_wrapper, &rb_mysql_result_type, wrapper);
3741

3842
typedef struct {
3943
int symbolizeKeys;
@@ -61,11 +65,11 @@ static VALUE sym_symbolize_keys, sym_as, sym_array, sym_database_timezone,
6165
static void rb_mysql_result_mark(void * wrapper) {
6266
mysql2_result_wrapper * w = wrapper;
6367
if (w) {
64-
rb_gc_mark(w->fields);
65-
rb_gc_mark(w->rows);
66-
rb_gc_mark(w->encoding);
67-
rb_gc_mark(w->client);
68-
rb_gc_mark(w->statement);
68+
rb_gc_mark_movable(w->fields);
69+
rb_gc_mark_movable(w->rows);
70+
rb_gc_mark_movable(w->encoding);
71+
rb_gc_mark_movable(w->client);
72+
rb_gc_mark_movable(w->statement);
6973
}
7074
}
7175

@@ -127,6 +131,44 @@ static void rb_mysql_result_free(void *ptr) {
127131
xfree(wrapper);
128132
}
129133

134+
static size_t rb_mysql_result_memsize(const void * wrapper) {
135+
const mysql2_result_wrapper * w = wrapper;
136+
size_t memsize = sizeof(*w);
137+
if (w->stmt_wrapper) {
138+
memsize += sizeof(*w->stmt_wrapper);
139+
}
140+
if (w->client_wrapper) {
141+
memsize += sizeof(*w->client_wrapper);
142+
}
143+
return memsize;
144+
}
145+
146+
static void rb_mysql_result_compact(void * wrapper) {
147+
mysql2_result_wrapper * w = wrapper;
148+
if (w) {
149+
rb_mysql2_gc_location(w->fields);
150+
rb_mysql2_gc_location(w->rows);
151+
rb_mysql2_gc_location(w->encoding);
152+
rb_mysql2_gc_location(w->client);
153+
rb_mysql2_gc_location(w->statement);
154+
}
155+
}
156+
157+
static const rb_data_type_t rb_mysql_result_type = {
158+
"rb_mysql_result",
159+
{
160+
rb_mysql_result_mark,
161+
rb_mysql_result_free,
162+
rb_mysql_result_memsize,
163+
#ifdef HAVE_RB_GC_MARK_MOVABLE
164+
rb_mysql_result_compact,
165+
#endif
166+
},
167+
0,
168+
0,
169+
RUBY_TYPED_FREE_IMMEDIATELY,
170+
};
171+
130172
static VALUE rb_mysql_result_free_(VALUE self) {
131173
GET_RESULT(self);
132174
rb_mysql_result_free_result(wrapper);
@@ -358,7 +400,7 @@ static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_e
358400
int enc_index;
359401

360402
enc_name = (field.charsetnr-1 < MYSQL2_CHARSETNR_SIZE) ? mysql2_mysql_enc_to_rb[field.charsetnr-1] : NULL;
361-
403+
362404
if (enc_name != NULL) {
363405
/* use the field encoding we were able to match */
364406
enc_index = rb_enc_find_index(enc_name);
@@ -1122,7 +1164,11 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
11221164
VALUE obj;
11231165
mysql2_result_wrapper * wrapper;
11241166

1167+
#ifdef NEW_TYPEDDATA_WRAPPER
1168+
obj = TypedData_Make_Struct(cMysql2Result, mysql2_result_wrapper, &rb_mysql_result_type, wrapper);
1169+
#else
11251170
obj = Data_Make_Struct(cMysql2Result, mysql2_result_wrapper, rb_mysql_result_mark, rb_mysql_result_free, wrapper);
1171+
#endif
11261172
wrapper->numberOfFields = 0;
11271173
wrapper->numberOfRows = 0;
11281174
wrapper->lastRowProcessed = 0;
@@ -1168,7 +1214,7 @@ void init_mysql2_result() {
11681214

11691215
cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
11701216
rb_global_variable(&cMysql2Result);
1171-
1217+
11721218
rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
11731219
rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0);
11741220
rb_define_method(cMysql2Result, "field_types", rb_mysql_result_fetch_field_types, 0);

ext/mysql2/statement.c

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,55 @@ static VALUE sym_stream, intern_new_with_args, intern_each, intern_to_s, intern_
66
static VALUE intern_sec_fraction, intern_usec, intern_sec, intern_min, intern_hour, intern_day, intern_month, intern_year,
77
intern_query_options;
88

9+
#ifndef NEW_TYPEDDATA_WRAPPER
10+
#define TypedData_Get_Struct(obj, type, ignore, sval) Data_Get_Struct(obj, type, sval)
11+
#endif
12+
913
#define GET_STATEMENT(self) \
1014
mysql_stmt_wrapper *stmt_wrapper; \
11-
Data_Get_Struct(self, mysql_stmt_wrapper, stmt_wrapper); \
15+
TypedData_Get_Struct(self, mysql_stmt_wrapper, &rb_mysql_statement_type, stmt_wrapper); \
1216
if (!stmt_wrapper->stmt) { rb_raise(cMysql2Error, "Invalid statement handle"); } \
1317
if (stmt_wrapper->closed) { rb_raise(cMysql2Error, "Statement handle already closed"); }
1418

1519
static void rb_mysql_stmt_mark(void * ptr) {
1620
mysql_stmt_wrapper *stmt_wrapper = ptr;
1721
if (!stmt_wrapper) return;
1822

19-
rb_gc_mark(stmt_wrapper->client);
23+
rb_gc_mark_movable(stmt_wrapper->client);
24+
}
25+
26+
static void rb_mysql_stmt_free(void *ptr) {
27+
mysql_stmt_wrapper *stmt_wrapper = ptr;
28+
decr_mysql2_stmt(stmt_wrapper);
2029
}
2130

31+
static size_t rb_mysql_stmt_memsize(const void * ptr) {
32+
const mysql_stmt_wrapper *stmt_wrapper = ptr;
33+
return sizeof(*stmt_wrapper);
34+
}
35+
36+
static void rb_mysql_stmt_compact(void * ptr) {
37+
mysql_stmt_wrapper *stmt_wrapper = ptr;
38+
if (!stmt_wrapper) return;
39+
40+
rb_mysql2_gc_location(stmt_wrapper->client);
41+
}
42+
43+
static const rb_data_type_t rb_mysql_statement_type = {
44+
"rb_mysql_statement",
45+
{
46+
rb_mysql_stmt_mark,
47+
rb_mysql_stmt_free,
48+
rb_mysql_stmt_memsize,
49+
#ifdef HAVE_RB_GC_MARK_MOVABLE
50+
rb_mysql_stmt_compact,
51+
#endif
52+
},
53+
0,
54+
0,
55+
RUBY_TYPED_FREE_IMMEDIATELY,
56+
};
57+
2258
static void *nogvl_stmt_close(void *ptr) {
2359
mysql_stmt_wrapper *stmt_wrapper = ptr;
2460
if (stmt_wrapper->stmt) {
@@ -28,11 +64,6 @@ static void *nogvl_stmt_close(void *ptr) {
2864
return NULL;
2965
}
3066

31-
static void rb_mysql_stmt_free(void *ptr) {
32-
mysql_stmt_wrapper *stmt_wrapper = ptr;
33-
decr_mysql2_stmt(stmt_wrapper);
34-
}
35-
3667
void decr_mysql2_stmt(mysql_stmt_wrapper *stmt_wrapper) {
3768
stmt_wrapper->refcount--;
3869

@@ -96,7 +127,11 @@ VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
96127

97128
Check_Type(sql, T_STRING);
98129

130+
#ifdef NEW_TYPEDDATA_WRAPPER
131+
rb_stmt = TypedData_Make_Struct(cMysql2Statement, mysql_stmt_wrapper, &rb_mysql_statement_type, stmt_wrapper);
132+
#else
99133
rb_stmt = Data_Make_Struct(cMysql2Statement, mysql_stmt_wrapper, rb_mysql_stmt_mark, rb_mysql_stmt_free, stmt_wrapper);
134+
#endif
100135
{
101136
stmt_wrapper->client = rb_client;
102137
stmt_wrapper->refcount = 1;

0 commit comments

Comments
 (0)