rage-iodine 1.7.58
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
- data/.github/workflows/ruby.yml +42 -0
- data/.gitignore +20 -0
- data/.rspec +2 -0
- data/.yardopts +8 -0
- data/CHANGELOG.md +1098 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +21 -0
- data/LIMITS.md +41 -0
- data/README.md +782 -0
- data/Rakefile +23 -0
- data/SPEC-PubSub-Draft.md +159 -0
- data/SPEC-WebSocket-Draft.md +239 -0
- data/bin/console +22 -0
- data/bin/info.md +353 -0
- data/bin/mustache_bench.rb +100 -0
- data/bin/poc/Gemfile.lock +23 -0
- data/bin/poc/README.md +37 -0
- data/bin/poc/config.ru +66 -0
- data/bin/poc/gemfile +1 -0
- data/bin/poc/www/index.html +57 -0
- data/examples/async_task.ru +92 -0
- data/examples/bates/README.md +3 -0
- data/examples/bates/config.ru +342 -0
- data/examples/bates/david+bold.pdf +0 -0
- data/examples/bates/public/drop-pdf.png +0 -0
- data/examples/bates/public/index.html +600 -0
- data/examples/config.ru +59 -0
- data/examples/echo.ru +59 -0
- data/examples/etag.ru +16 -0
- data/examples/hello.ru +29 -0
- data/examples/pubsub_engine.ru +81 -0
- data/examples/rack3.ru +12 -0
- data/examples/redis.ru +70 -0
- data/examples/shootout.ru +73 -0
- data/examples/sub-protocols.ru +90 -0
- data/examples/tcp_client.rb +66 -0
- data/examples/x-sendfile.ru +14 -0
- data/exe/iodine +280 -0
- data/ext/iodine/extconf.rb +110 -0
- data/ext/iodine/fio.c +12096 -0
- data/ext/iodine/fio.h +6390 -0
- data/ext/iodine/fio_cli.c +431 -0
- data/ext/iodine/fio_cli.h +189 -0
- data/ext/iodine/fio_json_parser.h +687 -0
- data/ext/iodine/fio_siphash.c +157 -0
- data/ext/iodine/fio_siphash.h +37 -0
- data/ext/iodine/fio_tls.h +129 -0
- data/ext/iodine/fio_tls_missing.c +649 -0
- data/ext/iodine/fio_tls_openssl.c +1056 -0
- data/ext/iodine/fio_tmpfile.h +50 -0
- data/ext/iodine/fiobj.h +44 -0
- data/ext/iodine/fiobj4fio.h +21 -0
- data/ext/iodine/fiobj_ary.c +333 -0
- data/ext/iodine/fiobj_ary.h +139 -0
- data/ext/iodine/fiobj_data.c +1185 -0
- data/ext/iodine/fiobj_data.h +167 -0
- data/ext/iodine/fiobj_hash.c +409 -0
- data/ext/iodine/fiobj_hash.h +176 -0
- data/ext/iodine/fiobj_json.c +622 -0
- data/ext/iodine/fiobj_json.h +68 -0
- data/ext/iodine/fiobj_mem.h +71 -0
- data/ext/iodine/fiobj_mustache.c +317 -0
- data/ext/iodine/fiobj_mustache.h +62 -0
- data/ext/iodine/fiobj_numbers.c +344 -0
- data/ext/iodine/fiobj_numbers.h +127 -0
- data/ext/iodine/fiobj_str.c +433 -0
- data/ext/iodine/fiobj_str.h +172 -0
- data/ext/iodine/fiobject.c +620 -0
- data/ext/iodine/fiobject.h +654 -0
- data/ext/iodine/hpack.h +1923 -0
- data/ext/iodine/http.c +2736 -0
- data/ext/iodine/http.h +1019 -0
- data/ext/iodine/http1.c +825 -0
- data/ext/iodine/http1.h +29 -0
- data/ext/iodine/http1_parser.h +1835 -0
- data/ext/iodine/http_internal.c +1279 -0
- data/ext/iodine/http_internal.h +248 -0
- data/ext/iodine/http_mime_parser.h +350 -0
- data/ext/iodine/iodine.c +1433 -0
- data/ext/iodine/iodine.h +64 -0
- data/ext/iodine/iodine_caller.c +218 -0
- data/ext/iodine/iodine_caller.h +27 -0
- data/ext/iodine/iodine_connection.c +941 -0
- data/ext/iodine/iodine_connection.h +55 -0
- data/ext/iodine/iodine_defer.c +420 -0
- data/ext/iodine/iodine_defer.h +6 -0
- data/ext/iodine/iodine_fiobj2rb.h +120 -0
- data/ext/iodine/iodine_helpers.c +282 -0
- data/ext/iodine/iodine_helpers.h +12 -0
- data/ext/iodine/iodine_http.c +1280 -0
- data/ext/iodine/iodine_http.h +23 -0
- data/ext/iodine/iodine_json.c +302 -0
- data/ext/iodine/iodine_json.h +6 -0
- data/ext/iodine/iodine_mustache.c +567 -0
- data/ext/iodine/iodine_mustache.h +6 -0
- data/ext/iodine/iodine_pubsub.c +580 -0
- data/ext/iodine/iodine_pubsub.h +26 -0
- data/ext/iodine/iodine_rack_io.c +273 -0
- data/ext/iodine/iodine_rack_io.h +20 -0
- data/ext/iodine/iodine_store.c +142 -0
- data/ext/iodine/iodine_store.h +20 -0
- data/ext/iodine/iodine_tcp.c +346 -0
- data/ext/iodine/iodine_tcp.h +13 -0
- data/ext/iodine/iodine_tls.c +261 -0
- data/ext/iodine/iodine_tls.h +13 -0
- data/ext/iodine/mustache_parser.h +1546 -0
- data/ext/iodine/redis_engine.c +957 -0
- data/ext/iodine/redis_engine.h +79 -0
- data/ext/iodine/resp_parser.h +317 -0
- data/ext/iodine/scheduler.c +173 -0
- data/ext/iodine/scheduler.h +6 -0
- data/ext/iodine/websocket_parser.h +506 -0
- data/ext/iodine/websockets.c +752 -0
- data/ext/iodine/websockets.h +185 -0
- data/iodine.gemspec +50 -0
- data/lib/iodine/connection.rb +61 -0
- data/lib/iodine/json.rb +42 -0
- data/lib/iodine/mustache.rb +113 -0
- data/lib/iodine/pubsub.rb +55 -0
- data/lib/iodine/rack_utils.rb +43 -0
- data/lib/iodine/tls.rb +16 -0
- data/lib/iodine/version.rb +3 -0
- data/lib/iodine.rb +274 -0
- data/lib/rack/handler/iodine.rb +33 -0
- data/logo.png +0 -0
- metadata +284 -0
@@ -0,0 +1,273 @@
|
|
1
|
+
/*
|
2
|
+
Copyright: Boaz segev, 2016-2017
|
3
|
+
License: MIT
|
4
|
+
|
5
|
+
Feel free to copy, use and enjoy according to the license provided.
|
6
|
+
*/
|
7
|
+
#include "iodine_rack_io.h"
|
8
|
+
|
9
|
+
#include "iodine.h"
|
10
|
+
|
11
|
+
#include <ruby/encoding.h>
|
12
|
+
#include <ruby/io.h>
|
13
|
+
#include <unistd.h>
|
14
|
+
|
15
|
+
#ifndef _GNU_SOURCE
|
16
|
+
#define _GNU_SOURCE
|
17
|
+
#endif
|
18
|
+
|
19
|
+
/* IodineRackIO manages a minimal interface to act as an IO wrapper according to
|
20
|
+
these Rack specifications:
|
21
|
+
|
22
|
+
The input stream is an IO-like object which contains the raw HTTP POST data.
|
23
|
+
When applicable, its external encoding must be “ASCII-8BIT” and it must be
|
24
|
+
opened in binary mode, for Ruby 1.9 compatibility. The input stream must respond
|
25
|
+
to gets, each, read and rewind.
|
26
|
+
|
27
|
+
gets must be called without arguments and return a string, or nil on EOF.
|
28
|
+
|
29
|
+
read behaves like IO#read. Its signature is read([length, [buffer]]). If given,
|
30
|
+
length must be a non-negative Integer (>= 0) or nil, and buffer must be a String
|
31
|
+
and may not be nil. If length is given and not nil, then this method reads at
|
32
|
+
most length bytes from the input stream. If length is not given or nil, then
|
33
|
+
this method reads all data until EOF. When EOF is reached, this method returns
|
34
|
+
nil if length is given and not nil, or “” if length is not given or is nil. If
|
35
|
+
buffer is given, then the read data will be placed into buffer instead of a
|
36
|
+
newly created String object.
|
37
|
+
|
38
|
+
each must be called without arguments and only yield Strings.
|
39
|
+
|
40
|
+
rewind must be called without arguments. It rewinds the input stream back to the
|
41
|
+
beginning. It must not raise Errno::ESPIPE: that is, it may not be a pipe or a
|
42
|
+
socket. Therefore, handler developers must buffer the input data into some
|
43
|
+
rewindable object if the underlying input stream is not rewindable.
|
44
|
+
|
45
|
+
close must never be called on the input stream.
|
46
|
+
|
47
|
+
*/
|
48
|
+
|
49
|
+
/* *****************************************************************************
|
50
|
+
Core data / helpers
|
51
|
+
*/
|
52
|
+
|
53
|
+
static VALUE rRackIO;
|
54
|
+
|
55
|
+
static ID env_id;
|
56
|
+
static ID io_id;
|
57
|
+
|
58
|
+
static VALUE R_INPUT; /* rack.input */
|
59
|
+
static VALUE hijack_func_sym;
|
60
|
+
static VALUE TCPSOCKET_CLASS;
|
61
|
+
static ID for_fd_id;
|
62
|
+
static ID iodine_fd_var_id;
|
63
|
+
static ID iodine_new_func_id;
|
64
|
+
#ifdef __MINGW32__
|
65
|
+
static ID iodine_osffd_id;
|
66
|
+
#endif
|
67
|
+
static rb_encoding *IodineUTF8Encoding;
|
68
|
+
static rb_encoding *IodineBinaryEncoding;
|
69
|
+
|
70
|
+
#define set_handle(object, handle) \
|
71
|
+
rb_ivar_set((object), iodine_fd_var_id, ULL2NUM((uintptr_t)handle))
|
72
|
+
|
73
|
+
inline static http_s *get_handle(VALUE obj) {
|
74
|
+
VALUE i = rb_ivar_get(obj, iodine_fd_var_id);
|
75
|
+
return (http_s *)NUM2ULL(i);
|
76
|
+
}
|
77
|
+
|
78
|
+
/* *****************************************************************************
|
79
|
+
IO API
|
80
|
+
*/
|
81
|
+
|
82
|
+
static inline FIOBJ get_data(VALUE self) {
|
83
|
+
VALUE i = rb_ivar_get(self, io_id);
|
84
|
+
return (FIOBJ)NUM2ULL(i);
|
85
|
+
}
|
86
|
+
|
87
|
+
static VALUE rio_rewind(VALUE self) {
|
88
|
+
FIOBJ io = get_data(self);
|
89
|
+
if (!FIOBJ_TYPE_IS(io, FIOBJ_T_DATA))
|
90
|
+
return Qnil;
|
91
|
+
fiobj_data_seek(io, 0);
|
92
|
+
return INT2NUM(0);
|
93
|
+
}
|
94
|
+
/**
|
95
|
+
Gets returns a line. this is okay for small lines,
|
96
|
+
but shouldn't really be used.
|
97
|
+
|
98
|
+
Limited to ~ 1Mb of a line length.
|
99
|
+
*/
|
100
|
+
static VALUE rio_gets(VALUE self) {
|
101
|
+
FIOBJ io = get_data(self);
|
102
|
+
if (!FIOBJ_TYPE_IS(io, FIOBJ_T_DATA))
|
103
|
+
return Qnil;
|
104
|
+
fio_str_info_s line = fiobj_data_gets(io);
|
105
|
+
if (line.len) {
|
106
|
+
VALUE buffer = rb_str_new(line.data, line.len);
|
107
|
+
// make sure the buffer is binary encoded.
|
108
|
+
rb_enc_associate(buffer, IodineBinaryEncoding);
|
109
|
+
return buffer;
|
110
|
+
}
|
111
|
+
return Qnil;
|
112
|
+
}
|
113
|
+
|
114
|
+
// Reads data from the IO, according to the Rack specifications for `#read`.
|
115
|
+
static VALUE rio_read(int argc, VALUE *argv, VALUE self) {
|
116
|
+
FIOBJ io = get_data(self);
|
117
|
+
VALUE buffer = Qnil;
|
118
|
+
uint8_t ret_nil = 0;
|
119
|
+
ssize_t len = 0;
|
120
|
+
if (!FIOBJ_TYPE_IS(io, FIOBJ_T_DATA)) {
|
121
|
+
return (argc > 0 && argv[0] != Qnil) ? Qnil : rb_str_buf_new(0);
|
122
|
+
}
|
123
|
+
|
124
|
+
// get the buffer object if given
|
125
|
+
if (argc == 2) {
|
126
|
+
Check_Type(argv[1], T_STRING);
|
127
|
+
buffer = argv[1];
|
128
|
+
}
|
129
|
+
// get the length object, if given
|
130
|
+
if (argc > 0 && argv[0] != Qnil) {
|
131
|
+
Check_Type(argv[0], T_FIXNUM);
|
132
|
+
len = FIX2LONG(argv[0]);
|
133
|
+
if (len < 0)
|
134
|
+
rb_raise(rb_eRangeError, "length should be bigger then 0.");
|
135
|
+
if (len == 0)
|
136
|
+
return rb_str_buf_new(0);
|
137
|
+
ret_nil = 1;
|
138
|
+
}
|
139
|
+
// return if we're at the EOF.
|
140
|
+
fio_str_info_s buf = fiobj_data_read(io, len);
|
141
|
+
if (buf.len) {
|
142
|
+
// create the buffer if we don't have one.
|
143
|
+
if (buffer == Qnil) {
|
144
|
+
// make sure the buffer is binary encoded.
|
145
|
+
buffer = rb_enc_str_new(buf.data, buf.len, IodineBinaryEncoding);
|
146
|
+
} else {
|
147
|
+
// make sure the buffer is binary encoded.
|
148
|
+
rb_enc_associate(buffer, IodineBinaryEncoding);
|
149
|
+
if (rb_str_capacity(buffer) < (size_t)buf.len)
|
150
|
+
rb_str_resize(buffer, buf.len);
|
151
|
+
memcpy(RSTRING_PTR(buffer), buf.data, buf.len);
|
152
|
+
rb_str_set_len(buffer, buf.len);
|
153
|
+
}
|
154
|
+
return buffer;
|
155
|
+
}
|
156
|
+
return ret_nil ? Qnil : rb_str_buf_new(0);
|
157
|
+
}
|
158
|
+
|
159
|
+
// Does nothing - this is controlled by the server.
|
160
|
+
static VALUE rio_close(VALUE self) {
|
161
|
+
// FIOBJ io = get_data(self);
|
162
|
+
// fiobj_free(io); // we don't call fiobj_dup, do we?
|
163
|
+
rb_ivar_set(self, io_id, INT2NUM(0));
|
164
|
+
(void)self;
|
165
|
+
return Qnil;
|
166
|
+
}
|
167
|
+
|
168
|
+
// Passes each line of the input to the block. This should be avoided.
|
169
|
+
static VALUE rio_each(VALUE self) {
|
170
|
+
rb_need_block();
|
171
|
+
rio_rewind(self);
|
172
|
+
VALUE str = Qnil;
|
173
|
+
while ((str = rio_gets(self)) != Qnil) {
|
174
|
+
rb_yield(str);
|
175
|
+
}
|
176
|
+
return self;
|
177
|
+
}
|
178
|
+
|
179
|
+
/* *****************************************************************************
|
180
|
+
Hijacking
|
181
|
+
*/
|
182
|
+
|
183
|
+
// defined by iodine_http
|
184
|
+
extern VALUE IODINE_R_HIJACK; // for Rack: rack.hijack
|
185
|
+
extern VALUE IODINE_R_HIJACK_CB; // for Rack: rack.hijack
|
186
|
+
extern VALUE IODINE_R_HIJACK_IO; // for Rack: rack.hijack_io
|
187
|
+
|
188
|
+
static VALUE rio_get_io(int argc, VALUE *argv, VALUE self) {
|
189
|
+
if (TCPSOCKET_CLASS == Qnil)
|
190
|
+
return Qfalse;
|
191
|
+
VALUE env = rb_ivar_get(self, env_id);
|
192
|
+
http_s *h = get_handle(self);
|
193
|
+
if (h == NULL) {
|
194
|
+
/* we're repeating ourselves, aren't we? */
|
195
|
+
VALUE io = rb_hash_aref(env, IODINE_R_HIJACK_IO);
|
196
|
+
return io;
|
197
|
+
}
|
198
|
+
// mark update
|
199
|
+
set_handle(self, NULL);
|
200
|
+
// hijack the IO object
|
201
|
+
intptr_t uuid = http_hijack(h, NULL);
|
202
|
+
#ifdef __MINGW32__
|
203
|
+
int osffd = fio_osffd4fd(fio_uuid2fd(uuid));
|
204
|
+
if (osffd == -1)
|
205
|
+
return Qfalse;
|
206
|
+
VALUE fd = INT2FIX(osffd);
|
207
|
+
#else
|
208
|
+
VALUE fd = INT2FIX(fio_uuid2fd(uuid));
|
209
|
+
#endif
|
210
|
+
// VALUE new_io = how the fuck do we create a new IO from the fd?
|
211
|
+
VALUE new_io = IodineCaller.call2(TCPSOCKET_CLASS, for_fd_id, 1,
|
212
|
+
&fd); // TCPSocket.for_fd(fd) ... cool...
|
213
|
+
rb_hash_aset(env, IODINE_R_HIJACK_IO, new_io);
|
214
|
+
if (argc)
|
215
|
+
rb_hash_aset(env, IODINE_R_HIJACK_CB, *argv);
|
216
|
+
return new_io;
|
217
|
+
}
|
218
|
+
|
219
|
+
/* *****************************************************************************
|
220
|
+
C land API
|
221
|
+
*/
|
222
|
+
|
223
|
+
// new object
|
224
|
+
static VALUE new_rack_io(http_s *h, VALUE env) {
|
225
|
+
VALUE rack_io = rb_funcall2(rRackIO, iodine_new_func_id, 0, NULL);
|
226
|
+
rb_ivar_set(rack_io, io_id, ULL2NUM(h->body));
|
227
|
+
set_handle(rack_io, h);
|
228
|
+
rb_ivar_set(rack_io, env_id, env);
|
229
|
+
rb_hash_aset(env, IODINE_R_INPUT, rack_io);
|
230
|
+
rb_hash_aset(env, IODINE_R_HIJACK, rb_obj_method(rack_io, hijack_func_sym));
|
231
|
+
return rack_io;
|
232
|
+
}
|
233
|
+
|
234
|
+
static void close_rack_io(VALUE rack_io) {
|
235
|
+
// rio_close(rack_io);
|
236
|
+
rb_ivar_set(rack_io, io_id, INT2NUM(0));
|
237
|
+
set_handle(rack_io, NULL); /* this disables hijacking. */
|
238
|
+
}
|
239
|
+
|
240
|
+
// initialize library
|
241
|
+
static void init_rack_io(void) {
|
242
|
+
IodineUTF8Encoding = rb_enc_find("UTF-8");
|
243
|
+
IodineBinaryEncoding = rb_enc_find("binary");
|
244
|
+
rRackIO = rb_define_class_under(IodineBaseModule, "RackIO", rb_cObject);
|
245
|
+
|
246
|
+
io_id = rb_intern("rack_io");
|
247
|
+
env_id = rb_intern("env");
|
248
|
+
for_fd_id = rb_intern("for_fd");
|
249
|
+
iodine_fd_var_id = rb_intern("fd");
|
250
|
+
iodine_new_func_id = rb_intern("new");
|
251
|
+
#ifdef __MINGW32__
|
252
|
+
iodine_osffd_id = rb_intern("osffd");
|
253
|
+
#endif
|
254
|
+
hijack_func_sym = ID2SYM(rb_intern("_hijack"));
|
255
|
+
|
256
|
+
TCPSOCKET_CLASS = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
|
257
|
+
// IO methods
|
258
|
+
|
259
|
+
rb_define_method(rRackIO, "rewind", rio_rewind, 0);
|
260
|
+
rb_define_method(rRackIO, "gets", rio_gets, 0);
|
261
|
+
rb_define_method(rRackIO, "read", rio_read, -1);
|
262
|
+
rb_define_method(rRackIO, "close", rio_close, 0);
|
263
|
+
rb_define_method(rRackIO, "each", rio_each, 0);
|
264
|
+
rb_define_method(rRackIO, "_hijack", rio_get_io, -1);
|
265
|
+
}
|
266
|
+
|
267
|
+
////////////////////////////////////////////////////////////////////////////
|
268
|
+
// the API interface
|
269
|
+
struct IodineRackIO IodineRackIO = {
|
270
|
+
.create = new_rack_io,
|
271
|
+
.close = close_rack_io,
|
272
|
+
.init = init_rack_io,
|
273
|
+
};
|
@@ -0,0 +1,20 @@
|
|
1
|
+
/*
|
2
|
+
Copyright: Boaz segev, 2016-2017
|
3
|
+
License: MIT
|
4
|
+
|
5
|
+
Feel free to copy, use and enjoy according to the license provided.
|
6
|
+
*/
|
7
|
+
#ifndef RUBY_RACK_IO_H
|
8
|
+
#define RUBY_RACK_IO_H
|
9
|
+
|
10
|
+
#include <ruby.h>
|
11
|
+
|
12
|
+
#include "http.h"
|
13
|
+
|
14
|
+
extern struct IodineRackIO {
|
15
|
+
VALUE (*create)(http_s *h, VALUE env);
|
16
|
+
void (*close)(VALUE rack_io);
|
17
|
+
void (*init)(void);
|
18
|
+
} IodineRackIO;
|
19
|
+
|
20
|
+
#endif /* RUBY_RACK_IO_H */
|
@@ -0,0 +1,142 @@
|
|
1
|
+
#include "iodine.h"
|
2
|
+
|
3
|
+
#include "iodine_store.h"
|
4
|
+
|
5
|
+
#include <inttypes.h>
|
6
|
+
#include <stdint.h>
|
7
|
+
|
8
|
+
#define FIO_SET_NAME fio_store
|
9
|
+
#define FIO_SET_OBJ_TYPE uintptr_t
|
10
|
+
#include <fio.h>
|
11
|
+
|
12
|
+
static fio_lock_i iodine_storage_lock = FIO_LOCK_INIT;
|
13
|
+
static fio_store_s iodine_storage = FIO_SET_INIT;
|
14
|
+
static size_t iodine_storage_count_max = 0;
|
15
|
+
|
16
|
+
#ifndef IODINE_DEBUG
|
17
|
+
#define IODINE_DEBUG 0
|
18
|
+
#endif
|
19
|
+
|
20
|
+
/* *****************************************************************************
|
21
|
+
API
|
22
|
+
***************************************************************************** */
|
23
|
+
|
24
|
+
/** Adds an object to the storage (or increases it's reference count). */
|
25
|
+
static VALUE storage_add(VALUE obj) {
|
26
|
+
if (!obj || obj == Qnil || obj == Qtrue || obj == Qfalse)
|
27
|
+
return obj;
|
28
|
+
uintptr_t old = 0;
|
29
|
+
fio_lock(&iodine_storage_lock);
|
30
|
+
fio_store_overwrite(&iodine_storage, obj, 1, &old);
|
31
|
+
if (old)
|
32
|
+
fio_store_overwrite(&iodine_storage, obj, old + 1, NULL);
|
33
|
+
if (iodine_storage_count_max < fio_store_count(&iodine_storage))
|
34
|
+
iodine_storage_count_max = fio_store_count(&iodine_storage);
|
35
|
+
fio_unlock(&iodine_storage_lock);
|
36
|
+
return obj;
|
37
|
+
}
|
38
|
+
/** Removes an object from the storage (or decreases it's reference count). */
|
39
|
+
static VALUE storage_remove(VALUE obj) {
|
40
|
+
if (!obj || obj == Qnil || obj == Qtrue || obj == Qfalse ||
|
41
|
+
iodine_storage.count == 0)
|
42
|
+
return obj;
|
43
|
+
fio_lock(&iodine_storage_lock);
|
44
|
+
uintptr_t old = 0;
|
45
|
+
fio_store_remove(&iodine_storage, obj, 0, &old);
|
46
|
+
if (old > 1)
|
47
|
+
fio_store_overwrite(&iodine_storage, obj, old - 1, NULL);
|
48
|
+
fio_unlock(&iodine_storage_lock);
|
49
|
+
return obj;
|
50
|
+
}
|
51
|
+
/** Should be called after forking to reset locks */
|
52
|
+
static void storage_after_fork(void) { iodine_storage_lock = FIO_LOCK_INIT; }
|
53
|
+
|
54
|
+
/** Prints debugging information to the console. */
|
55
|
+
static void storage_print(void) {
|
56
|
+
FIO_LOG_DEBUG("Ruby <=> C Memory storage stats (pid: %d):\n", getpid());
|
57
|
+
fio_lock(&iodine_storage_lock);
|
58
|
+
uintptr_t index = 0;
|
59
|
+
FIO_SET_FOR_LOOP(&iodine_storage, pos) {
|
60
|
+
if (pos->obj) {
|
61
|
+
fprintf(stderr, "[%" PRIuPTR "] => %" PRIuPTR " X obj %p type %d\n",
|
62
|
+
index++, pos->obj, (void *)pos->hash, TYPE(pos->hash));
|
63
|
+
}
|
64
|
+
}
|
65
|
+
fprintf(stderr, "Total of %" PRIuPTR " objects protected form GC\n", index);
|
66
|
+
fprintf(stderr,
|
67
|
+
"Storage uses %" PRIuPTR " Hash bins for %" PRIuPTR " objects\n"
|
68
|
+
"The largest collection was %zu objects.\n",
|
69
|
+
iodine_storage.capa, iodine_storage.count, iodine_storage_count_max);
|
70
|
+
fio_unlock(&iodine_storage_lock);
|
71
|
+
}
|
72
|
+
|
73
|
+
/**
|
74
|
+
* Used for debugging purposes (when testing iodine for Ruby object "leaks").
|
75
|
+
*/
|
76
|
+
static VALUE storage_print_rb(VALUE self) {
|
77
|
+
storage_print();
|
78
|
+
return Qnil;
|
79
|
+
(void)self;
|
80
|
+
}
|
81
|
+
/* *****************************************************************************
|
82
|
+
GC protection
|
83
|
+
***************************************************************************** */
|
84
|
+
|
85
|
+
/* a callback for the GC (marking active objects) */
|
86
|
+
static void storage_mark(void *ignore) {
|
87
|
+
(void)ignore;
|
88
|
+
if (FIO_LOG_LEVEL >= FIO_LOG_LEVEL_DEBUG)
|
89
|
+
storage_print();
|
90
|
+
fio_lock(&iodine_storage_lock);
|
91
|
+
// fio_store_compact(&iodine_storage);
|
92
|
+
FIO_SET_FOR_LOOP(&iodine_storage, pos) {
|
93
|
+
if (pos->obj) {
|
94
|
+
rb_gc_mark((VALUE)pos->hash);
|
95
|
+
}
|
96
|
+
}
|
97
|
+
fio_unlock(&iodine_storage_lock);
|
98
|
+
}
|
99
|
+
|
100
|
+
/* clear the registry (end of lifetime) */
|
101
|
+
static void storage_clear(void *ignore) {
|
102
|
+
(void)ignore;
|
103
|
+
FIO_LOG_DEBUG("Ruby<=>C Storage cleared.\n");
|
104
|
+
fio_lock(&iodine_storage_lock);
|
105
|
+
fio_store_free(&iodine_storage);
|
106
|
+
iodine_storage = (fio_store_s)FIO_SET_INIT;
|
107
|
+
fio_unlock(&iodine_storage_lock);
|
108
|
+
}
|
109
|
+
|
110
|
+
/*
|
111
|
+
the data-type used to identify the registry
|
112
|
+
this sets the callbacks.
|
113
|
+
*/
|
114
|
+
static const struct rb_data_type_struct storage_type_struct = {
|
115
|
+
.wrap_struct_name = "RubyReferencesIn_C_Land",
|
116
|
+
.function.dfree = (void (*)(void *))storage_clear,
|
117
|
+
.function.dmark = (void (*)(void *))storage_mark,
|
118
|
+
};
|
119
|
+
|
120
|
+
/* *****************************************************************************
|
121
|
+
Initialization
|
122
|
+
***************************************************************************** */
|
123
|
+
|
124
|
+
struct IodineStorage_s IodineStore = {
|
125
|
+
.add = storage_add,
|
126
|
+
.remove = storage_remove,
|
127
|
+
.after_fork = storage_after_fork,
|
128
|
+
.print = storage_print,
|
129
|
+
};
|
130
|
+
|
131
|
+
/** Initializes the storage unit for first use. */
|
132
|
+
void iodine_storage_init(void) {
|
133
|
+
fio_store_capa_require(&iodine_storage, 512);
|
134
|
+
VALUE tmp =
|
135
|
+
rb_define_class_under(rb_cObject, "IodineObjectStorage", rb_cObject);
|
136
|
+
VALUE storage_obj =
|
137
|
+
TypedData_Wrap_Struct(tmp, &storage_type_struct, &iodine_storage);
|
138
|
+
// rb_global_variable(&iodine_storage_obj);
|
139
|
+
rb_ivar_set(IodineModule, rb_intern2("storage", 7), storage_obj);
|
140
|
+
rb_define_module_function(IodineBaseModule, "db_print_protected_objects",
|
141
|
+
storage_print_rb, 0);
|
142
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
#ifndef H_IODINE_STORAGE_H
|
2
|
+
#define H_IODINE_STORAGE_H
|
3
|
+
|
4
|
+
#include "ruby.h"
|
5
|
+
|
6
|
+
extern struct IodineStorage_s {
|
7
|
+
/** Adds an object to the storage (or increases it's reference count). */
|
8
|
+
VALUE (*add)(VALUE);
|
9
|
+
/** Removes an object from the storage (or decreases it's reference count). */
|
10
|
+
VALUE (*remove)(VALUE);
|
11
|
+
/** Should be called after forking to reset locks */
|
12
|
+
void (*after_fork)(void);
|
13
|
+
/** Prints debugging information to the console. */
|
14
|
+
void (*print)(void);
|
15
|
+
} IodineStore;
|
16
|
+
|
17
|
+
/** Initializes the storage unit for first use. */
|
18
|
+
void iodine_storage_init(void);
|
19
|
+
|
20
|
+
#endif
|