iodine 0.4.16 → 0.4.17
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of iodine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +6 -2
- data/SPEC-Websocket-Draft.md +1 -1
- data/ext/iodine/rb-call.c +71 -31
- data/ext/iodine/rb-call.h +19 -9
- data/lib/iodine/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5178eaf9e2ee165e6c1d0dbb166b8c07696152ec5102e897599c16e893171bfe
|
4
|
+
data.tar.gz: d5bfe18ca14d60aef0d2122ce32f216600b44a51d70295fa929693ac10966f04
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c982ce0213871aca6041636d5091d09522e6485b31b699ce80aed70ad3d9bf13daeca857d89f6f697b73b401f4bee8ca7a7c0aa5e554c13e2785ebcb21f3c198
|
7
|
+
data.tar.gz: 6a304583f391cbaaa41271277ab291f50c37d34984069e8531cf068d382f2490eaaab4bce488262aa78c8230e6d6cd34e9827500bd5d30bf678fd024d5d55d00
|
data/CHANGELOG.md
CHANGED
@@ -8,6 +8,10 @@ Please notice that this change log contains changes for upcoming releases as wel
|
|
8
8
|
|
9
9
|
---
|
10
10
|
|
11
|
+
#### Change log v.0.4.17
|
12
|
+
|
13
|
+
**Fix**: (`iodine RubyCaller`) fixed issue #26 that exposed an issue in the exception handling logic. This fix enforces exception handling whenever entering the Ruby GVL (GIL), allowing C functions to safely enter the user's Ruby code (where before C functions were assumed to be safe and user code would be executed unprotected when routed through certain functions). Credit to @haukot for exposing this issue (issue #26).
|
14
|
+
|
11
15
|
#### Change log v.0.4.16
|
12
16
|
|
13
17
|
**Fix**: (`websocket_parser`) The websocket parser had a memory offset and alignment handling issue in it's unmasking (XOR) logic and the new memory alignment protection code. The issue would impact the parser in rare occasions when multiple messages where pipelined in the internal buffer and their length produced an odd alignment (the issue would occur with very fast clients, or a very stressed server).
|
data/README.md
CHANGED
@@ -6,6 +6,10 @@
|
|
6
6
|
[![Inline docs](http://inch-ci.org/github/boazsegev/iodine.svg?branch=master)](http://www.rubydoc.info/github/boazsegev/iodine/master/frames)
|
7
7
|
[![GitHub](https://img.shields.io/badge/GitHub-Open%20Source-blue.svg)](https://github.com/boazsegev/iodine)
|
8
8
|
|
9
|
+
***Notice*: iodine's core library, [facil.io](https://github.com/boazsegev/facil.io) is being re-vamped with many updates and changes. This is a time to ask - what features are important for you? [let me know here](https://github.com/boazsegev/facil.io/issues/24)**.
|
10
|
+
|
11
|
+
**The development branch is the `v.0.5-rewrite` branch.**
|
12
|
+
|
9
13
|
Iodine is a fast concurrent web server for real-time Ruby applications, with native support for:
|
10
14
|
|
11
15
|
* Websockets;
|
@@ -31,7 +35,7 @@ Iodine includes a light and fast HTTP and Websocket server written in C that was
|
|
31
35
|
|
32
36
|
With `Iodine.listen2http` it's possible to run multiple HTTP applications in addition to (or instead of) the default `Iodine::Rack` HTTP service.
|
33
37
|
|
34
|
-
Iodine also supports native process cluster Pub/Sub and a native
|
38
|
+
Iodine also supports native process cluster Pub/Sub and a native RedisEngine to easily scale iodine's Pub/Sub horizontally.
|
35
39
|
|
36
40
|
### Running the web server
|
37
41
|
|
@@ -214,7 +218,7 @@ end
|
|
214
218
|
|
215
219
|
Another added bonus is pattern publishing (is addition to pattern subscriptions) which isn't available when using Redis (since Redis doesn't support this feature).
|
216
220
|
|
217
|
-
* Iodine's Redis client does *not* support multiple databases. This is both
|
221
|
+
* Iodine's Redis client does *not* support multiple databases. This is both because [database scoping is ignored by Redis during pub/sub](https://redis.io/topics/pubsub#database-amp-scoping) and because [Redis Cluster doesn't support multiple databases](https://redis.io/topics/cluster-spec). This indicated that multiple database support just isn't worth the extra effort.
|
218
222
|
|
219
223
|
* The iodine Redis client will use two Redis connections per process (one for subscriptions and the other for publishing and commands). Both connections will be automatically re-established if timeouts or errors occur.
|
220
224
|
|
data/SPEC-Websocket-Draft.md
CHANGED
@@ -99,7 +99,7 @@ The requirement that the server extends the class of the Websocket Callback Obje
|
|
99
99
|
|
100
100
|
## Iodine's Collection Extension to the Rack Websockets
|
101
101
|
|
102
|
-
The biggest benefit from Iodine's Collection Extension is that it allows the creation of pub/sub plugins and other similar extensions that require access to all the connected Websockets - no
|
102
|
+
The biggest benefit from Iodine's Collection Extension is that it allows the creation of pub/sub plugins and other similar extensions that require access to all the connected Websockets - no need for the plugin to ask "where's the list" or "add this code to `on_open`" or anything at all, truly "plug and play".
|
103
103
|
|
104
104
|
This extension should be easy enough to implement using a loop or an array within each process context. These methods do **not** cross process boundaries and they are meant to help implement more advanced features (they aren't a feature as much as an "access point").
|
105
105
|
|
data/ext/iodine/rb-call.c
CHANGED
@@ -13,21 +13,39 @@ Feel free to copy, use and enjoy according to the license provided.
|
|
13
13
|
#define _Thread_local __thread
|
14
14
|
#endif
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
typedef enum {
|
17
|
+
RUBY_TASK,
|
18
|
+
C_TASK,
|
19
|
+
} iodine_task_type_en;
|
20
|
+
|
21
|
+
typedef struct {
|
22
|
+
iodine_task_type_en type;
|
20
23
|
VALUE obj;
|
21
24
|
int argc;
|
22
25
|
VALUE *argv;
|
23
26
|
VALUE returned;
|
24
27
|
ID method;
|
25
|
-
|
28
|
+
int exception;
|
29
|
+
} iodine_rb_task_s;
|
30
|
+
|
31
|
+
typedef struct {
|
32
|
+
iodine_task_type_en type;
|
33
|
+
void *(*func)(void *);
|
34
|
+
void *arg;
|
35
|
+
} iodine_c_task_s;
|
26
36
|
|
27
37
|
// running the actual method call
|
28
|
-
static VALUE
|
29
|
-
|
30
|
-
|
38
|
+
static VALUE iodine_ruby_caller_perform(VALUE tsk_) {
|
39
|
+
switch (*(iodine_task_type_en *)tsk_) {
|
40
|
+
case RUBY_TASK: {
|
41
|
+
iodine_rb_task_s *task = (void *)tsk_;
|
42
|
+
return rb_funcall2(task->obj, task->method, task->argc, task->argv);
|
43
|
+
}
|
44
|
+
case C_TASK: {
|
45
|
+
iodine_c_task_s *task = (void *)tsk_;
|
46
|
+
return (VALUE)task->func(task->arg);
|
47
|
+
}
|
48
|
+
}
|
31
49
|
}
|
32
50
|
|
33
51
|
////////////////////////////////////////////////////////////////////////////
|
@@ -52,19 +70,20 @@ static void *handle_exception(void *ignr) {
|
|
52
70
|
(int)RSTRING_LEN(msg), RSTRING_PTR(msg));
|
53
71
|
}
|
54
72
|
rb_backtrace();
|
73
|
+
fprintf(stderr, "\n");
|
55
74
|
rb_set_errinfo(Qnil);
|
56
75
|
}
|
57
76
|
return (void *)Qnil;
|
58
77
|
}
|
59
78
|
|
60
|
-
|
61
|
-
static void *
|
62
|
-
struct RubyArgCall *task = tsk_;
|
79
|
+
/* wrap the function call in the exception handling code */
|
80
|
+
static void *iodine_protected_call(void *tsk_) {
|
63
81
|
int state = 0;
|
64
|
-
|
65
|
-
if (state)
|
82
|
+
VALUE ret = rb_protect(iodine_ruby_caller_perform, (VALUE)(tsk_), &state);
|
83
|
+
if (state) {
|
66
84
|
handle_exception(NULL);
|
67
|
-
|
85
|
+
}
|
86
|
+
return (void *)ret;
|
68
87
|
}
|
69
88
|
|
70
89
|
////////////////////////////////////////////////////////////////////////////
|
@@ -72,44 +91,65 @@ static void *run_ruby_method_within_gvl(void *tsk_) {
|
|
72
91
|
|
73
92
|
// a thread specific global variable that lets us know if we're in the GVL
|
74
93
|
static _Thread_local char in_gvl = 0;
|
75
|
-
static char
|
94
|
+
static char iodine_rb_check_in_gvl(void) { return in_gvl; }
|
95
|
+
|
96
|
+
static void iodine_rb_set_gvl_state(char state) { in_gvl = state; }
|
76
97
|
|
77
98
|
////////////////////////////////////////////////////////////////////////////
|
78
99
|
// Calling C functions.
|
79
|
-
static void *
|
100
|
+
static inline void *iodine_rb_call_c(void *(*func)(void *), void *arg) {
|
80
101
|
if (in_gvl) {
|
81
102
|
return func(arg);
|
82
103
|
}
|
104
|
+
iodine_c_task_s task = {.type = C_TASK, .func = func, .arg = arg};
|
83
105
|
void *ret;
|
84
106
|
in_gvl = 1;
|
85
|
-
ret = rb_thread_call_with_gvl(
|
107
|
+
ret = rb_thread_call_with_gvl(iodine_protected_call, &task);
|
86
108
|
in_gvl = 0;
|
87
109
|
return ret;
|
88
110
|
}
|
89
111
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
return
|
112
|
+
static void *iodine_rb_leave_gvl(void *(*func)(void *), void *arg) {
|
113
|
+
if (!in_gvl) {
|
114
|
+
return func(arg);
|
115
|
+
}
|
116
|
+
in_gvl = 0;
|
117
|
+
void *ret = rb_thread_call_without_gvl(func, arg, NULL, NULL);
|
118
|
+
in_gvl = 1;
|
119
|
+
return ret;
|
98
120
|
}
|
99
121
|
|
100
122
|
////////////////////////////////////////////////////////////////////////////
|
101
123
|
// A heavier (memory) design for when we're passing arguments around.
|
102
124
|
|
103
125
|
// wrapping any API calls for exception management AND GVL entry
|
104
|
-
static VALUE
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
126
|
+
static VALUE iodin_rb_call_arg(VALUE obj, ID method, int argc, VALUE *argv) {
|
127
|
+
iodine_rb_task_s task = {.type = RUBY_TASK,
|
128
|
+
.obj = obj,
|
129
|
+
.method = method,
|
130
|
+
.argc = argc,
|
131
|
+
.argv = argv};
|
132
|
+
void *ret;
|
133
|
+
if (in_gvl)
|
134
|
+
return (VALUE)rb_funcall2(obj, method, argc, argv);
|
135
|
+
in_gvl = 1;
|
136
|
+
ret = rb_thread_call_with_gvl(iodine_protected_call, &task);
|
137
|
+
in_gvl = 0;
|
138
|
+
return (VALUE)ret;
|
139
|
+
}
|
140
|
+
|
141
|
+
// wrapping any API calls for exception management AND GVL entry
|
142
|
+
static VALUE iodin_rb_call(VALUE obj, ID method) {
|
143
|
+
return iodin_rb_call_arg(obj, method, 0, NULL);
|
109
144
|
}
|
110
145
|
|
111
146
|
////////////////////////////////////////////////////////////////////////////
|
112
147
|
// the API interface
|
113
148
|
struct _Ruby_Method_Caller_Class_ RubyCaller = {
|
114
|
-
.call =
|
149
|
+
.call = iodin_rb_call,
|
150
|
+
.call2 = iodin_rb_call_arg,
|
151
|
+
.call_c = iodine_rb_call_c,
|
152
|
+
.leave_gvl = iodine_rb_leave_gvl,
|
153
|
+
.in_gvl = iodine_rb_check_in_gvl,
|
154
|
+
.set_gvl_state = iodine_rb_set_gvl_state,
|
115
155
|
};
|
data/ext/iodine/rb-call.h
CHANGED
@@ -36,25 +36,35 @@ This library keeps track of the thread, adjusting the method to be called in
|
|
36
36
|
case the thread is already within the GVL.
|
37
37
|
|
38
38
|
The library assums that it is within a Ruby thread that is was released of
|
39
|
-
the GVL using `
|
39
|
+
the GVL using `rb_thread_call_without_gvl`. If this isn t the case, use the
|
40
|
+
`RubyCaller.set_gvl_state` function to manually set the GVL state assumption.
|
40
41
|
*/
|
41
42
|
extern struct _Ruby_Method_Caller_Class_ {
|
42
43
|
/** calls a Object's ruby method, adjusting for the GVL if needed */
|
43
44
|
VALUE (*call)(VALUE object, ID method_id);
|
44
45
|
/**
|
45
|
-
calls a Object's ruby method with arguments, adjusting for the GVL if
|
46
|
-
|
46
|
+
* calls a Object's ruby method with arguments, adjusting for the GVL if
|
47
|
+
* needed
|
48
|
+
*/
|
47
49
|
VALUE (*call2)(VALUE obj, ID method, int argc, VALUE *argv);
|
48
50
|
/**
|
49
|
-
calls a C method that requires the GVL for access to the Ruby API, managing
|
50
|
-
GVL state as required.
|
51
|
-
|
51
|
+
* calls a C method that requires the GVL for access to the Ruby API, managing
|
52
|
+
* the GVL state as required.
|
53
|
+
*/
|
52
54
|
void *(*call_c)(void *(*func)(void *), void *arg);
|
53
55
|
/**
|
54
|
-
|
55
|
-
|
56
|
-
|
56
|
+
* Exits the GVL (if the thread is withing the GVL) and calls a C function.
|
57
|
+
*/
|
58
|
+
void *(*leave_gvl)(void *(*func)(void *), void *arg);
|
59
|
+
/**
|
60
|
+
* returns the thread's GVL status (1 if inside a GVL and 0 if free from the
|
61
|
+
* GVL).
|
62
|
+
*/
|
57
63
|
char (*in_gvl)(void);
|
64
|
+
/**
|
65
|
+
* forces a thread's GVL state flag (ignoring the actual GVL state) - careful!
|
66
|
+
*/
|
67
|
+
void (*set_gvl_state)(char state);
|
58
68
|
} RubyCaller;
|
59
69
|
|
60
70
|
#endif /* RB_CALL_H */
|
data/lib/iodine/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: iodine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.17
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Boaz Segev
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-03-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|