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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '05830f6522c10a7287f298b6e4b7036ae90c958e28764f485f3e1b231fe8e953'
4
- data.tar.gz: 41cfef0ab58ccdcb11f7d9437e6cc9a8454580073c89ad98701dfe9604abba74
3
+ metadata.gz: 5178eaf9e2ee165e6c1d0dbb166b8c07696152ec5102e897599c16e893171bfe
4
+ data.tar.gz: d5bfe18ca14d60aef0d2122ce32f216600b44a51d70295fa929693ac10966f04
5
5
  SHA512:
6
- metadata.gz: e3ff1d96d782bbf698ea13bd23a57972abcc94e2ce155f4863395efc67a91694997d9f44e1d2dd35e4ec5fd181064242e1ff6fb9f08e3208694b2e582a440b23
7
- data.tar.gz: c4a014449954157add87616fa7ffe376499204c02b10d313a44b7edba5ccf1a2e865e98ed883e4b9fbf0be8c00f16d0a7def400f30cb84cc4b391f882d2582b4
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 RedisEngins to easily scale iodine's Pub/Sub horizontally.
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 becasue [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.
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
 
@@ -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 nee for the plugin to ask "where's the list" or "add this code to `on_open`" or anything at all, truly "plug and play".
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
- // this is a simple helper that calls Ruby methods on Ruby objects while within
18
- // a non-GVL ruby thread zone.
19
- struct RubyArgCall {
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 run_ruby_method_unsafe(VALUE tsk_) {
29
- struct RubyArgCall *task = (void *)tsk_;
30
- return rb_funcall2(task->obj, task->method, task->argc, task->argv);
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
- // GVL gateway
61
- static void *run_ruby_method_within_gvl(void *tsk_) {
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
- task->returned = rb_protect(run_ruby_method_unsafe, (VALUE)(task), &state);
65
- if (state)
82
+ VALUE ret = rb_protect(iodine_ruby_caller_perform, (VALUE)(tsk_), &state);
83
+ if (state) {
66
84
  handle_exception(NULL);
67
- return task;
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 check_in_gvl(void) { return in_gvl; }
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 *call_c(void *(*func)(void *), void *arg) {
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(func, arg);
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
- // A simple (and a bit lighter) design for when there's no need for arguments.
92
-
93
- // wrapping any API calls for exception management AND GVL entry
94
- static VALUE call(VALUE obj, ID method) {
95
- struct RubyArgCall task = {.obj = obj, .method = method};
96
- call_c(run_ruby_method_within_gvl, &task);
97
- return task.returned;
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 call_arg(VALUE obj, ID method, int argc, VALUE *argv) {
105
- struct RubyArgCall task = {
106
- .obj = obj, .method = method, .argc = argc, .argv = argv};
107
- call_c(run_ruby_method_within_gvl, &task);
108
- return task.returned;
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 = call, .call2 = call_arg, .call_c = call_c, .in_gvl = check_in_gvl,
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 `rb_thread_call_without_gvl2` or `rb_thread_call_without_gvl`.
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 needed
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 the
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
- returns the thread's GVL status (1 if inside a GVL and 0 if free from the
55
- GVL).
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 */
@@ -1,3 +1,3 @@
1
1
  module Iodine
2
- VERSION = '0.4.16'.freeze
2
+ VERSION = '0.4.17'.freeze
3
3
  end
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.16
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: 2017-12-29 00:00:00.000000000 Z
11
+ date: 2018-03-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack