iodine 0.3.6 → 0.4.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.

Potentially problematic release.


This version of iodine might be problematic. Click here for more details.

Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -0
  3. data/LIMITS.md +25 -0
  4. data/README.md +39 -80
  5. data/SPEC-Websocket-Draft.md +129 -4
  6. data/bin/echo +2 -2
  7. data/bin/http-hello +1 -0
  8. data/bin/updated api +113 -0
  9. data/bin/ws-echo +0 -1
  10. data/examples/broadcast.ru +56 -0
  11. data/examples/echo.ru +57 -0
  12. data/examples/hello.ru +30 -0
  13. data/examples/redis.ru +69 -0
  14. data/examples/shootout.ru +53 -0
  15. data/exe/iodine +2 -80
  16. data/ext/iodine/defer.c +11 -5
  17. data/ext/iodine/empty.h +26 -0
  18. data/ext/iodine/evio.h +1 -1
  19. data/ext/iodine/facil.c +103 -61
  20. data/ext/iodine/facil.h +20 -12
  21. data/ext/iodine/fio_dict.c +446 -0
  22. data/ext/iodine/fio_dict.h +90 -0
  23. data/ext/iodine/fio_hash_table.h +370 -0
  24. data/ext/iodine/fio_list.h +30 -3
  25. data/ext/iodine/http.c +169 -37
  26. data/ext/iodine/http.h +33 -10
  27. data/ext/iodine/http1.c +78 -42
  28. data/ext/iodine/http_request.c +6 -0
  29. data/ext/iodine/http_request.h +3 -0
  30. data/ext/iodine/http_response.c +43 -11
  31. data/ext/iodine/iodine.c +380 -0
  32. data/ext/iodine/iodine.h +62 -0
  33. data/ext/iodine/iodine_helpers.c +235 -0
  34. data/ext/iodine/iodine_helpers.h +13 -0
  35. data/ext/iodine/iodine_http.c +409 -241
  36. data/ext/iodine/iodine_http.h +7 -14
  37. data/ext/iodine/iodine_protocol.c +626 -0
  38. data/ext/iodine/iodine_protocol.h +13 -0
  39. data/ext/iodine/iodine_pubsub.c +646 -0
  40. data/ext/iodine/iodine_pubsub.h +27 -0
  41. data/ext/iodine/iodine_websockets.c +796 -0
  42. data/ext/iodine/iodine_websockets.h +19 -0
  43. data/ext/iodine/pubsub.c +544 -0
  44. data/ext/iodine/pubsub.h +215 -0
  45. data/ext/iodine/random.c +4 -4
  46. data/ext/iodine/rb-call.c +1 -5
  47. data/ext/iodine/rb-defer.c +3 -20
  48. data/ext/iodine/rb-rack-io.c +22 -22
  49. data/ext/iodine/rb-rack-io.h +3 -4
  50. data/ext/iodine/rb-registry.c +111 -118
  51. data/ext/iodine/redis_connection.c +277 -0
  52. data/ext/iodine/redis_connection.h +77 -0
  53. data/ext/iodine/redis_engine.c +398 -0
  54. data/ext/iodine/redis_engine.h +68 -0
  55. data/ext/iodine/resp.c +842 -0
  56. data/ext/iodine/resp.h +253 -0
  57. data/ext/iodine/sock.c +26 -12
  58. data/ext/iodine/sock.h +14 -3
  59. data/ext/iodine/spnlock.inc +19 -2
  60. data/ext/iodine/websockets.c +299 -11
  61. data/ext/iodine/websockets.h +159 -6
  62. data/lib/iodine.rb +104 -1
  63. data/lib/iodine/cli.rb +106 -0
  64. data/lib/iodine/monkeypatch.rb +40 -0
  65. data/lib/iodine/pubsub.rb +70 -0
  66. data/lib/iodine/version.rb +1 -1
  67. data/lib/iodine/websocket.rb +12 -0
  68. data/lib/rack/handler/iodine.rb +33 -7
  69. metadata +35 -7
  70. data/ext/iodine/iodine_core.c +0 -760
  71. data/ext/iodine/iodine_core.h +0 -79
  72. data/ext/iodine/iodine_websocket.c +0 -551
  73. data/ext/iodine/iodine_websocket.h +0 -22
  74. data/lib/iodine/http.rb +0 -4
