Skip to content

Commit 8a83482

Browse files
committed
Support ruby GC compaction
1 parent 7f6f33a commit 8a83482

File tree

6 files changed

+166
-23
lines changed

6 files changed

+166
-23
lines changed

ext/mysql2/client.c

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -192,11 +192,47 @@ static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
192192
static void rb_mysql_client_mark(void * wrapper) {
193193
mysql_client_wrapper * w = wrapper;
194194
if (w) {
195-
rb_gc_mark(w->encoding);
196-
rb_gc_mark(w->active_fiber);
195+
rb_gc_mark_movable(w->encoding);
196+
rb_gc_mark_movable(w->active_fiber);
197197
}
198198
}
199199

200+
/* this is called during GC */
201+
static void rb_mysql_client_free(void *ptr) {
202+
mysql_client_wrapper *wrapper = ptr;
203+
decr_mysql2_client(wrapper);
204+
}
205+
206+
static size_t rb_mysql_client_memsize(const void * wrapper) {
207+
const mysql_client_wrapper * w = wrapper;
208+
return sizeof(*w);
209+
}
210+
211+
static void rb_mysql_client_compact(void * wrapper) {
212+
mysql_client_wrapper * w = wrapper;
213+
if (w) {
214+
rb_mysql2_gc_location(w->encoding);
215+
rb_mysql2_gc_location(w->active_fiber);
216+
}
217+
}
218+
219+
const rb_data_type_t rb_mysql_client_type = {
220+
"rb_mysql_client",
221+
{
222+
rb_mysql_client_mark,
223+
rb_mysql_client_free,
224+
rb_mysql_client_memsize,
225+
#ifdef HAVE_RB_GC_MARK_MOVABLE
226+
rb_mysql_client_compact,
227+
#endif
228+
},
229+
0,
230+
0,
231+
#ifdef RUBY_TYPED_FREE_IMMEDIATELY
232+
RUBY_TYPED_FREE_IMMEDIATELY,
233+
#endif
234+
};
235+
200236
static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
201237
VALUE rb_error_msg = rb_str_new2(mysql_error(wrapper->client));
202238
VALUE rb_sql_state = rb_str_new2(mysql_sqlstate(wrapper->client));
@@ -303,12 +339,6 @@ static void *nogvl_close(void *ptr) {
303339
return NULL;
304340
}
305341

306-
/* this is called during GC */
307-
static void rb_mysql_client_free(void *ptr) {
308-
mysql_client_wrapper *wrapper = ptr;
309-
decr_mysql2_client(wrapper);
310-
}
311-
312342
void decr_mysql2_client(mysql_client_wrapper *wrapper)
313343
{
314344
wrapper->refcount--;
@@ -340,7 +370,11 @@ void decr_mysql2_client(mysql_client_wrapper *wrapper)
340370
static VALUE allocate(VALUE klass) {
341371
VALUE obj;
342372
mysql_client_wrapper * wrapper;
373+
#ifdef NEW_TYPEDDATA_WRAPPER
374+
obj = TypedData_Make_Struct(klass, mysql_client_wrapper, &rb_mysql_client_type, wrapper);
375+
#else
343376
obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
377+
#endif
344378
wrapper->encoding = Qnil;
345379
wrapper->active_fiber = Qnil;
346380
wrapper->automatic_close = 1;

ext/mysql2/client.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,17 @@ typedef struct {
1717

1818
void rb_mysql_set_server_query_flags(MYSQL *client, VALUE result);
1919

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

2432
void init_mysql2_client(void);
2533
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
@@ -36,6 +36,9 @@ def add_ssl_defines(header)
3636
have_func('rb_absint_size')
3737
have_func('rb_absint_singlebit_p')
3838

39+
# 2.7+
40+
have_func('rb_gc_mark_movable')
41+
3942
# Missing in RBX (https://github.com/rubinius/rubinius/issues/3771)
4043
have_func('rb_wait_for_single_fd')
4144

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: 56 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,46 @@ 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+
#ifdef RUBY_TYPED_FREE_IMMEDIATELY
170+
RUBY_TYPED_FREE_IMMEDIATELY,
171+
#endif
172+
};
173+
130174
static VALUE rb_mysql_result_free_(VALUE self) {
131175
GET_RESULT(self);
132176
rb_mysql_result_free_result(wrapper);
@@ -365,7 +409,7 @@ static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_e
365409
int enc_index;
366410

367411
enc_name = (field.charsetnr-1 < MYSQL2_CHARSETNR_SIZE) ? mysql2_mysql_enc_to_rb[field.charsetnr-1] : NULL;
368-
412+
369413
if (enc_name != NULL) {
370414
/* use the field encoding we were able to match */
371415
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_
11291173
VALUE obj;
11301174
mysql2_result_wrapper * wrapper;
11311175

1176+
#ifdef NEW_TYPEDDATA_WRAPPER
1177+
obj = TypedData_Make_Struct(cMysql2Result, mysql2_result_wrapper, &rb_mysql_result_type, wrapper);
1178+
#else
11321179
obj = Data_Make_Struct(cMysql2Result, mysql2_result_wrapper, rb_mysql_result_mark, rb_mysql_result_free, wrapper);
1180+
#endif
11331181
wrapper->numberOfFields = 0;
11341182
wrapper->numberOfRows = 0;
11351183
wrapper->lastRowProcessed = 0;
@@ -1176,7 +1224,7 @@ void init_mysql2_result() {
11761224
cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
11771225
rb_undef_alloc_func(cMysql2Result);
11781226
rb_global_variable(&cMysql2Result);
1179-
1227+
11801228
rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
11811229
rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0);
11821230
rb_define_method(cMysql2Result, "field_types", rb_mysql_result_fetch_field_types, 0);

ext/mysql2/statement.c

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,57 @@ 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);
29+
}
30+
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);
2041
}
2142

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+
#ifdef RUBY_TYPED_FREE_IMMEDIATELY
56+
RUBY_TYPED_FREE_IMMEDIATELY,
57+
#endif
58+
};
59+
2260
static void *nogvl_stmt_close(void *ptr) {
2361
mysql_stmt_wrapper *stmt_wrapper = ptr;
2462
if (stmt_wrapper->stmt) {
@@ -28,11 +66,6 @@ static void *nogvl_stmt_close(void *ptr) {
2866
return NULL;
2967
}
3068

31-
static void rb_mysql_stmt_free(void *ptr) {
32-
mysql_stmt_wrapper *stmt_wrapper = ptr;
33-
decr_mysql2_stmt(stmt_wrapper);
34-
}
35-
3669
void decr_mysql2_stmt(mysql_stmt_wrapper *stmt_wrapper) {
3770
stmt_wrapper->refcount--;
3871

@@ -96,7 +129,11 @@ VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
96129

97130
Check_Type(sql, T_STRING);
98131

132+
#ifdef NEW_TYPEDDATA_WRAPPER
133+
rb_stmt = TypedData_Make_Struct(cMysql2Statement, mysql_stmt_wrapper, &rb_mysql_statement_type, stmt_wrapper);
134+
#else
99135
rb_stmt = Data_Make_Struct(cMysql2Statement, mysql_stmt_wrapper, rb_mysql_stmt_mark, rb_mysql_stmt_free, stmt_wrapper);
136+
#endif
100137
{
101138
stmt_wrapper->client = rb_client;
102139
stmt_wrapper->refcount = 1;

0 commit comments

Comments
 (0)