Skip to content

Commit b4c1a05

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

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
@@ -179,11 +179,47 @@ 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+
#ifdef RUBY_TYPED_FREE_IMMEDIATELY
219+
RUBY_TYPED_FREE_IMMEDIATELY,
220+
#endif
221+
};
222+
187223
static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
188224
VALUE rb_error_msg = rb_str_new2(mysql_error(wrapper->client));
189225
VALUE rb_sql_state = rb_str_new2(mysql_sqlstate(wrapper->client));
@@ -290,12 +326,6 @@ static void *nogvl_close(void *ptr) {
290326
return NULL;
291327
}
292328

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-
299329
void decr_mysql2_client(mysql_client_wrapper *wrapper)
300330
{
301331
wrapper->refcount--;
@@ -327,7 +357,11 @@ void decr_mysql2_client(mysql_client_wrapper *wrapper)
327357
static VALUE allocate(VALUE klass) {
328358
VALUE obj;
329359
mysql_client_wrapper * wrapper;
360+
#ifdef NEW_TYPEDDATA_WRAPPER
361+
obj = TypedData_Make_Struct(klass, mysql_client_wrapper, &rb_mysql_client_type, wrapper);
362+
#else
330363
obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
364+
#endif
331365
wrapper->encoding = Qnil;
332366
wrapper->active_thread = Qnil;
333367
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: 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);
@@ -358,7 +402,7 @@ static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_e
358402
int enc_index;
359403

360404
enc_name = (field.charsetnr-1 < MYSQL2_CHARSETNR_SIZE) ? mysql2_mysql_enc_to_rb[field.charsetnr-1] : NULL;
361-
405+
362406
if (enc_name != NULL) {
363407
/* use the field encoding we were able to match */
364408
enc_index = rb_enc_find_index(enc_name);
@@ -1122,7 +1166,11 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
11221166
VALUE obj;
11231167
mysql2_result_wrapper * wrapper;
11241168

1169+
#ifdef NEW_TYPEDDATA_WRAPPER
1170+
obj = TypedData_Make_Struct(cMysql2Result, mysql2_result_wrapper, &rb_mysql_result_type, wrapper);
1171+
#else
11251172
obj = Data_Make_Struct(cMysql2Result, mysql2_result_wrapper, rb_mysql_result_mark, rb_mysql_result_free, wrapper);
1173+
#endif
11261174
wrapper->numberOfFields = 0;
11271175
wrapper->numberOfRows = 0;
11281176
wrapper->lastRowProcessed = 0;
@@ -1168,7 +1216,7 @@ void init_mysql2_result() {
11681216

11691217
cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
11701218
rb_global_variable(&cMysql2Result);
1171-
1219+
11721220
rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
11731221
rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0);
11741222
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)