@@ -0,0 +1,19 @@
1
+ #ifndef H_IODINE_WEBSOCKETS_H
2
+ #define H_IODINE_WEBSOCKETS_H
3
+ /*
4
+ Copyright: Boaz segev, 2016-2017
5
+ License: MIT
6
+
7
+ Feel free to copy, use and enjoy according to the license provided.
8
+ */
9
+ #include "iodine.h"
10
+
11
+ #include "http.h"
12
+
13
+ void Iodine_init_websocket(void);
14
+
15
+ void iodine_websocket_upgrade(http_request_s *request,
16
+ http_response_s *response, VALUE handler,
17
+ size_t max_msg, uint8_t ping);
18
+
19
+ #endif
@@ -0,0 +1,544 @@
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 "spnlock.inc"
8
+
9
+ #include "facil.h"
10
+ #include "fio_dict.h"
11
+ #include "fio_hash_table.h"
12
+ #include "pubsub.h"
13
+
14
+ #include <errno.h>
15
+ #include <signal.h>
16
+ #include <stdint.h>
17
+ #include <stdio.h>
18
+ #include <stdlib.h>
19
+ #include <string.h>
20
+
21
+ /* *****************************************************************************
22
+ Data Structures / State
23
+ ***************************************************************************** */
24
+
25
+ typedef struct {
26
+ volatile uint64_t ref;
27
+ struct pubsub_publish_args pub;
28
+ } msg_s;
29
+
30
+ typedef struct {
31
+ fio_ht_s clients;
32
+ const pubsub_engine_s *engine;
33
+ struct {
34
+ char *name;
35
+ uint32_t len;
36
+ };
37
+ union {
38
+ fio_dict_s dict;
39
+ fio_list_s list;
40
+ } channels;
41
+ unsigned use_pattern : 1;
42
+ } channel_s;
43
+
44
+ typedef struct pubsub_sub_s {
45
+ fio_ht_node_s clients;
46
+ void (*on_message)(pubsub_message_s *msg);
47
+ void (*on_unsubscribe)(void *udata1, void *udata2);
48
+ void *udata1;
49
+ void *udata2;
50
+ channel_s *parent;
51
+ volatile uint64_t active;
52
+ uint32_t ref;
53
+ } client_s;
54
+
55
+ typedef struct {
56
+ msg_s *origin;
57
+ pubsub_message_s msg;
58
+ } msg_container_s;
59
+
60
+ static fio_dict_s pubsub_channels = FIO_DICT_INIT_STATIC;
61
+ static fio_list_s pubsub_patterns = FIO_LIST_INIT_STATIC(pubsub_patterns);
62
+
63
+ spn_lock_i pubsub_GIL = SPN_LOCK_INIT;
64
+
65
+ /* *****************************************************************************
66
+ Helpers
67
+ ***************************************************************************** */
68
+
69
+ static void pubsub_free_client(void *client_, void *ignr) {
70
+ client_s *client = client_;
71
+ if (spn_sub(&client->active, 1))
72
+ return;
73
+ if (client->on_unsubscribe)
74
+ client->on_unsubscribe(client->udata1, client->udata2);
75
+ free(client);
76
+ (void)ignr;
77
+ }
78
+
79
+ static void pubsub_free_msg(void *msg_, void *ignr) {
80
+ msg_s *msg = msg_;
81
+ if (!spn_sub(&msg->ref, 1)) {
82
+ free(msg);
83
+ }
84
+ (void)ignr;
85
+ }
86
+
87
+ static void pubsub_deliver_msg(void *client_, void *msg_) {
88
+ client_s *cl = client_;
89
+ msg_s *msg = msg_;
90
+ msg_container_s clmsg = {
91
+ .origin = msg,
92
+ .msg =
93
+ {
94
+ .engine = msg->pub.engine,
95
+ .channel.name = msg->pub.channel.name,
96
+ .channel.len = msg->pub.channel.len,
97
+ .msg.data = msg->pub.msg.data,
98
+ .msg.len = msg->pub.msg.len,
99
+ .use_pattern = msg->pub.use_pattern,
100
+ .udata1 = cl->udata1,
101
+ .udata2 = cl->udata2,
102
+ .subscription = cl,
103
+ },
104
+ };
105
+ cl->on_message(&clmsg.msg);
106
+ pubsub_free_msg(msg, NULL);
107
+ pubsub_free_client(cl, NULL);
108
+ }
109
+
110
+ /* *****************************************************************************
111
+ Default Engine / Cluster Support
112
+ ***************************************************************************** */
113
+
114
+ /** Used internally to identify oub/sub cluster messages. */
115
+ #define FACIL_PUBSUB_CLUSTER_MESSAGE_ID ((int32_t)-1)
116
+
117
+ /*
118
+ a `ppublish` / `publish` found this channel as a match...
119
+ Pubblish to clients and test against subscribed patterns.
120
+ */
121
+ static void pubsub_publish_matched_channel(fio_dict_s *ch_, void *msg_) {
122
+ msg_s *msg = msg_;
123
+ client_s *cl;
124
+ if (ch_) {
125
+ channel_s *channel = fio_node2obj(channel_s, channels, ch_);
126
+ fio_ht_for_each(client_s, clients, cl, channel->clients) {
127
+ spn_add(&msg->ref, 1);
128
+ spn_add(&cl->active, 1);
129
+ defer(pubsub_deliver_msg, cl, msg);
130
+ }
131
+ }
132
+ channel_s *pattern;
133
+ fio_list_for_each(channel_s, channels.list, pattern, pubsub_patterns) {
134
+ if (msg->pub.engine == pattern->engine &&
135
+ fio_glob_match((uint8_t *)msg->pub.channel.name, msg->pub.channel.len,
136
+ (uint8_t *)pattern->name, pattern->len)) {
137
+ fio_ht_for_each(client_s, clients, cl, pattern->clients) {
138
+ spn_add(&msg->ref, 1);
139
+ spn_add(&cl->active, 1);
140
+ defer(pubsub_deliver_msg, cl, msg);
141
+ }
142
+ }
143
+ }
144
+ }
145
+
146
+ static void pubsub_perform_publish(void *msg_, void *ignr) {
147
+ msg_s *msg = msg_;
148
+ fio_dict_s *channel;
149
+ spn_lock(&pubsub_GIL);
150
+
151
+ if (msg->pub.use_pattern) {
152
+ fio_dict_each_match_glob(fio_dict_prefix(&pubsub_channels,
153
+ (void *)&msg->pub.engine,
154
+ sizeof(void *)),
155
+ msg->pub.channel.name, msg->pub.channel.len,
156
+ pubsub_publish_matched_channel, msg);
157
+ } else {
158
+ channel = (void *)fio_dict_get(fio_dict_prefix(&pubsub_channels,
159
+ (void *)&msg->pub.engine,
160
+ sizeof(void *)),
161
+ msg->pub.channel.name, msg->pub.channel.len);
162
+ pubsub_publish_matched_channel(channel, msg);
163
+ }
164
+
165
+ spn_unlock(&pubsub_GIL);
166
+ pubsub_free_msg(msg, NULL);
167
+ (void)ignr;
168
+ }
169
+
170
+ static int pubsub_cluster_publish(struct pubsub_publish_args args) {
171
+ msg_s *msg = malloc(sizeof(*msg) + args.channel.len + args.msg.len + 2);
172
+ if (!msg)
173
+ return -1;
174
+ *msg = (msg_s){.ref = 1, .pub = args};
175
+ struct pubsub_publish_args *pub = &msg->pub;
176
+ pub->msg.data = (char *)(pub + 1);
177
+ memcpy(pub->msg.data, args.msg.data, args.msg.len);
178
+ pub->msg.data[args.msg.len] = 0;
179
+ if (pub->channel.name) {
180
+ pub->channel.name = pub->msg.data + args.msg.len + 1;
181
+ memcpy(pub->channel.name, args.channel.name, args.channel.len);
182
+ pub->channel.name[args.channel.len] = 0;
183
+ }
184
+ pub->use_pattern = args.use_pattern;
185
+ if (args.push2cluster)
186
+ facil_cluster_send(FACIL_PUBSUB_CLUSTER_MESSAGE_ID, msg,
187
+ sizeof(*msg) + args.channel.len + args.msg.len + 2);
188
+ defer(pubsub_perform_publish, msg, NULL);
189
+ return 0;
190
+ }
191
+
192
+ static void pubsub_cluster_handle_publishing(void *data, uint32_t len) {
193
+ msg_s *msg = data;
194
+ if (len != sizeof(*msg) + msg->pub.channel.len + msg->pub.msg.len + 2) {
195
+ fprintf(stderr,
196
+ "ERROR: (pub/sub) cluster message size error. Message ignored.\n");
197
+ return;
198
+ }
199
+ msg = malloc(len);
200
+ if (!msg) {
201
+ fprintf(
202
+ stderr,
203
+ "ERROR: (pub/sub) cluster message allocation error.Message ignored.\n");
204
+ return;
205
+ }
206
+ memcpy(msg, data, len);
207
+ msg->ref = 1;
208
+ msg->pub.msg.data = (char *)(msg + 1);
209
+ if (msg->pub.channel.name)
210
+ msg->pub.channel.name = msg->pub.msg.data + msg->pub.msg.len + 1;
211
+ defer(pubsub_perform_publish, msg, NULL);
212
+ }
213
+
214
+ void pubsub_cluster_init(void) {
215
+ /* facil functions are thread safe, so we can call this without a guard. */
216
+ facil_cluster_set_handler(FACIL_PUBSUB_CLUSTER_MESSAGE_ID,
217
+ pubsub_cluster_handle_publishing);
218
+ }
219
+
220
+ static int pubsub_cluster_subscribe(const pubsub_engine_s *eng, const char *ch,
221
+ size_t ch_len, uint8_t use_pattern) {
222
+ return 0;
223
+ (void)ch;
224
+ (void)ch_len;
225
+ (void)use_pattern;
226
+ (void)eng;
227
+ }
228
+
229
+ static void pubsub_cluster_unsubscribe(const pubsub_engine_s *eng,
230
+ const char *ch, size_t ch_len,
231
+ uint8_t use_pattern) {
232
+ (void)ch;
233
+ (void)ch_len;
234
+ (void)use_pattern;
235
+ (void)eng;
236
+ }
237
+
238
+ static int pubsub_cluster_eng_publish(const pubsub_engine_s *eng,
239
+ const char *ch, size_t ch_len,
240
+ const char *msg, size_t msg_len,
241
+ uint8_t use_pattern) {
242
+ pubsub_cluster_publish((struct pubsub_publish_args){
243
+ .engine = eng,
244
+ .channel.name = (char *)ch,
245
+ .channel.len = ch_len,
246
+ .msg.data = (char *)msg,
247
+ .msg.len = msg_len,
248
+ .use_pattern = use_pattern,
249
+ .push2cluster = eng->push2cluster,
250
+ });
251
+ return 0;
252
+ }
253
+
254
+ const pubsub_engine_s PUBSUB_CLUSTER_ENGINE_S = {
255
+ .publish = pubsub_cluster_eng_publish,
256
+ .subscribe = pubsub_cluster_subscribe,
257
+ .unsubscribe = pubsub_cluster_unsubscribe,
258
+ .push2cluster = 1,
259
+ };
260
+ const pubsub_engine_s *PUBSUB_CLUSTER_ENGINE = &PUBSUB_CLUSTER_ENGINE_S;
261
+
262
+ const pubsub_engine_s PUBSUB_PROCESS_ENGINE_S = {
263
+ .publish = pubsub_cluster_eng_publish,
264
+ .subscribe = pubsub_cluster_subscribe,
265
+ .unsubscribe = pubsub_cluster_unsubscribe,
266
+ };
267
+ const pubsub_engine_s *PUBSUB_PROCESS_ENGINE = &PUBSUB_PROCESS_ENGINE_S;
268
+
269
+ pubsub_engine_s *PUBSUB_DEFAULT_ENGINE =
270
+ (pubsub_engine_s *)&PUBSUB_CLUSTER_ENGINE_S;
271
+
272
+ /* *****************************************************************************
273
+ External Engine Bridge
274
+ ***************************************************************************** */
275
+
276
+ #undef pubsub_engine_distribute
277
+
278
+ void pubsub_engine_distribute(pubsub_message_s msg) {
279
+ pubsub_cluster_publish((struct pubsub_publish_args){
280
+ .engine = msg.engine,
281
+ .channel.name = msg.channel.name,
282
+ .channel.len = msg.channel.len,
283
+ .msg.data = msg.msg.data,
284
+ .msg.len = msg.msg.len,
285
+ .push2cluster = msg.engine->push2cluster,
286
+ .use_pattern = msg.use_pattern,
287
+ });
288
+ }
289
+
290
+ static void resubscribe_action(fio_dict_s *ch_, void *eng_) {
291
+ if (!eng_)
292
+ return;
293
+ pubsub_engine_s *eng = eng_;
294
+ channel_s *ch = fio_node2obj(channel_s, channels.dict, ch_);
295
+ eng->subscribe(eng, ch->name, ch->len, ch->use_pattern);
296
+ }
297
+
298
+ static void pubsub_engine_resubscribe_task(void *eng_, void *ignored) {
299
+ if (!eng_)
300
+ return;
301
+ pubsub_engine_s *eng = eng_;
302
+ channel_s *ch;
303
+ spn_lock(&pubsub_GIL);
304
+ fio_list_for_each(channel_s, channels.list, ch, pubsub_patterns) {
305
+ eng->subscribe(eng, ch->name, ch->len, ch->use_pattern);
306
+ }
307
+
308
+ fio_dict_each(fio_dict_prefix(&pubsub_channels, (void *)&eng, sizeof(void *)),
309
+ resubscribe_action, eng);
310
+ spn_unlock(&pubsub_GIL);
311
+
312
+ (void)ignored;
313
+ }
314
+ /**
315
+ * Engines can ask facil.io to perform an action on all their active channels.
316
+ */
317
+ void pubsub_engine_resubscribe(pubsub_engine_s *eng) {
318
+ if (!eng)
319
+ return;
320
+ defer(pubsub_engine_resubscribe_task, eng, NULL);
321
+ }
322
+
323
+ /* *****************************************************************************
324
+ Hashing
325
+ ***************************************************************************** */
326
+
327
+ #define pubsub_client_hash(p1, p2, p3) \
328
+ (((((uint64_t)(p1) * ((uint64_t)p2 ^ 0x736f6d6570736575ULL)) >> 5) | \
329
+ (((uint64_t)(p1) * ((uint64_t)p2 ^ 0x736f6d6570736575ULL)) << 59)) ^ \
330
+ ((uint64_t)p3 ^ 0x646f72616e646f6dULL))
331
+
332
+ /* *****************************************************************************
333
+ API
334
+ ***************************************************************************** */
335
+
336
+ #undef pubsub_subscribe
337
+ pubsub_sub_pt pubsub_subscribe(struct pubsub_subscribe_args args) {
338
+ if (!args.on_message)
339
+ goto err_unlocked;
340
+ if (args.channel.name && !args.channel.len)
341
+ args.channel.len = strlen(args.channel.name);
342
+ if (args.channel.len > FIO_PUBBSUB_MAX_CHANNEL_LEN) {
343
+ goto err_unlocked;
344
+ }
345
+ if (!args.engine)
346
+ args.engine = PUBSUB_DEFAULT_ENGINE;
347
+ channel_s *ch;
348
+ client_s *client;
349
+ uint64_t cl_hash =
350
+ pubsub_client_hash(args.on_message, args.udata1, args.udata2);
351
+
352
+ spn_lock(&pubsub_GIL);
353
+ if (args.use_pattern) {
354
+ fio_list_for_each(channel_s, channels.list, ch, pubsub_patterns) {
355
+ if (ch->engine == args.engine && ch->len == args.channel.len &&
356
+ !memcmp(ch->name, args.channel.name, args.channel.len))
357
+ goto found_channel;
358
+ }
359
+ } else {
360
+ ch = (void *)fio_dict_get(
361
+ fio_dict_prefix(&pubsub_channels, (void *)&args.engine, sizeof(void *)),
362
+ args.channel.name, args.channel.len);
363
+ if (ch) {
364
+ ch = fio_node2obj(channel_s, channels, ch);
365
+ goto found_channel;
366
+ }
367
+ }
368
+
369
+ if (args.engine->subscribe(args.engine, args.channel.name, args.channel.len,
370
+ args.use_pattern))
371
+ goto error;
372
+ ch = malloc(sizeof(*ch) + args.channel.len + 1 + sizeof(void *));
373
+ *ch = (channel_s){
374
+ .clients = FIO_HASH_TABLE_INIT(ch->clients),
375
+ .name = (char *)(ch + 1),
376
+ .len = args.channel.len,
377
+ .use_pattern = args.use_pattern,
378
+ .engine = args.engine,
379
+ };
380
+ memcpy(ch->name, args.channel.name, args.channel.len);
381
+
382
+ ch->name[args.channel.len + sizeof(void *)] = 0;
383
+
384
+ if (args.use_pattern) {
385
+ fio_list_add(&pubsub_patterns, &ch->channels.list);
386
+ } else {
387
+ fio_dict_set(fio_dict_ensure_prefix(&pubsub_channels, (void *)&args.engine,
388
+ sizeof(void *)),
389
+ args.channel.name, args.channel.len, &ch->channels.dict);
390
+ }
391
+ // fprintf(stderr, "Created a new channel for %s\n", args.channel.name);
392
+ found_channel:
393
+
394
+ client = (void *)fio_ht_find(&ch->clients, cl_hash);
395
+ if (client) {
396
+ client = fio_ht_object(client_s, clients, client);
397
+ goto found_client;
398
+ }
399
+
400
+ client = malloc(sizeof(*client));
401
+ if (!client)
402
+ goto error;
403
+ *client = (client_s){
404
+ .on_message = args.on_message,
405
+ .on_unsubscribe = args.on_unsubscribe,
406
+ .udata1 = args.udata1,
407
+ .udata2 = args.udata2,
408
+ .parent = ch,
409
+ .active = 0,
410
+ .ref = 0,
411
+ };
412
+ fio_ht_add(&ch->clients, &client->clients, cl_hash);
413
+
414
+ found_client:
415
+
416
+ client->ref++;
417
+ spn_add(&client->active, 1);
418
+ spn_unlock(&pubsub_GIL);
419
+ return client;
420
+
421
+ error:
422
+ spn_unlock(&pubsub_GIL);
423
+ err_unlocked:
424
+ if (args.on_unsubscribe)
425
+ args.on_unsubscribe(args.udata1, args.udata2);
426
+ return NULL;
427
+ }
428
+
429
+ /**
430
+ * This helper searches for an existing subscription.
431
+ * Use with care, NEVER call `pubsub_unsubscribe` more times than you have
432
+ * called `pubsub_subscribe`, since the subscription handle memory is realesed
433
+ * onnce the reference count reaches 0.
434
+ *
435
+ * Returns a subscription pointer or NULL (none found).
436
+ */
437
+ #undef pubsub_find_sub
438
+ pubsub_sub_pt pubsub_find_sub(struct pubsub_subscribe_args args) {
439
+ if (!args.on_message)
440
+ return NULL;
441
+ if (args.channel.name && !args.channel.len)
442
+ args.channel.len = strlen(args.channel.name);
443
+ if (args.channel.len > FIO_PUBBSUB_MAX_CHANNEL_LEN) {
444
+ return NULL;
445
+ }
446
+ if (!args.engine)
447
+ args.engine = PUBSUB_DEFAULT_ENGINE;
448
+ channel_s *ch;
449
+ client_s *client;
450
+ uint64_t cl_hash =
451
+ pubsub_client_hash(args.on_message, args.udata1, args.udata2);
452
+
453
+ spn_lock(&pubsub_GIL);
454
+ if (args.use_pattern) {
455
+ fio_list_for_each(channel_s, channels.list, ch, pubsub_patterns) {
456
+ if (ch->engine == args.engine && ch->len == args.channel.len &&
457
+ !memcmp(ch->name, args.channel.name, args.channel.len))
458
+ goto found_channel;
459
+ }
460
+ } else {
461
+ ch = (void *)fio_dict_get(
462
+ fio_dict_prefix(&pubsub_channels, (void *)&args.engine, sizeof(void *)),
463
+ args.channel.name, args.channel.len);
464
+ if (ch) {
465
+ ch = fio_node2obj(channel_s, channels, ch);
466
+ goto found_channel;
467
+ }
468
+ }
469
+ goto not_found;
470
+
471
+ found_channel:
472
+
473
+ client = (void *)fio_ht_find(&ch->clients, cl_hash);
474
+ if (client) {
475
+ client = fio_ht_object(client_s, clients, client);
476
+ spn_unlock(&pubsub_GIL);
477
+ return client;
478
+ }
479
+
480
+ not_found:
481
+ spn_unlock(&pubsub_GIL);
482
+ return NULL;
483
+ }
484
+
485
+ void pubsub_unsubscribe(pubsub_sub_pt client) {
486
+ if (!client)
487
+ return;
488
+ spn_lock(&pubsub_GIL);
489
+ client->ref--;
490
+ if (client->ref) {
491
+ spn_unlock(&pubsub_GIL);
492
+ spn_sub(&client->active, 1);
493
+ return;
494
+ }
495
+ fio_ht_remove(&client->clients);
496
+ if (client->parent->clients.count) {
497
+ spn_unlock(&pubsub_GIL);
498
+ defer(pubsub_free_client, client, NULL);
499
+ return;
500
+ }
501
+ channel_s *ch = client->parent;
502
+ if (ch->use_pattern) {
503
+ fio_list_remove(&ch->channels.list);
504
+ } else {
505
+ fio_dict_remove(&ch->channels.dict);
506
+ }
507
+ spn_unlock(&pubsub_GIL);
508
+ defer(pubsub_free_client, client, NULL);
509
+ ch->engine->unsubscribe(ch->engine, ch->name, ch->len, ch->use_pattern);
510
+ fio_ht_rehash(&ch->clients, 0);
511
+ free(ch);
512
+ }
513
+
514
+ #undef pubsub_publish
515
+ int pubsub_publish(struct pubsub_publish_args args) {
516
+ if (!args.msg.data)
517
+ return -1;
518
+ if (!args.msg.len)
519
+ args.msg.len = strlen(args.msg.data);
520
+ if (args.channel.name && !args.channel.len)
521
+ args.channel.len = strlen(args.channel.name);
522
+ if (!args.engine) {
523
+ args.engine = PUBSUB_DEFAULT_ENGINE;
524
+ args.push2cluster = 1;
525
+ } else if (args.push2cluster)
526
+ PUBSUB_CLUSTER_ENGINE_S.publish(args.engine, args.channel.name,
527
+ args.channel.len, args.msg.data,
528
+ args.msg.len, args.use_pattern);
529
+ return args.engine->publish(args.engine, args.channel.name, args.channel.len,
530
+ args.msg.data, args.msg.len, args.use_pattern);
531
+ }
532
+
533
+ /**
534
+ * defers message hadling if it can't be performed (i.e., resource is busy) or
535
+ * should be fragmented (allowing large tasks to be broken down).
536
+ */
537
+ void pubsub_defer(pubsub_message_s *msg_) {
538
+ if (!msg_)
539
+ return;
540
+ msg_container_s *msg = fio_node2obj(msg_container_s, msg, msg_);
541
+ spn_add(&msg->origin->ref, 1);
542
+ spn_add(&msg->msg.subscription->active, 1);
543
+ defer(pubsub_deliver_msg, msg->msg.subscription, msg->origin);
544
+ }