iodine 0.7.60 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.clang-format +15 -0
- data/.rspec +1 -0
- data/.yardopts +1 -3
- data/CHANGELOG.md +132 -12
- data/Gemfile +5 -7
- data/LICENSE.txt +1 -1
- data/README.md +147 -671
- data/Rakefile +9 -16
- data/examples/chatroom.nru +9 -0
- data/examples/client +74 -0
- data/examples/config-hello.nru +9 -0
- data/examples/config-hello.ru +11 -0
- data/examples/config-hijack.ru +13 -0
- data/examples/config-shootout.ru +43 -0
- data/examples/config.nru +120 -0
- data/examples/config.rb +37 -0
- data/examples/config.ru +42 -53
- data/examples/fake +24 -0
- data/examples/hello_neorack +137 -0
- data/examples/routes.nru +29 -0
- data/examples/routes.ru +42 -0
- data/exe/iodine +218 -258
- data/ext/iodine/extconf.rb +27 -100
- data/ext/iodine/fio-stl.h +128336 -0
- data/ext/iodine/fio-stl.md +26396 -0
- data/ext/iodine/iodine.c +213 -1393
- data/ext/iodine/iodine.h +250 -55
- data/ext/iodine/iodine_arg_helper.h +329 -0
- data/ext/iodine/iodine_caller.h +234 -25
- data/ext/iodine/iodine_cli.h +434 -0
- data/ext/iodine/iodine_compression.h +312 -0
- data/ext/iodine/iodine_connection.h +3484 -34
- data/ext/iodine/iodine_core.h +363 -0
- data/ext/iodine/iodine_crypto.h +1441 -0
- data/ext/iodine/iodine_defer.h +225 -4
- data/ext/iodine/iodine_json.h +738 -4
- data/ext/iodine/iodine_listener.h +263 -0
- data/ext/iodine/iodine_log.h +297 -0
- data/ext/iodine/iodine_minimap.h +543 -0
- data/ext/iodine/iodine_mustache.h +461 -4
- data/ext/iodine/iodine_pubsub_eng.h +729 -0
- data/ext/iodine/iodine_pubsub_history.h +547 -0
- data/ext/iodine/iodine_pubsub_msg.h +228 -0
- data/ext/iodine/iodine_redis.h +322 -0
- data/ext/iodine/iodine_store.h +418 -16
- data/ext/iodine/iodine_threads.h +279 -0
- data/ext/iodine/iodine_tls.h +515 -6
- data/ext/iodine/iodine_utils.h +923 -0
- data/iodine.gemspec +37 -42
- data/lib/iodine/benchmark.rb +1690 -0
- data/lib/iodine/documentation.rb +3085 -0
- data/lib/iodine/version.rb +17 -1
- data/lib/iodine.rb +54 -255
- data/logo.svg +36 -0
- data/mustache.md +55 -0
- data/sig/iodine.rbs +4 -0
- metadata +58 -184
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
- data/.github/workflows/ruby.yml +0 -42
- data/.gitignore +0 -21
- data/LIMITS.md +0 -41
- data/SPEC-PubSub-Draft.md +0 -159
- data/SPEC-WebSocket-Draft.md +0 -239
- data/bin/console +0 -22
- data/bin/info.md +0 -353
- data/bin/mustache_bench.rb +0 -100
- data/bin/poc/Gemfile.lock +0 -23
- data/bin/poc/README.md +0 -37
- data/bin/poc/config.ru +0 -66
- data/bin/poc/gemfile +0 -1
- data/bin/poc/www/index.html +0 -57
- data/examples/async_task.ru +0 -92
- data/examples/bates/README.md +0 -3
- data/examples/bates/config.ru +0 -342
- data/examples/bates/david+bold.pdf +0 -0
- data/examples/bates/public/drop-pdf.png +0 -0
- data/examples/bates/public/index.html +0 -600
- data/examples/echo.ru +0 -59
- data/examples/etag.ru +0 -16
- data/examples/hello.ru +0 -29
- data/examples/pubsub_engine.ru +0 -81
- data/examples/rack3.ru +0 -12
- data/examples/redis.ru +0 -70
- data/examples/shootout.ru +0 -73
- data/examples/sub-protocols.ru +0 -90
- data/examples/tcp_client.rb +0 -66
- data/examples/x-sendfile.ru +0 -14
- data/ext/iodine/fio.c +0 -11995
- data/ext/iodine/fio.h +0 -6371
- data/ext/iodine/fio_cli.c +0 -431
- data/ext/iodine/fio_cli.h +0 -189
- data/ext/iodine/fio_json_parser.h +0 -688
- data/ext/iodine/fio_siphash.c +0 -157
- data/ext/iodine/fio_siphash.h +0 -37
- data/ext/iodine/fio_tls.h +0 -129
- data/ext/iodine/fio_tls_missing.c +0 -649
- data/ext/iodine/fio_tls_openssl.c +0 -1106
- data/ext/iodine/fio_tmpfile.h +0 -50
- data/ext/iodine/fiobj.h +0 -44
- data/ext/iodine/fiobj4fio.h +0 -21
- data/ext/iodine/fiobj_ary.c +0 -333
- data/ext/iodine/fiobj_ary.h +0 -139
- data/ext/iodine/fiobj_data.c +0 -1187
- data/ext/iodine/fiobj_data.h +0 -167
- data/ext/iodine/fiobj_hash.c +0 -409
- data/ext/iodine/fiobj_hash.h +0 -176
- data/ext/iodine/fiobj_json.c +0 -622
- data/ext/iodine/fiobj_json.h +0 -68
- data/ext/iodine/fiobj_mem.h +0 -71
- data/ext/iodine/fiobj_mustache.c +0 -317
- data/ext/iodine/fiobj_mustache.h +0 -62
- data/ext/iodine/fiobj_numbers.c +0 -344
- data/ext/iodine/fiobj_numbers.h +0 -127
- data/ext/iodine/fiobj_str.c +0 -433
- data/ext/iodine/fiobj_str.h +0 -172
- data/ext/iodine/fiobject.c +0 -620
- data/ext/iodine/fiobject.h +0 -655
- data/ext/iodine/hpack.h +0 -1923
- data/ext/iodine/http.c +0 -2743
- data/ext/iodine/http.h +0 -1002
- data/ext/iodine/http1.c +0 -910
- data/ext/iodine/http1.h +0 -29
- data/ext/iodine/http1_parser.h +0 -878
- data/ext/iodine/http_internal.c +0 -1279
- data/ext/iodine/http_internal.h +0 -237
- data/ext/iodine/http_mime_parser.h +0 -350
- data/ext/iodine/iodine_caller.c +0 -230
- data/ext/iodine/iodine_connection.c +0 -941
- data/ext/iodine/iodine_defer.c +0 -420
- data/ext/iodine/iodine_fiobj2rb.h +0 -120
- data/ext/iodine/iodine_helpers.c +0 -282
- data/ext/iodine/iodine_helpers.h +0 -12
- data/ext/iodine/iodine_http.c +0 -1181
- data/ext/iodine/iodine_http.h +0 -23
- data/ext/iodine/iodine_json.c +0 -302
- data/ext/iodine/iodine_mustache.c +0 -567
- data/ext/iodine/iodine_pubsub.c +0 -580
- data/ext/iodine/iodine_pubsub.h +0 -26
- data/ext/iodine/iodine_rack_io.c +0 -273
- data/ext/iodine/iodine_rack_io.h +0 -20
- data/ext/iodine/iodine_store.c +0 -148
- data/ext/iodine/iodine_tcp.c +0 -346
- data/ext/iodine/iodine_tcp.h +0 -13
- data/ext/iodine/iodine_tls.c +0 -261
- data/ext/iodine/mustache_parser.h +0 -1546
- data/ext/iodine/redis_engine.c +0 -957
- data/ext/iodine/redis_engine.h +0 -79
- data/ext/iodine/resp_parser.h +0 -317
- data/ext/iodine/websocket_parser.h +0 -506
- data/ext/iodine/websockets.c +0 -743
- data/ext/iodine/websockets.h +0 -185
- data/lib/iodine/connection.rb +0 -61
- data/lib/iodine/json.rb +0 -42
- data/lib/iodine/mustache.rb +0 -113
- data/lib/iodine/pubsub.rb +0 -55
- data/lib/iodine/rack_utils.rb +0 -43
- data/lib/iodine/tls.rb +0 -16
- data/lib/rack/handler/iodine.rb +0 -33
- data/logo.png +0 -0
|
@@ -0,0 +1,729 @@
|
|
|
1
|
+
#ifndef H___IODINE_PUBSUB_ENG___H
|
|
2
|
+
#define H___IODINE_PUBSUB_ENG___H
|
|
3
|
+
#include "iodine.h"
|
|
4
|
+
|
|
5
|
+
/* *****************************************************************************
|
|
6
|
+
Iodine PubSub Engine - Custom Publish/Subscribe Engine Support
|
|
7
|
+
|
|
8
|
+
This module provides the Iodine::PubSub::Engine Ruby class which allows
|
|
9
|
+
creating custom PubSub engines that can bridge Iodine's internal pub/sub
|
|
10
|
+
system with external message brokers (Redis, RabbitMQ, etc.).
|
|
11
|
+
|
|
12
|
+
A custom engine can implement any of these callbacks:
|
|
13
|
+
- subscribe(channel) - Called when subscribing to a channel
|
|
14
|
+
- psubscribe(pattern) - Called when subscribing to a pattern
|
|
15
|
+
- unsubscribe(channel) - Called when unsubscribing from a channel
|
|
16
|
+
- punsubscribe(pattern) - Called when unsubscribing from a pattern
|
|
17
|
+
- publish(message) - Called when publishing a message
|
|
18
|
+
- on_cleanup - Called when engine is detached
|
|
19
|
+
|
|
20
|
+
Built-in engines (constants on Iodine::PubSub):
|
|
21
|
+
- LOCAL - Publish within the local machine (master + all workers)
|
|
22
|
+
- CLUSTER - Publish to all workers across the cluster (default)
|
|
23
|
+
|
|
24
|
+
Ruby API:
|
|
25
|
+
- Iodine::PubSub.default = engine - Set default engine
|
|
26
|
+
- Iodine::PubSub.default - Get default engine
|
|
27
|
+
- Iodine::PubSub::Engine.new - Create custom engine (subclass this)
|
|
28
|
+
***************************************************************************** */
|
|
29
|
+
|
|
30
|
+
/* *****************************************************************************
|
|
31
|
+
Ruby PubSub Engine Type
|
|
32
|
+
***************************************************************************** */
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Internal structure representing a PubSub engine.
|
|
36
|
+
*
|
|
37
|
+
* Wraps a facil.io pubsub engine with a Ruby handler object that
|
|
38
|
+
* receives callbacks for subscribe/unsubscribe/publish operations.
|
|
39
|
+
*/
|
|
40
|
+
typedef struct iodine_pubsub_eng_s {
|
|
41
|
+
fio_pubsub_engine_s engine; /**< The facil.io engine callbacks */
|
|
42
|
+
fio_pubsub_engine_s *ptr; /**< Pointer to engine (self or built-in) */
|
|
43
|
+
VALUE handler; /**< Ruby handler object (self) */
|
|
44
|
+
} iodine_pubsub_eng_s;
|
|
45
|
+
|
|
46
|
+
/* *****************************************************************************
|
|
47
|
+
Ruby PubSub Engine Bridge - C to Ruby Callback Wrappers
|
|
48
|
+
|
|
49
|
+
These functions bridge the facil.io C callbacks to Ruby method calls.
|
|
50
|
+
They handle GVL acquisition and Ruby object creation/cleanup.
|
|
51
|
+
***************************************************************************** */
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Arguments passed to GVL-wrapped callback functions.
|
|
55
|
+
*/
|
|
56
|
+
typedef struct iodine_pubsub_eng___args_s {
|
|
57
|
+
iodine_pubsub_eng_s *eng; /**< The engine receiving the callback */
|
|
58
|
+
const fio_pubsub_msg_s *msg; /**< Message for publish callbacks */
|
|
59
|
+
fio_buf_info_s channel; /**< Channel name for subscribe callbacks */
|
|
60
|
+
int16_t filter; /**< Filter value (reserved) */
|
|
61
|
+
} iodine_pubsub_eng___args_s;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Called after the engine was detached from the pubsub system.
|
|
65
|
+
* Invokes the Ruby handler's `on_cleanup` method for resource cleanup.
|
|
66
|
+
*
|
|
67
|
+
* @param eng The detached engine
|
|
68
|
+
*/
|
|
69
|
+
static void iodine_pubsub_eng___detached(const fio_pubsub_engine_s *eng) {
|
|
70
|
+
iodine_pubsub_eng_s *e = (iodine_pubsub_eng_s *)eng;
|
|
71
|
+
iodine_ruby_call_anywhere(e->handler, rb_intern("on_cleanup"));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
static void *iodine_pubsub_eng___subscribe__in_GC(void *a_) {
|
|
75
|
+
iodine_pubsub_eng___args_s *args = (iodine_pubsub_eng___args_s *)a_;
|
|
76
|
+
VALUE ch = rb_str_new(args->channel.buf, args->channel.len);
|
|
77
|
+
STORE.cache(ch);
|
|
78
|
+
iodine_ruby_call_inside(args->eng->handler, rb_intern("subscribe"), 1, &ch);
|
|
79
|
+
return NULL;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Subscribes to a channel. Called ONLY in the Root (master) process.
|
|
83
|
+
* Invokes the Ruby handler's `subscribe` method with the channel name.
|
|
84
|
+
*
|
|
85
|
+
* @param eng The pubsub engine
|
|
86
|
+
* @param channel The channel name to subscribe to
|
|
87
|
+
* @param filter Filter value (reserved for future use)
|
|
88
|
+
*/
|
|
89
|
+
static void iodine_pubsub_eng___subscribe(const fio_pubsub_engine_s *eng,
|
|
90
|
+
fio_buf_info_s channel,
|
|
91
|
+
int16_t filter) {
|
|
92
|
+
iodine_pubsub_eng___args_s args = {
|
|
93
|
+
.eng = (iodine_pubsub_eng_s *)eng,
|
|
94
|
+
.channel = channel,
|
|
95
|
+
.filter = filter,
|
|
96
|
+
};
|
|
97
|
+
iodine_c_call_with(iodine_pubsub_eng___subscribe__in_GC, &args);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
static void *iodine_pubsub_eng___psubscribe__in_GC(void *a_) {
|
|
101
|
+
iodine_pubsub_eng___args_s *args = (iodine_pubsub_eng___args_s *)a_;
|
|
102
|
+
VALUE ch = rb_str_new(args->channel.buf, args->channel.len);
|
|
103
|
+
STORE.cache(ch);
|
|
104
|
+
iodine_ruby_call_inside(args->eng->handler, rb_intern("psubscribe"), 1, &ch);
|
|
105
|
+
return NULL;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Subscribes to a pattern. Called ONLY in the Root (master) process.
|
|
109
|
+
* Invokes the Ruby handler's `psubscribe` method with the pattern.
|
|
110
|
+
*
|
|
111
|
+
* @param eng The pubsub engine
|
|
112
|
+
* @param channel The pattern to subscribe to
|
|
113
|
+
* @param filter Filter value (reserved for future use)
|
|
114
|
+
*/
|
|
115
|
+
static void iodine_pubsub_eng___psubscribe(const fio_pubsub_engine_s *eng,
|
|
116
|
+
fio_buf_info_s channel,
|
|
117
|
+
int16_t filter) {
|
|
118
|
+
iodine_pubsub_eng___args_s args = {
|
|
119
|
+
.eng = (iodine_pubsub_eng_s *)eng,
|
|
120
|
+
.channel = channel,
|
|
121
|
+
.filter = filter,
|
|
122
|
+
};
|
|
123
|
+
iodine_c_call_with(iodine_pubsub_eng___psubscribe__in_GC, &args);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
static void *iodine_pubsub_eng___unsubscribe__in_GC(void *a_) {
|
|
127
|
+
iodine_pubsub_eng___args_s *args = (iodine_pubsub_eng___args_s *)a_;
|
|
128
|
+
VALUE ch = rb_str_new(args->channel.buf, args->channel.len);
|
|
129
|
+
STORE.cache(ch);
|
|
130
|
+
iodine_ruby_call_inside(args->eng->handler, rb_intern("unsubscribe"), 1, &ch);
|
|
131
|
+
return NULL;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Unsubscribes from a channel. Called ONLY in the Root (master) process.
|
|
135
|
+
* Invokes the Ruby handler's `unsubscribe` method with the channel name.
|
|
136
|
+
*
|
|
137
|
+
* @param eng The pubsub engine
|
|
138
|
+
* @param channel The channel name to unsubscribe from
|
|
139
|
+
* @param filter Filter value (reserved for future use)
|
|
140
|
+
*/
|
|
141
|
+
static void iodine_pubsub_eng___unsubscribe(const fio_pubsub_engine_s *eng,
|
|
142
|
+
fio_buf_info_s channel,
|
|
143
|
+
int16_t filter) {
|
|
144
|
+
iodine_pubsub_eng___args_s args = {
|
|
145
|
+
.eng = (iodine_pubsub_eng_s *)eng,
|
|
146
|
+
.channel = channel,
|
|
147
|
+
.filter = filter,
|
|
148
|
+
};
|
|
149
|
+
iodine_c_call_with(iodine_pubsub_eng___unsubscribe__in_GC, &args);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
static void *iodine_pubsub_eng___punsubscribe__in_GC(void *a_) {
|
|
153
|
+
iodine_pubsub_eng___args_s *args = (iodine_pubsub_eng___args_s *)a_;
|
|
154
|
+
VALUE ch = rb_str_new(args->channel.buf, args->channel.len);
|
|
155
|
+
STORE.cache(ch);
|
|
156
|
+
iodine_ruby_call_inside(args->eng->handler,
|
|
157
|
+
rb_intern("punsubscribe"),
|
|
158
|
+
1,
|
|
159
|
+
&ch);
|
|
160
|
+
return NULL;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Unsubscribes from a pattern. Called ONLY in the Root (master) process.
|
|
164
|
+
* Invokes the Ruby handler's `punsubscribe` method with the pattern.
|
|
165
|
+
*
|
|
166
|
+
* @param eng The pubsub engine
|
|
167
|
+
* @param channel The pattern to unsubscribe from
|
|
168
|
+
* @param filter Filter value (reserved for future use)
|
|
169
|
+
*/
|
|
170
|
+
static void iodine_pubsub_eng___punsubscribe(const fio_pubsub_engine_s *eng,
|
|
171
|
+
fio_buf_info_s channel,
|
|
172
|
+
int16_t filter) {
|
|
173
|
+
iodine_pubsub_eng___args_s args = {
|
|
174
|
+
.eng = (iodine_pubsub_eng_s *)eng,
|
|
175
|
+
.channel = channel,
|
|
176
|
+
.filter = filter,
|
|
177
|
+
};
|
|
178
|
+
iodine_c_call_with(iodine_pubsub_eng___punsubscribe__in_GC, &args);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
static void *iodine_pubsub_eng___publish__in_GC(void *a_) {
|
|
182
|
+
iodine_pubsub_eng___args_s *args = (iodine_pubsub_eng___args_s *)a_;
|
|
183
|
+
VALUE msg = iodine_pubsub_msg_new(args->msg);
|
|
184
|
+
STORE.cache(msg);
|
|
185
|
+
iodine_ruby_call_inside(args->eng->handler, rb_intern("publish"), 1, &msg);
|
|
186
|
+
return NULL;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Publishes a message through the engine. Called by any worker/thread.
|
|
190
|
+
* Invokes the Ruby handler's `publish` method with a Message object.
|
|
191
|
+
*
|
|
192
|
+
* publish is called INLINE by fio_pubsub_publish() — GVL state is ambiguous.
|
|
193
|
+
* When Ruby calls Iodine.publish with a custom engine, we already have GVL.
|
|
194
|
+
* When called from IO thread / IPC delivery, we don't.
|
|
195
|
+
*
|
|
196
|
+
* @param eng The pubsub engine
|
|
197
|
+
* @param msg The message to publish
|
|
198
|
+
*/
|
|
199
|
+
static void iodine_pubsub_eng___publish(const fio_pubsub_engine_s *eng,
|
|
200
|
+
const fio_pubsub_msg_s *msg) {
|
|
201
|
+
iodine_pubsub_eng___args_s args = {
|
|
202
|
+
.eng = (iodine_pubsub_eng_s *)eng,
|
|
203
|
+
.msg = msg,
|
|
204
|
+
};
|
|
205
|
+
iodine_c_call_with(iodine_pubsub_eng___publish__in_GC, &args);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Validates a Ruby object and creates a pubsub engine struct.
|
|
210
|
+
*
|
|
211
|
+
* Checks which callback methods the Ruby object responds to and
|
|
212
|
+
* sets up the corresponding C callbacks. Methods not implemented
|
|
213
|
+
* by the Ruby object will have NULL callbacks.
|
|
214
|
+
*
|
|
215
|
+
* @param obj The Ruby engine object to validate
|
|
216
|
+
* @return A configured fio_pubsub_engine_s struct
|
|
217
|
+
*/
|
|
218
|
+
static fio_pubsub_engine_s iodine_pubsub___engine_validate(VALUE obj) {
|
|
219
|
+
fio_pubsub_engine_s r = {
|
|
220
|
+
/* Called after the engine was detached, may be used for cleanup. */
|
|
221
|
+
.detached = (rb_respond_to(obj, rb_intern("on_cleanup")))
|
|
222
|
+
? iodine_pubsub_eng___detached
|
|
223
|
+
: NULL,
|
|
224
|
+
/* Subscribes to a channel. Called ONLY in the Root (master) process. */
|
|
225
|
+
.subscribe = (rb_respond_to(obj, rb_intern("subscribe")))
|
|
226
|
+
? iodine_pubsub_eng___subscribe
|
|
227
|
+
: NULL,
|
|
228
|
+
/* Subscribes to a pattern. Called ONLY in the Root (master) process. */
|
|
229
|
+
.psubscribe = (rb_respond_to(obj, rb_intern("psubscribe")))
|
|
230
|
+
? iodine_pubsub_eng___psubscribe
|
|
231
|
+
: NULL,
|
|
232
|
+
/* Unsubscribes from a channel. Called ONLY in the Root (master) process.
|
|
233
|
+
*/
|
|
234
|
+
.unsubscribe = (rb_respond_to(obj, rb_intern("unsubscribe")))
|
|
235
|
+
? iodine_pubsub_eng___unsubscribe
|
|
236
|
+
: NULL,
|
|
237
|
+
/* Unsubscribes from a pattern. Called ONLY in the Root (master) process.
|
|
238
|
+
*/
|
|
239
|
+
.punsubscribe = (rb_respond_to(obj, rb_intern("punsubscribe")))
|
|
240
|
+
? iodine_pubsub_eng___punsubscribe
|
|
241
|
+
: NULL,
|
|
242
|
+
/* Publishes a message through the engine. Called by any worker/thread. */
|
|
243
|
+
.publish = (rb_respond_to(obj, rb_intern("publish")))
|
|
244
|
+
? iodine_pubsub_eng___publish
|
|
245
|
+
: NULL,
|
|
246
|
+
};
|
|
247
|
+
return r;
|
|
248
|
+
}
|
|
249
|
+
/* *****************************************************************************
|
|
250
|
+
Ruby PubSub Engine Object - Ruby TypedData Wrapper
|
|
251
|
+
***************************************************************************** */
|
|
252
|
+
|
|
253
|
+
static size_t iodine_pubsub_eng_data_size(const void *ptr_) {
|
|
254
|
+
iodine_pubsub_eng_s *m = (iodine_pubsub_eng_s *)ptr_;
|
|
255
|
+
return sizeof(*m);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
static void iodine_pubsub_eng_free(void *ptr_) {
|
|
259
|
+
iodine_pubsub_eng_s *e = (iodine_pubsub_eng_s *)ptr_;
|
|
260
|
+
if (fio_pubsub_engine_default() == e->ptr)
|
|
261
|
+
fio_pubsub_engine_default_set(NULL);
|
|
262
|
+
ruby_xfree(e);
|
|
263
|
+
// FIO_LEAK_COUNTER_ON_FREE(iodine_pubsub_eng);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
static const rb_data_type_t IODINE_PUBSUB_ENG_DATA_TYPE = {
|
|
267
|
+
.wrap_struct_name = "IodinePSEngine",
|
|
268
|
+
.function =
|
|
269
|
+
{
|
|
270
|
+
.dfree = iodine_pubsub_eng_free,
|
|
271
|
+
.dsize = iodine_pubsub_eng_data_size,
|
|
272
|
+
},
|
|
273
|
+
.data = NULL,
|
|
274
|
+
// .flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
static VALUE iodine_pubsub_eng_alloc(VALUE klass) {
|
|
278
|
+
/* use Ruby allocator for long life object */
|
|
279
|
+
iodine_pubsub_eng_s *m = (iodine_pubsub_eng_s *)ruby_xmalloc(sizeof(*m));
|
|
280
|
+
if (!m)
|
|
281
|
+
goto no_memory;
|
|
282
|
+
*m = (iodine_pubsub_eng_s){0};
|
|
283
|
+
m->ptr = &m->engine;
|
|
284
|
+
// FIO_LEAK_COUNTER_ON_ALLOC(iodine_pubsub_eng);
|
|
285
|
+
m->handler = TypedData_Wrap_Struct(klass, &IODINE_PUBSUB_ENG_DATA_TYPE, m);
|
|
286
|
+
m->engine = iodine_pubsub___engine_validate(m->handler);
|
|
287
|
+
return m->handler;
|
|
288
|
+
|
|
289
|
+
no_memory:
|
|
290
|
+
FIO_LOG_FATAL("Memory allocation failed");
|
|
291
|
+
fio_io_stop();
|
|
292
|
+
return Qnil;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
static iodine_pubsub_eng_s *iodine_pubsub_eng_get(VALUE self) {
|
|
296
|
+
iodine_pubsub_eng_s *m;
|
|
297
|
+
TypedData_Get_Struct(self,
|
|
298
|
+
iodine_pubsub_eng_s,
|
|
299
|
+
&IODINE_PUBSUB_ENG_DATA_TYPE,
|
|
300
|
+
m);
|
|
301
|
+
return m;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/* *****************************************************************************
|
|
305
|
+
Ruby Methods - Engine API
|
|
306
|
+
***************************************************************************** */
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Initializes a new PubSub engine and attaches it to the pubsub system.
|
|
310
|
+
*
|
|
311
|
+
* @param self The Engine instance
|
|
312
|
+
* @return self
|
|
313
|
+
*
|
|
314
|
+
* Ruby: engine = Iodine::PubSub::Engine.new
|
|
315
|
+
*/
|
|
316
|
+
static VALUE iodine_pubsub_eng_initialize(VALUE self) {
|
|
317
|
+
iodine_pubsub_eng_s *m = iodine_pubsub_eng_get(self);
|
|
318
|
+
fio_pubsub_engine_attach(m->ptr);
|
|
319
|
+
return self;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/** Internal constant name for storing the default engine */
|
|
323
|
+
#define IODINE_PUBSUB_DEFAULT_NM "PUBSUB____DEFAULT"
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Sets the default PubSub engine for all publish operations.
|
|
327
|
+
*
|
|
328
|
+
* @param klass The Iodine::PubSub module
|
|
329
|
+
* @param eng The engine to set as default (or nil for CLUSTER)
|
|
330
|
+
* @return The new default engine
|
|
331
|
+
*
|
|
332
|
+
* Ruby: Iodine::PubSub.default = my_engine
|
|
333
|
+
*/
|
|
334
|
+
static VALUE iodine_pubsub_eng_default_set(VALUE klass, VALUE eng) {
|
|
335
|
+
fio_pubsub_engine_s *e = (fio_pubsub_engine_s *)fio_pubsub_engine_cluster();
|
|
336
|
+
ID name = rb_intern(IODINE_PUBSUB_DEFAULT_NM);
|
|
337
|
+
if (!IODINE_STORE_IS_SKIP(eng)) {
|
|
338
|
+
e = iodine_pubsub_eng_get(eng)->ptr;
|
|
339
|
+
}
|
|
340
|
+
fio_pubsub_engine_default_set(e);
|
|
341
|
+
VALUE old = rb_const_get(iodine_rb_IODINE_BASE, name);
|
|
342
|
+
if ((uintptr_t)old > 15)
|
|
343
|
+
STORE.release(old);
|
|
344
|
+
STORE.hold(eng);
|
|
345
|
+
rb_const_remove(iodine_rb_IODINE_BASE, name);
|
|
346
|
+
rb_const_set(iodine_rb_IODINE_BASE, name, eng);
|
|
347
|
+
return eng;
|
|
348
|
+
(void)klass;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Gets the current default PubSub engine.
|
|
353
|
+
*
|
|
354
|
+
* @param klass The Iodine::PubSub module
|
|
355
|
+
* @return The current default engine
|
|
356
|
+
*
|
|
357
|
+
* Ruby: Iodine::PubSub.default
|
|
358
|
+
*/
|
|
359
|
+
static VALUE iodine_pubsub_eng_default_get(VALUE klass) {
|
|
360
|
+
return rb_const_get(iodine_rb_IODINE_BASE,
|
|
361
|
+
rb_intern(IODINE_PUBSUB_DEFAULT_NM));
|
|
362
|
+
(void)klass;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/* *****************************************************************************
|
|
366
|
+
Initialize - Ruby Class Registration
|
|
367
|
+
***************************************************************************** */
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Initializes the Iodine::PubSub::Engine Ruby class.
|
|
371
|
+
*
|
|
372
|
+
* Defines:
|
|
373
|
+
* - Iodine::PubSub.default / default= module methods
|
|
374
|
+
* - Iodine::PubSub::Engine class with initialize method
|
|
375
|
+
* - Built-in engine constants: LOCAL and CLUSTER
|
|
376
|
+
*
|
|
377
|
+
* Only two built-in engines are provided:
|
|
378
|
+
* - LOCAL (fio_pubsub_engine_ipc): Local machine only (master + all workers)
|
|
379
|
+
* - CLUSTER (fio_pubsub_engine_cluster): Multi-machine cluster (default)
|
|
380
|
+
*/
|
|
381
|
+
static void Init_Iodine_PubSub_Engine(void) {
|
|
382
|
+
/** Initialize Iodine::PubSub::Engine */
|
|
383
|
+
rb_define_module_function(iodine_rb_IODINE_PUBSUB,
|
|
384
|
+
"default=",
|
|
385
|
+
iodine_pubsub_eng_default_set,
|
|
386
|
+
1);
|
|
387
|
+
rb_define_module_function(iodine_rb_IODINE_PUBSUB,
|
|
388
|
+
"default",
|
|
389
|
+
iodine_pubsub_eng_default_get,
|
|
390
|
+
0);
|
|
391
|
+
|
|
392
|
+
iodine_rb_IODINE_PUBSUB_ENG =
|
|
393
|
+
rb_define_class_under(iodine_rb_IODINE_PUBSUB, "Engine", rb_cObject);
|
|
394
|
+
STORE.hold(iodine_rb_IODINE_PUBSUB_ENG);
|
|
395
|
+
rb_define_alloc_func(iodine_rb_IODINE_PUBSUB_ENG, iodine_pubsub_eng_alloc);
|
|
396
|
+
|
|
397
|
+
/* Define LOCAL engine (IPC - local machine only) */
|
|
398
|
+
{
|
|
399
|
+
VALUE tmp = rb_obj_alloc(iodine_rb_IODINE_PUBSUB_ENG);
|
|
400
|
+
iodine_pubsub_eng_get(tmp)->ptr =
|
|
401
|
+
(fio_pubsub_engine_s *)fio_pubsub_engine_ipc();
|
|
402
|
+
rb_define_const(iodine_rb_IODINE_PUBSUB, "LOCAL", tmp);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/* Define CLUSTER engine (multi-machine cluster) */
|
|
406
|
+
{
|
|
407
|
+
VALUE tmp = rb_obj_alloc(iodine_rb_IODINE_PUBSUB_ENG);
|
|
408
|
+
iodine_pubsub_eng_get(tmp)->ptr =
|
|
409
|
+
(fio_pubsub_engine_s *)fio_pubsub_engine_cluster();
|
|
410
|
+
rb_define_const(iodine_rb_IODINE_PUBSUB, "CLUSTER", tmp);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
rb_define_const(iodine_rb_IODINE_BASE,
|
|
414
|
+
IODINE_PUBSUB_DEFAULT_NM,
|
|
415
|
+
rb_const_get(iodine_rb_IODINE_PUBSUB, rb_intern("CLUSTER")));
|
|
416
|
+
|
|
417
|
+
rb_define_method(iodine_rb_IODINE_PUBSUB_ENG,
|
|
418
|
+
"initialize",
|
|
419
|
+
iodine_pubsub_eng_initialize,
|
|
420
|
+
0);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/* *****************************************************************************
|
|
424
|
+
Iodine PubSub Subscription - Independent Non-IO Subscription Handle
|
|
425
|
+
|
|
426
|
+
This module provides the Iodine::PubSub::Subscription Ruby class which wraps
|
|
427
|
+
a non-IO-bound facil.io subscription. Unlike Iodine.subscribe (which allows
|
|
428
|
+
only one callback per channel per global context), each Subscription object
|
|
429
|
+
is its own independent context — multiple Subscription objects can subscribe
|
|
430
|
+
to the same channel simultaneously.
|
|
431
|
+
|
|
432
|
+
Features:
|
|
433
|
+
- Multiple independent subscriptions to the same channel
|
|
434
|
+
- Early cancellation via #cancel (idempotent)
|
|
435
|
+
- Live handler replacement via #handler=
|
|
436
|
+
- Global lifetime even without a caller-held reference
|
|
437
|
+
- Auto-cancel during FIO_CALL_AT_EXIT
|
|
438
|
+
|
|
439
|
+
Ruby API:
|
|
440
|
+
sub = Iodine::PubSub::Subscription.new("channel") { |msg| ... }
|
|
441
|
+
sub = Iodine::PubSub::Subscription.new(channel: "ch", filter: 0) { |msg| ... }
|
|
442
|
+
sub.handler # => current Proc (retained after cancellation)
|
|
443
|
+
sub.handler = proc # => proc (replaces callback for future messages)
|
|
444
|
+
sub.active? # => true/false
|
|
445
|
+
sub.cancel # => self (idempotent)
|
|
446
|
+
***************************************************************************** */
|
|
447
|
+
|
|
448
|
+
/* *****************************************************************************
|
|
449
|
+
Ruby PubSub Subscription Type
|
|
450
|
+
***************************************************************************** */
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* One native object is shared by the Ruby wrapper and facil.io.
|
|
454
|
+
*
|
|
455
|
+
* The wrapper is held in STORE for as long as the native subscription exists,
|
|
456
|
+
* so a subscription remains active even when the caller doesn't save it.
|
|
457
|
+
*/
|
|
458
|
+
typedef struct iodine_pubsub_sub_s {
|
|
459
|
+
uintptr_t handle;
|
|
460
|
+
VALUE handler;
|
|
461
|
+
VALUE wrapper;
|
|
462
|
+
uint8_t initialized;
|
|
463
|
+
} iodine_pubsub_sub_s;
|
|
464
|
+
|
|
465
|
+
/** Requests cancellation. fio_pubsub_unsubscribe zeros the handle and is
|
|
466
|
+
* idempotent when guarded by the handle check. */
|
|
467
|
+
static void iodine_pubsub_sub_force_cleanup(void *sub_) {
|
|
468
|
+
iodine_pubsub_sub_s *s = (iodine_pubsub_sub_s *)sub_;
|
|
469
|
+
if (s->handle)
|
|
470
|
+
fio_pubsub_unsubscribe(.subscription_handle_ptr = &s->handle);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/* *****************************************************************************
|
|
474
|
+
Ruby PubSub Subscription - Message Callbacks
|
|
475
|
+
***************************************************************************** */
|
|
476
|
+
|
|
477
|
+
/** Called inside the GVL to dispatch a pub/sub message. */
|
|
478
|
+
static void *iodine_pubsub_sub_on_message_in_gvl(void *m_) {
|
|
479
|
+
fio_pubsub_msg_s *m = (fio_pubsub_msg_s *)m_;
|
|
480
|
+
iodine_pubsub_sub_s *s = (iodine_pubsub_sub_s *)m->udata;
|
|
481
|
+
if (!s->handle || IODINE_STORE_IS_SKIP(s->handler))
|
|
482
|
+
return m_;
|
|
483
|
+
VALUE msg = iodine_pubsub_msg_new(m);
|
|
484
|
+
iodine_ruby_call_inside(s->handler, IODINE_CALL_ID, 1, &msg);
|
|
485
|
+
STORE.release(msg);
|
|
486
|
+
return m_;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/** Called by facil.io when a message arrives on the subscribed channel. */
|
|
490
|
+
static void iodine_pubsub_sub_on_message(fio_pubsub_msg_s *m) {
|
|
491
|
+
iodine_c_call_with(iodine_pubsub_sub_on_message_in_gvl, m);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Removes exit cleanup and releases the STORE-held Ruby wrapper after the
|
|
496
|
+
* native subscription is completely gone. Safe outside the GVL.
|
|
497
|
+
*/
|
|
498
|
+
static void iodine_pubsub_sub_on_unsubscribe(void *udata) {
|
|
499
|
+
iodine_pubsub_sub_s *s = (iodine_pubsub_sub_s *)udata;
|
|
500
|
+
VALUE wrapper = s->wrapper;
|
|
501
|
+
s->handle = 0;
|
|
502
|
+
s->wrapper = Qnil;
|
|
503
|
+
fio_state_callback_remove(FIO_CALL_AT_EXIT,
|
|
504
|
+
iodine_pubsub_sub_force_cleanup,
|
|
505
|
+
s);
|
|
506
|
+
STORE.release(wrapper);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/* *****************************************************************************
|
|
510
|
+
Ruby PubSub Subscription Object - Ruby TypedData Wrapper
|
|
511
|
+
***************************************************************************** */
|
|
512
|
+
|
|
513
|
+
static size_t iodine_pubsub_sub_data_size(const void *ptr_) {
|
|
514
|
+
(void)ptr_;
|
|
515
|
+
return sizeof(iodine_pubsub_sub_s);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* GC mark callback — keeps the handler proc alive while the subscription is.
|
|
520
|
+
*/
|
|
521
|
+
static void iodine_pubsub_sub_mark(void *ptr_) {
|
|
522
|
+
iodine_pubsub_sub_s *s = (iodine_pubsub_sub_s *)ptr_;
|
|
523
|
+
if (!IODINE_STORE_IS_SKIP(s->handler))
|
|
524
|
+
rb_gc_mark(s->handler);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/** The wrapper can only be collected after native unsubscribe releases it. */
|
|
528
|
+
static void iodine_pubsub_sub_free(void *ptr_) {
|
|
529
|
+
ruby_xfree(ptr_);
|
|
530
|
+
FIO_LEAK_COUNTER_ON_FREE(iodine_pubsub_sub);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
static const rb_data_type_t IODINE_PUBSUB_SUB_DATA_TYPE = {
|
|
534
|
+
.wrap_struct_name = "IodinePSSub",
|
|
535
|
+
.function =
|
|
536
|
+
{
|
|
537
|
+
.dmark = iodine_pubsub_sub_mark,
|
|
538
|
+
.dfree = iodine_pubsub_sub_free,
|
|
539
|
+
.dsize = iodine_pubsub_sub_data_size,
|
|
540
|
+
},
|
|
541
|
+
.data = NULL,
|
|
542
|
+
// .flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
static VALUE iodine_pubsub_sub_alloc(VALUE klass) {
|
|
546
|
+
iodine_pubsub_sub_s *s = (iodine_pubsub_sub_s *)ruby_xmalloc(sizeof(*s));
|
|
547
|
+
if (!s)
|
|
548
|
+
goto no_memory;
|
|
549
|
+
*s = (iodine_pubsub_sub_s){.handler = Qnil, .wrapper = Qnil};
|
|
550
|
+
FIO_LEAK_COUNTER_ON_ALLOC(iodine_pubsub_sub);
|
|
551
|
+
return TypedData_Wrap_Struct(klass, &IODINE_PUBSUB_SUB_DATA_TYPE, s);
|
|
552
|
+
no_memory:
|
|
553
|
+
FIO_LOG_FATAL("Memory allocation failed");
|
|
554
|
+
fio_io_stop();
|
|
555
|
+
return Qnil;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
static iodine_pubsub_sub_s *iodine_pubsub_sub_get(VALUE self) {
|
|
559
|
+
iodine_pubsub_sub_s *s;
|
|
560
|
+
TypedData_Get_Struct(self,
|
|
561
|
+
iodine_pubsub_sub_s,
|
|
562
|
+
&IODINE_PUBSUB_SUB_DATA_TYPE,
|
|
563
|
+
s);
|
|
564
|
+
return s;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/* *****************************************************************************
|
|
568
|
+
Ruby Methods - Subscription API
|
|
569
|
+
***************************************************************************** */
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Initializes a new independent pub/sub subscription.
|
|
573
|
+
*
|
|
574
|
+
* Creates a non-IO-bound facil.io subscription using subscription_handle_ptr,
|
|
575
|
+
* allowing multiple independent subscriptions to the same channel.
|
|
576
|
+
*
|
|
577
|
+
* @param channel [String] the channel name to subscribe to
|
|
578
|
+
* @param filter [Integer] optional numerical filter (default: 0)
|
|
579
|
+
* @param since [Integer] optional replay-since timestamp in milliseconds
|
|
580
|
+
* @param block [Proc] required message handler callback
|
|
581
|
+
* @return self
|
|
582
|
+
*
|
|
583
|
+
* Ruby: sub = Iodine::PubSub::Subscription.new("channel") { |msg| ... }
|
|
584
|
+
* sub = Iodine::PubSub::Subscription.new(channel: "ch", filter: 0) {
|
|
585
|
+
* |msg| ... }
|
|
586
|
+
*/
|
|
587
|
+
static VALUE iodine_pubsub_sub_initialize(int argc, VALUE *argv, VALUE self) {
|
|
588
|
+
iodine_pubsub_sub_s *s = iodine_pubsub_sub_get(self);
|
|
589
|
+
fio_buf_info_s channel = FIO_BUF_INFO2(NULL, 0);
|
|
590
|
+
int64_t filter = 0;
|
|
591
|
+
uint64_t since = 0;
|
|
592
|
+
VALUE proc = Qnil;
|
|
593
|
+
|
|
594
|
+
iodine_rb2c_arg(argc,
|
|
595
|
+
argv,
|
|
596
|
+
IODINE_ARG_BUF(channel, 0, "channel", 1),
|
|
597
|
+
IODINE_ARG_NUM(filter, 0, "filter", 0),
|
|
598
|
+
IODINE_ARG_U64(since, 0, "since", 0),
|
|
599
|
+
IODINE_ARG_PROC(proc, 0, "callback", 1));
|
|
600
|
+
|
|
601
|
+
if ((size_t)filter & (~(size_t)0xFFFF))
|
|
602
|
+
rb_raise(rb_eRangeError,
|
|
603
|
+
"filter out of range (%lld > 0xFFFF)",
|
|
604
|
+
(long long)filter);
|
|
605
|
+
|
|
606
|
+
if (s->initialized)
|
|
607
|
+
rb_raise(rb_eRuntimeError, "Subscription is already initialized");
|
|
608
|
+
s->initialized = 1;
|
|
609
|
+
s->handler = proc;
|
|
610
|
+
s->wrapper = self;
|
|
611
|
+
STORE.hold(self);
|
|
612
|
+
fio_state_callback_add(FIO_CALL_AT_EXIT,
|
|
613
|
+
iodine_pubsub_sub_force_cleanup,
|
|
614
|
+
s);
|
|
615
|
+
|
|
616
|
+
/* The same native object is both Ruby's TypedData and facil.io's udata. */
|
|
617
|
+
fio_pubsub_subscribe(.subscription_handle_ptr = &s->handle,
|
|
618
|
+
.filter = (int16_t)filter,
|
|
619
|
+
.channel = channel,
|
|
620
|
+
.udata = (void *)s,
|
|
621
|
+
.queue = fio_io_async_queue(&IODINE_THREAD_POOL),
|
|
622
|
+
.on_message = iodine_pubsub_sub_on_message,
|
|
623
|
+
.on_unsubscribe = iodine_pubsub_sub_on_unsubscribe,
|
|
624
|
+
.replay_since = since);
|
|
625
|
+
return self;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Returns the current message handler proc.
|
|
630
|
+
*
|
|
631
|
+
* @return [Proc, nil] the current handler, retained after cancellation
|
|
632
|
+
*
|
|
633
|
+
* Ruby: sub.handler # => Proc
|
|
634
|
+
*/
|
|
635
|
+
static VALUE iodine_pubsub_sub_handler_get(VALUE self) {
|
|
636
|
+
iodine_pubsub_sub_s *s = iodine_pubsub_sub_get(self);
|
|
637
|
+
return s->handler;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Replaces the message handler proc.
|
|
642
|
+
*
|
|
643
|
+
* Updates the single handler field used by both #handler and message delivery.
|
|
644
|
+
* Both this method and message dispatch run under the GVL.
|
|
645
|
+
*
|
|
646
|
+
* @param new_handler [Proc] the new handler (must respond to #call)
|
|
647
|
+
* @return [Proc] the new handler
|
|
648
|
+
*
|
|
649
|
+
* Ruby: sub.handler = proc { |msg| ... }
|
|
650
|
+
*/
|
|
651
|
+
static VALUE iodine_pubsub_sub_handler_set(VALUE self, VALUE new_handler) {
|
|
652
|
+
iodine_pubsub_sub_s *s = iodine_pubsub_sub_get(self);
|
|
653
|
+
if (!IODINE_STORE_IS_SKIP(new_handler) &&
|
|
654
|
+
!rb_respond_to(new_handler, rb_intern2("call", 4)))
|
|
655
|
+
rb_raise(rb_eArgError, "handler must respond to `call`");
|
|
656
|
+
s->handler = new_handler;
|
|
657
|
+
return new_handler;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Returns true if the subscription is still active.
|
|
662
|
+
*
|
|
663
|
+
* @return [Boolean] true if active, false if cancelled or not yet subscribed
|
|
664
|
+
*
|
|
665
|
+
* Ruby: sub.active? # => true/false
|
|
666
|
+
*/
|
|
667
|
+
static VALUE iodine_pubsub_sub_active_p(VALUE self) {
|
|
668
|
+
return iodine_pubsub_sub_get(self)->handle ? Qtrue : Qfalse;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
/**
|
|
672
|
+
* Cancels the subscription early. Idempotent — safe to call multiple times.
|
|
673
|
+
*
|
|
674
|
+
* After cancellation, no further messages will be delivered. The handler
|
|
675
|
+
* proc is NOT released here (it remains accessible via #handler until GC).
|
|
676
|
+
*
|
|
677
|
+
* @return [self]
|
|
678
|
+
*
|
|
679
|
+
* Ruby: sub.cancel # => self
|
|
680
|
+
*/
|
|
681
|
+
static VALUE iodine_pubsub_sub_cancel(VALUE self) {
|
|
682
|
+
iodine_pubsub_sub_force_cleanup(iodine_pubsub_sub_get(self));
|
|
683
|
+
return self;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/* *****************************************************************************
|
|
687
|
+
Initialize - Ruby Class Registration
|
|
688
|
+
***************************************************************************** */
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* Initializes the Iodine::PubSub::Subscription Ruby class.
|
|
692
|
+
*
|
|
693
|
+
* Defines:
|
|
694
|
+
* - Iodine::PubSub::Subscription class with initialize, handler, handler=,
|
|
695
|
+
* active?, and cancel methods.
|
|
696
|
+
*
|
|
697
|
+
* Each Subscription instance is an independent non-IO-bound subscription,
|
|
698
|
+
* allowing multiple simultaneous subscriptions to the same channel.
|
|
699
|
+
*/
|
|
700
|
+
static void Init_Iodine_PubSub_Subscription(void) {
|
|
701
|
+
iodine_rb_IODINE_PUBSUB_SUB = rb_define_class_under(iodine_rb_IODINE_PUBSUB,
|
|
702
|
+
"Subscription",
|
|
703
|
+
rb_cObject);
|
|
704
|
+
STORE.hold(iodine_rb_IODINE_PUBSUB_SUB);
|
|
705
|
+
rb_define_alloc_func(iodine_rb_IODINE_PUBSUB_SUB, iodine_pubsub_sub_alloc);
|
|
706
|
+
|
|
707
|
+
rb_define_method(iodine_rb_IODINE_PUBSUB_SUB,
|
|
708
|
+
"initialize",
|
|
709
|
+
iodine_pubsub_sub_initialize,
|
|
710
|
+
-1);
|
|
711
|
+
rb_define_method(iodine_rb_IODINE_PUBSUB_SUB,
|
|
712
|
+
"handler",
|
|
713
|
+
iodine_pubsub_sub_handler_get,
|
|
714
|
+
0);
|
|
715
|
+
rb_define_method(iodine_rb_IODINE_PUBSUB_SUB,
|
|
716
|
+
"handler=",
|
|
717
|
+
iodine_pubsub_sub_handler_set,
|
|
718
|
+
1);
|
|
719
|
+
rb_define_method(iodine_rb_IODINE_PUBSUB_SUB,
|
|
720
|
+
"active?",
|
|
721
|
+
iodine_pubsub_sub_active_p,
|
|
722
|
+
0);
|
|
723
|
+
rb_define_method(iodine_rb_IODINE_PUBSUB_SUB,
|
|
724
|
+
"cancel",
|
|
725
|
+
iodine_pubsub_sub_cancel,
|
|
726
|
+
0);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
#endif /* H___IODINE_PUBSUB_ENG___H */
|