jmoses-couchbase 1.3.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. data/.gitignore +15 -0
  2. data/.travis.yml +22 -0
  3. data/.yardopts +5 -0
  4. data/CONTRIBUTING.markdown +75 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE +201 -0
  7. data/Makefile +3 -0
  8. data/README.markdown +665 -0
  9. data/RELEASE_NOTES.markdown +819 -0
  10. data/Rakefile +20 -0
  11. data/couchbase.gemspec +49 -0
  12. data/examples/chat-em/Gemfile +7 -0
  13. data/examples/chat-em/README.markdown +45 -0
  14. data/examples/chat-em/server.rb +82 -0
  15. data/examples/chat-goliath-grape/Gemfile +5 -0
  16. data/examples/chat-goliath-grape/README.markdown +50 -0
  17. data/examples/chat-goliath-grape/app.rb +67 -0
  18. data/examples/chat-goliath-grape/config/app.rb +20 -0
  19. data/examples/transcoders/Gemfile +3 -0
  20. data/examples/transcoders/README.markdown +59 -0
  21. data/examples/transcoders/cb-zcat +40 -0
  22. data/examples/transcoders/cb-zcp +45 -0
  23. data/examples/transcoders/gzip_transcoder.rb +49 -0
  24. data/examples/transcoders/options.rb +54 -0
  25. data/ext/couchbase_ext/.gitignore +4 -0
  26. data/ext/couchbase_ext/arguments.c +956 -0
  27. data/ext/couchbase_ext/arithmetic.c +316 -0
  28. data/ext/couchbase_ext/bucket.c +1373 -0
  29. data/ext/couchbase_ext/context.c +65 -0
  30. data/ext/couchbase_ext/couchbase_ext.c +1364 -0
  31. data/ext/couchbase_ext/couchbase_ext.h +644 -0
  32. data/ext/couchbase_ext/delete.c +163 -0
  33. data/ext/couchbase_ext/eventmachine_plugin.c +452 -0
  34. data/ext/couchbase_ext/extconf.rb +169 -0
  35. data/ext/couchbase_ext/get.c +316 -0
  36. data/ext/couchbase_ext/gethrtime.c +129 -0
  37. data/ext/couchbase_ext/http.c +432 -0
  38. data/ext/couchbase_ext/multithread_plugin.c +1090 -0
  39. data/ext/couchbase_ext/observe.c +171 -0
  40. data/ext/couchbase_ext/plugin_common.c +171 -0
  41. data/ext/couchbase_ext/result.c +129 -0
  42. data/ext/couchbase_ext/stats.c +163 -0
  43. data/ext/couchbase_ext/store.c +542 -0
  44. data/ext/couchbase_ext/timer.c +192 -0
  45. data/ext/couchbase_ext/touch.c +186 -0
  46. data/ext/couchbase_ext/unlock.c +176 -0
  47. data/ext/couchbase_ext/utils.c +551 -0
  48. data/ext/couchbase_ext/version.c +142 -0
  49. data/lib/action_dispatch/middleware/session/couchbase_store.rb +38 -0
  50. data/lib/active_support/cache/couchbase_store.rb +430 -0
  51. data/lib/couchbase.rb +155 -0
  52. data/lib/couchbase/bucket.rb +457 -0
  53. data/lib/couchbase/cluster.rb +119 -0
  54. data/lib/couchbase/connection_pool.rb +58 -0
  55. data/lib/couchbase/constants.rb +12 -0
  56. data/lib/couchbase/result.rb +26 -0
  57. data/lib/couchbase/transcoder.rb +120 -0
  58. data/lib/couchbase/utils.rb +62 -0
  59. data/lib/couchbase/version.rb +21 -0
  60. data/lib/couchbase/view.rb +506 -0
  61. data/lib/couchbase/view_row.rb +272 -0
  62. data/lib/ext/multi_json_fix.rb +56 -0
  63. data/lib/rack/session/couchbase.rb +108 -0
  64. data/tasks/benchmark.rake +6 -0
  65. data/tasks/compile.rake +160 -0
  66. data/tasks/test.rake +100 -0
  67. data/tasks/util.rake +21 -0
  68. data/test/profile/.gitignore +1 -0
  69. data/test/profile/Gemfile +6 -0
  70. data/test/profile/benchmark.rb +195 -0
  71. data/test/setup.rb +178 -0
  72. data/test/test_arithmetic.rb +185 -0
  73. data/test/test_async.rb +316 -0
  74. data/test/test_bucket.rb +276 -0
  75. data/test/test_cas.rb +235 -0
  76. data/test/test_couchbase.rb +77 -0
  77. data/test/test_couchbase_connection_pool.rb +77 -0
  78. data/test/test_couchbase_rails_cache_store.rb +361 -0
  79. data/test/test_delete.rb +120 -0
  80. data/test/test_errors.rb +82 -0
  81. data/test/test_eventmachine.rb +70 -0
  82. data/test/test_format.rb +164 -0
  83. data/test/test_get.rb +407 -0
  84. data/test/test_stats.rb +57 -0
  85. data/test/test_store.rb +216 -0
  86. data/test/test_timer.rb +42 -0
  87. data/test/test_touch.rb +97 -0
  88. data/test/test_unlock.rb +119 -0
  89. data/test/test_utils.rb +58 -0
  90. data/test/test_version.rb +52 -0
  91. metadata +353 -0
@@ -0,0 +1,1090 @@
1
+ /* vim: ft=c et ts=8 sts=4 sw=4 cino=
2
+ *
3
+ * Copyright 2012 Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #include "couchbase_ext.h"
19
+
20
+
21
+ #ifndef _WIN32
22
+
23
+ #ifndef HAVE_RB_THREAD_BLOCKING_REGION
24
+ #include <rubysig.h>
25
+ #endif
26
+ #include <errno.h>
27
+ #ifdef HAVE_POLL
28
+ #include <poll.h>
29
+ #endif
30
+
31
+ /* events sorted array */
32
+ typedef struct rb_mt_event rb_mt_event;
33
+ struct rb_mt_event {
34
+ void *cb_data;
35
+ void (*handler)(lcb_socket_t sock, short which, void *cb_data);
36
+ lcb_socket_t socket;
37
+ int loop_index;
38
+ short flags;
39
+ short actual_flags;
40
+ short inserted;
41
+ rb_mt_event *next;
42
+ };
43
+
44
+ typedef struct rb_mt_socket_list rb_mt_socket_list;
45
+ struct rb_mt_socket_list {
46
+ lcb_socket_t socket;
47
+ short flags;
48
+ rb_mt_event *first;
49
+ };
50
+
51
+ typedef struct rb_mt_events rb_mt_events;
52
+ struct rb_mt_events {
53
+ uint32_t capa;
54
+ uint32_t count;
55
+ rb_mt_socket_list *sockets;
56
+ };
57
+
58
+ static int
59
+ events_init(rb_mt_events *events)
60
+ {
61
+ rb_mt_socket_list *new_socks = malloc(4 * sizeof(*new_socks));
62
+ if (new_socks == NULL) {
63
+ return 0;
64
+ }
65
+ events->capa = 4;
66
+ events->count = 0;
67
+ events->sockets = new_socks;
68
+ return 1;
69
+ }
70
+
71
+ static void
72
+ events_finalize(rb_mt_events *events)
73
+ {
74
+ if (events->sockets) {
75
+ uint32_t i;
76
+ for(i = 0; i < events->count; i++) {
77
+ rb_mt_socket_list *list = &events->sockets[i];
78
+ while(list->first) {
79
+ rb_mt_event *next = list->first->next;
80
+ free(list->first);
81
+ list->first = next;
82
+ }
83
+ }
84
+ free(events->sockets);
85
+ events->sockets = NULL;
86
+ }
87
+ events->capa = 0;
88
+ events->count = 0;
89
+ }
90
+
91
+ static uint32_t
92
+ events_index(rb_mt_events *events, lcb_socket_t socket)
93
+ {
94
+ uint32_t m, l = 0, r = events->count;
95
+ while(l < r) {
96
+ m = l + (r - l) / 2;
97
+ if (events->sockets[m].socket >= socket) {
98
+ r = m;
99
+ } else {
100
+ l = m + 1;
101
+ }
102
+ }
103
+ return l;
104
+ }
105
+
106
+ static void
107
+ events_insert(rb_mt_events *events, rb_mt_event *event)
108
+ {
109
+ uint32_t i = events_index(events, event->socket);
110
+ rb_mt_socket_list *list = &events->sockets[i];
111
+ if (i == events->count || list->socket != event->socket) {
112
+ if (events->capa == events->count) {
113
+ uint32_t new_capa = events->capa << 1;
114
+ rb_mt_socket_list *new_socks = realloc(events->sockets, new_capa * sizeof(*new_socks));
115
+ if (new_socks == NULL) {
116
+ rb_raise(cb_eClientNoMemoryError, "failed to allocate memory for events array");
117
+ }
118
+ events->sockets = new_socks;
119
+ events->capa = new_capa;
120
+ list = &events->sockets[i];
121
+ }
122
+ if (i < events->count) {
123
+ MEMMOVE(events->sockets+i+1, events->sockets+i, rb_mt_socket_list, events->count - i);
124
+ }
125
+ events->count++;
126
+ list->socket = event->socket;
127
+ list->flags = event->flags;
128
+ list->first = event;
129
+ event->next = NULL;
130
+ } else {
131
+ list->flags |= event->flags;
132
+ event->next = list->first;
133
+ list->first = event;
134
+ }
135
+ event->inserted = 1;
136
+ }
137
+
138
+ static void
139
+ event_list_fix_flags(rb_mt_socket_list *list)
140
+ {
141
+ short flags = 0;
142
+ rb_mt_event *event = list->first;
143
+ while (event) {
144
+ flags |= event->flags;
145
+ event = event->next;
146
+ }
147
+ list->flags = flags;
148
+ }
149
+
150
+ static void
151
+ events_remove(rb_mt_events *events, rb_mt_event *event)
152
+ {
153
+ uint32_t i = events_index(events, event->socket);
154
+ rb_mt_socket_list *list = &events->sockets[i];
155
+ rb_mt_event **next;
156
+ if (list->socket != event->socket) {
157
+ rb_raise(rb_eIndexError, "There is no socket in event loop");
158
+ }
159
+ next = &list->first;
160
+ for(;;) {
161
+ if (*next == NULL) {
162
+ rb_raise(rb_eIndexError, "There is no event in event loop");
163
+ }
164
+ if (*next == event) {
165
+ *next = event->next;
166
+ event->next = NULL;
167
+ event->inserted = 0;
168
+ break;
169
+ }
170
+ next = &event->next;
171
+ }
172
+ if (list->first == NULL) {
173
+ MEMMOVE(events->sockets + i, events->sockets + i + 1, rb_mt_socket_list, events->count - i - 1);
174
+ events->count--;
175
+ } else {
176
+ event_list_fix_flags(list);
177
+ }
178
+ }
179
+
180
+ static void
181
+ events_fix_flags(rb_mt_events *events, lcb_socket_t socket)
182
+ {
183
+ uint32_t i = events_index(events, socket);
184
+ rb_mt_socket_list *list = &events->sockets[i];
185
+ if (list->socket != socket) {
186
+ rb_raise(rb_eIndexError, "There is no socket in event loop");
187
+ }
188
+ event_list_fix_flags(list);
189
+ }
190
+
191
+ static inline lcb_socket_t
192
+ events_max_fd(rb_mt_events *events)
193
+ {
194
+ if (events->count) {
195
+ return events->sockets[events->count - 1].socket;
196
+ } else {
197
+ return -1;
198
+ }
199
+ }
200
+
201
+ /* events sorted array end */
202
+
203
+ /* timers heap */
204
+ typedef struct rb_mt_timer rb_mt_timer;
205
+ struct rb_mt_timer {
206
+ void *cb_data;
207
+ void (*handler)(lcb_socket_t sock, short which, void *cb_data);
208
+ int index;
209
+ hrtime_t ts;
210
+ hrtime_t period;
211
+ };
212
+
213
+ typedef struct rb_mt_timers rb_mt_timers;
214
+ struct rb_mt_timers {
215
+ uint32_t capa;
216
+ uint32_t count;
217
+ rb_mt_timer **timers;
218
+ };
219
+
220
+ static int
221
+ timers_init(rb_mt_timers *timers)
222
+ {
223
+ rb_mt_timer **new_timers = malloc(4 * sizeof(*new_timers));
224
+ if (new_timers == NULL) {
225
+ return 0;
226
+ }
227
+ timers->capa = 4;
228
+ timers->count = 0;
229
+ timers->timers = new_timers;
230
+ return 1;
231
+ }
232
+
233
+ static void
234
+ timers_finalize(rb_mt_timers *timers)
235
+ {
236
+ if (timers->timers) {
237
+ uint32_t i;
238
+ for(i = 0; i < timers->count; i++) {
239
+ free(timers->timers[i]);
240
+ }
241
+ free(timers->timers);
242
+ timers->timers = NULL;
243
+ }
244
+ timers->count = 0;
245
+ timers->capa = 0;
246
+ }
247
+
248
+ #define tms_at(_timers, at) (_timers)->timers[(at)]
249
+ #define tms_ts_at(timers, at) tms_at((timers), (at))->ts
250
+
251
+ static void
252
+ timers_move_last(rb_mt_timers *timers, uint32_t to)
253
+ {
254
+ if (to < timers->count - 1) {
255
+ rb_mt_timer *last = tms_at(timers, timers->count - 1);
256
+ tms_at(timers, to) = last;
257
+ last->index = to;
258
+ }
259
+ timers->count--;
260
+ }
261
+
262
+ static inline void
263
+ timers_swap(rb_mt_timers *timers, uint32_t i, uint32_t j)
264
+ {
265
+ rb_mt_timer *itmp = tms_at(timers, j);
266
+ rb_mt_timer *jtmp = tms_at(timers, i);
267
+ tms_at(timers, i) = itmp;
268
+ tms_at(timers, j) = jtmp;
269
+ itmp->index = i;
270
+ jtmp->index = j;
271
+ }
272
+
273
+ static void timers_heapify_up(rb_mt_timers *timers, uint32_t pos);
274
+
275
+ static void
276
+ timers_insert(rb_mt_timers *timers, rb_mt_timer *timer)
277
+ {
278
+ if (timers->count == timers->capa) {
279
+ rb_mt_timer **new_timers;
280
+ size_t new_capa = timers->capa << 1;
281
+ new_timers = realloc(timers->timers, new_capa * sizeof(rb_mt_timer*));
282
+ if (new_timers == NULL) {
283
+ rb_raise(cb_eClientNoMemoryError, "failed to allocate memory for timers heap");
284
+ }
285
+ timers->timers = new_timers;
286
+ timers->capa = new_capa;
287
+ }
288
+ tms_at(timers, timers->count) = timer;
289
+ timer->index = timers->count;
290
+ timers->count++;
291
+ timers_heapify_up(timers, timer->index);
292
+ }
293
+
294
+ static void
295
+ timers_heapify_up(rb_mt_timers *timers, uint32_t pos)
296
+ {
297
+ hrtime_t cur_ts = tms_ts_at(timers, pos);
298
+ uint32_t higher = (pos - 1) / 2;
299
+ while (pos && tms_ts_at(timers, higher) > cur_ts) {
300
+ timers_swap(timers, higher, pos);
301
+ pos = higher;
302
+ higher = (pos - 1) / 2;
303
+ }
304
+ }
305
+
306
+ static void
307
+ timers_heapify_down(rb_mt_timers *timers, uint32_t pos)
308
+ {
309
+ uint32_t count = timers->count;
310
+ uint32_t middle = (timers->count - 2) / 2;
311
+ hrtime_t cur_ts = tms_ts_at(timers, pos);
312
+ if (count == 1) return;
313
+ while (pos <= middle) {
314
+ uint32_t min_pos = pos;
315
+ hrtime_t ch_ts, min_ts = cur_ts;
316
+
317
+ if ((ch_ts = tms_ts_at(timers, pos * 2 + 1)) < min_ts) {
318
+ min_pos = pos * 2 + 1;
319
+ min_ts = ch_ts;
320
+ }
321
+
322
+ if (pos * 2 + 2 < count && tms_ts_at(timers, pos * 2 + 2) < min_ts) {
323
+ min_pos = pos * 2 + 2;
324
+ }
325
+
326
+ if (min_pos == pos) break;
327
+ timers_swap(timers, pos, min_pos);
328
+ pos = min_pos;
329
+ }
330
+ }
331
+
332
+ static void
333
+ timers_heapify_item(rb_mt_timers *timers, uint32_t pos)
334
+ {
335
+ if (pos && tms_ts_at(timers, pos) < tms_ts_at(timers, (pos - 1) / 2)) {
336
+ timers_heapify_up(timers, pos);
337
+ } else {
338
+ timers_heapify_down(timers, pos);
339
+ }
340
+ }
341
+
342
+ static inline hrtime_t
343
+ timers_minimum(rb_mt_timers *timers)
344
+ {
345
+ if (timers->count) {
346
+ return tms_ts_at(timers, 0);
347
+ } else {
348
+ return 0;
349
+ }
350
+ }
351
+
352
+ static inline rb_mt_timer *
353
+ timers_first(rb_mt_timers *timers)
354
+ {
355
+ if (timers->count) {
356
+ return tms_at(timers, 0);
357
+ } else {
358
+ return 0;
359
+ }
360
+ }
361
+
362
+ static void
363
+ timers_remove_timer(rb_mt_timers *timers, rb_mt_timer *timer)
364
+ {
365
+ uint32_t at = timer->index;
366
+ timer->index = -1;
367
+ if (at < timers->count - 1) {
368
+ timers_move_last(timers, at);
369
+ timers_heapify_item(timers, at);
370
+ } else {
371
+ timers->count--;
372
+ }
373
+ }
374
+
375
+ static void
376
+ timers_run(rb_mt_timers *timers, hrtime_t now)
377
+ {
378
+ hrtime_t next_time = timers_minimum(timers);
379
+ while (next_time && next_time < now) {
380
+ rb_mt_timer *first = timers_first(timers);
381
+
382
+ first->ts = now + first->period;
383
+ timers_heapify_item(timers, 0);
384
+
385
+ first->handler(-1, 0, first->cb_data);
386
+
387
+ next_time = timers_minimum(timers);
388
+ }
389
+ }
390
+ /* timers heap end */
391
+
392
+ /* callbacks array */
393
+ typedef struct rb_mt_callbacks rb_mt_callbacks;
394
+ struct rb_mt_callbacks {
395
+ uint32_t capa;
396
+ uint32_t count;
397
+ rb_mt_event **events;
398
+ };
399
+
400
+ static int
401
+ callbacks_init(rb_mt_callbacks *callbacks)
402
+ {
403
+ rb_mt_event **new_events = calloc(4, sizeof(*new_events));
404
+ if (new_events == NULL) {
405
+ return 0;
406
+ }
407
+ callbacks->events = new_events;
408
+ callbacks->capa = 4;
409
+ callbacks->count = 0;
410
+ return 1;
411
+ }
412
+
413
+ static void
414
+ callbacks_finalize(rb_mt_callbacks *callbacks)
415
+ {
416
+ if (callbacks->events) {
417
+ free(callbacks->events);
418
+ callbacks->events = NULL;
419
+ }
420
+ callbacks->capa = 0;
421
+ callbacks->count = 0;
422
+ }
423
+
424
+ static void
425
+ callbacks_push(rb_mt_callbacks *callbacks, rb_mt_event *event)
426
+ {
427
+ if (callbacks->count == callbacks->capa) {
428
+ uint32_t new_capa = callbacks->capa << 1;
429
+ rb_mt_event **new_events = realloc(callbacks->events, new_capa * sizeof(*new_events));
430
+ if (new_events == NULL) {
431
+ rb_raise(cb_eClientNoMemoryError, "failed to allocate memory for callbacks array");
432
+ }
433
+ callbacks->capa = new_capa;
434
+ callbacks->events = new_events;
435
+ }
436
+ event->loop_index = callbacks->count;
437
+ callbacks->events[callbacks->count] = event;
438
+ callbacks->count++;
439
+ }
440
+
441
+ static void
442
+ callbacks_remove(rb_mt_callbacks *callbacks, rb_mt_event *event)
443
+ {
444
+ int i = event->loop_index;
445
+ if (i >= 0) {
446
+ if (callbacks->events[i] != event) {
447
+ rb_raise(rb_eIndexError, "callback index belongs to different callback");
448
+ }
449
+ event->loop_index = -1;
450
+ callbacks->events[i] = NULL;
451
+ }
452
+ }
453
+
454
+ static void
455
+ callbacks_run(rb_mt_callbacks *callbacks)
456
+ {
457
+ uint32_t i;
458
+ for(i = 0; i < callbacks->count; i++) {
459
+ rb_mt_event *cb = callbacks->events[i];
460
+ if (cb) {
461
+ cb->loop_index = -1;
462
+ callbacks->events[i] = NULL;
463
+ cb->handler(cb->socket, cb->actual_flags, cb->cb_data);
464
+ }
465
+ }
466
+ callbacks->count = 0;
467
+ }
468
+
469
+ static void
470
+ callbacks_clean(rb_mt_callbacks *callbacks)
471
+ {
472
+ uint32_t i;
473
+ for(i = 0; i < callbacks->count; i++) {
474
+ if (callbacks->events[i]) {
475
+ callbacks->events[i]->loop_index = -1;
476
+ callbacks->events[i] = NULL;
477
+ }
478
+ }
479
+ callbacks->count = 0;
480
+ }
481
+ /* callbacks array end */
482
+
483
+ typedef struct rb_mt_loop rb_mt_loop;
484
+ struct rb_mt_loop {
485
+ rb_mt_events events;
486
+ rb_mt_timers timers;
487
+ rb_mt_callbacks callbacks;
488
+ short run;
489
+ };
490
+
491
+ static rb_mt_loop*
492
+ loop_create()
493
+ {
494
+ rb_mt_loop *loop = calloc(1, sizeof(*loop));
495
+ if (loop == NULL) return NULL;
496
+ if (!events_init(&loop->events)) goto free_loop;
497
+ if (!timers_init(&loop->timers)) goto free_events;
498
+ if (!callbacks_init(&loop->callbacks)) goto free_timers;
499
+ return loop;
500
+
501
+ free_timers:
502
+ timers_finalize(&loop->timers);
503
+ free_events:
504
+ events_finalize(&loop->events);
505
+ free_loop:
506
+ free(loop);
507
+ return NULL;
508
+ }
509
+
510
+ static void
511
+ loop_destroy(rb_mt_loop *loop)
512
+ {
513
+ events_finalize(&loop->events);
514
+ timers_finalize(&loop->timers);
515
+ callbacks_finalize(&loop->callbacks);
516
+ free(loop);
517
+ }
518
+
519
+ static void
520
+ loop_remove_event(rb_mt_loop *loop, rb_mt_event *event)
521
+ {
522
+ if (event->inserted) {
523
+ events_remove(&loop->events, event);
524
+ }
525
+ callbacks_remove(&loop->callbacks, event);
526
+ }
527
+
528
+ static void
529
+ loop_enque_events(rb_mt_callbacks *callbacks, rb_mt_event *sock, short flags)
530
+ {
531
+ while (sock) {
532
+ short actual = sock->flags & flags;
533
+ if (actual) {
534
+ sock->actual_flags = actual;
535
+ callbacks_push(callbacks, (rb_mt_event*)sock);
536
+ }
537
+ sock = sock->next;
538
+ }
539
+ }
540
+
541
+ /* loop select implementation */
542
+ #ifndef HAVE_RB_THREAD_FD_SELECT
543
+ typedef fd_set rb_fdset_t;
544
+ #define rb_fd_init FD_ZERO
545
+ #define rb_fd_set FD_SET
546
+ #define rb_fd_isset FD_ISSET
547
+ #define rb_fd_term(set) (void)0
548
+ #define rb_thread_fd_select rb_thread_select
549
+ #endif
550
+
551
+ typedef struct loop_select_arg {
552
+ rb_mt_loop *loop;
553
+ rb_fdset_t in, out, exc;
554
+ } ls_arg;
555
+
556
+ static void
557
+ ls_arg_free(void *p) {
558
+ ls_arg *args = p;
559
+ if (args) {
560
+ rb_fd_term(&args->in);
561
+ rb_fd_term(&args->out);
562
+ xfree(args);
563
+ }
564
+ }
565
+
566
+ static VALUE
567
+ ls_arg_alloc(ls_arg **args)
568
+ {
569
+ return Data_Make_Struct(rb_cObject, ls_arg, 0, ls_arg_free, *args);
570
+ }
571
+
572
+ static VALUE
573
+ loop_run_select(VALUE argp)
574
+ {
575
+ ls_arg *args = (ls_arg*) argp;
576
+ rb_mt_loop *loop = args->loop;
577
+ rb_fdset_t *in = NULL, *out = NULL, *exc = NULL;
578
+ struct timeval timeout;
579
+ struct timeval *timeoutp = NULL;
580
+ int result, max = 0;
581
+ hrtime_t now, next_time;
582
+
583
+ next_time = timers_minimum(&loop->timers);
584
+ if (next_time) {
585
+ now = gethrtime();
586
+ if (next_time <= now) {
587
+ timeout.tv_sec = 0;
588
+ timeout.tv_usec = 0;
589
+ } else {
590
+ hrtime_t hrto = (next_time - now) / 1000;
591
+ timeout.tv_sec = (long)(hrto / 1000000);
592
+ timeout.tv_usec = (long)(hrto % 1000000);
593
+ }
594
+ timeoutp = &timeout;
595
+ }
596
+
597
+ if (loop->events.count) {
598
+ uint32_t i;
599
+ rb_fd_init(&args->in);
600
+ rb_fd_init(&args->out);
601
+ rb_fd_init(&args->exc);
602
+ for(i = 0; i < loop->events.count; i++) {
603
+ rb_mt_socket_list *list = &loop->events.sockets[i];
604
+ if (list->flags != 0) {
605
+ if (list->flags & LCB_READ_EVENT) {
606
+ in = &args->in;
607
+ rb_fd_set(list->socket, in);
608
+ }
609
+ if (list->flags & LCB_WRITE_EVENT) {
610
+ out = &args->out;
611
+ rb_fd_set(list->socket, out);
612
+ }
613
+ exc = &args->exc;
614
+ rb_fd_set(list->socket, exc);
615
+ }
616
+ }
617
+ max = events_max_fd(&loop->events) + 1;
618
+ }
619
+
620
+ result = rb_thread_fd_select(max, in, out, exc, timeoutp);
621
+
622
+ if (result < 0) {
623
+ rb_sys_fail("rb_thread_fd_select");
624
+ }
625
+ /* fix current time so that socket callbacks will not cause timers timeouts */
626
+ if (next_time) {
627
+ now = gethrtime();
628
+ }
629
+
630
+ if (result > 0) {
631
+ uint32_t i;
632
+ for(i = 0; i < loop->events.count && result; i++) {
633
+ rb_mt_socket_list *list = loop->events.sockets + i;
634
+ rb_mt_event *sock = list->first;
635
+ short flags = 0;
636
+ if (in && rb_fd_isset(list->socket, in)) {
637
+ flags |= LCB_READ_EVENT;
638
+ result--;
639
+ }
640
+ if (out && rb_fd_isset(list->socket, out)) {
641
+ flags |= LCB_WRITE_EVENT;
642
+ result--;
643
+ }
644
+ if (exc && rb_fd_isset(list->socket, exc)) {
645
+ flags = LCB_ERROR_EVENT | LCB_WRITE_EVENT;
646
+ result--;
647
+ }
648
+ if (flags) {
649
+ loop_enque_events(&loop->callbacks, sock, flags);
650
+ }
651
+ }
652
+ callbacks_run(&loop->callbacks);
653
+ }
654
+
655
+ if (next_time) {
656
+ timers_run(&loop->timers, now);
657
+ }
658
+ if (loop->events.count == 0 && loop->timers.count == 0) {
659
+ loop->run = 0;
660
+ }
661
+ return Qnil;
662
+ }
663
+
664
+ static VALUE
665
+ loop_select_cleanup(VALUE argp)
666
+ {
667
+ ls_arg *args = DATA_PTR(argp);
668
+ if (args) {
669
+ callbacks_clean(&args->loop->callbacks);
670
+ ls_arg_free(args);
671
+ DATA_PTR(argp) = 0;
672
+ }
673
+ return Qnil;
674
+ }
675
+ /* loop select implementaion end */
676
+
677
+ /* loop poll implementation */
678
+ #ifdef HAVE_POLL
679
+ /* code influenced by ruby's source and cool.io */
680
+ #define POLLIN_SET (POLLIN | POLLHUP | POLLERR)
681
+ #define POLLOUT_SET (POLLOUT | POLLHUP | POLLERR)
682
+ #define HRTIME_INFINITY ((hrtime_t)~0)
683
+
684
+ #ifdef HAVE_PPOLL
685
+ static int
686
+ xpoll(struct pollfd *fds, nfds_t nfds, hrtime_t timeout)
687
+ {
688
+ if (timeout != HRTIME_INFINITY) {
689
+ struct timespec ts;
690
+ ts.tv_sec = (long)(timeout / (1000 * 1000 * 1000));
691
+ ts.tv_nsec = (long)(timeout % (1000 * 1000 * 1000));
692
+ return ppoll(fds, nfds, &ts, NULL);
693
+ }
694
+ return ppoll(fds, nfds, NULL, NULL);
695
+ }
696
+ #else
697
+ #define TIMEOUT_MAX ((hrtime_t)(((unsigned int)~0) >> 1))
698
+ static int
699
+ xpoll(struct pollfd *fds, nfds_t nfds, hrtime_t timeout)
700
+ {
701
+ int ts = -1;
702
+ if (timeout != HRTIME_INFINITY) {
703
+ timeout = (timeout + 999999) / (1000 * 1000);
704
+ if (timeout <= TIMEOUT_MAX) {
705
+ ts = (int)timeout;
706
+ }
707
+ }
708
+ return poll(fds, nfds, ts);
709
+ }
710
+ #endif
711
+
712
+ typedef struct poll_args lp_arg;
713
+ struct poll_args {
714
+ rb_mt_loop *loop;
715
+ struct pollfd *fds;
716
+ nfds_t nfd;
717
+ hrtime_t ts;
718
+ int result;
719
+ int lerrno;
720
+ };
721
+
722
+ static void
723
+ lp_arg_free(void *p)
724
+ {
725
+ lp_arg *args = p;
726
+ if (args) {
727
+ if (args->fds) {
728
+ free(args->fds);
729
+ }
730
+ xfree(args);
731
+ }
732
+ }
733
+
734
+ static VALUE
735
+ lp_arg_alloc(lp_arg **args)
736
+ {
737
+ return Data_Make_Struct(rb_cObject, lp_arg, 0, lp_arg_free, *args);
738
+ }
739
+
740
+ #if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
741
+ static VALUE
742
+ loop_blocking_poll(void *argp)
743
+ {
744
+ lp_arg *args = argp;
745
+ args->result = xpoll(args->fds, args->nfd, args->ts);
746
+ if (args->result < 0) args->lerrno = errno;
747
+ return Qnil;
748
+ }
749
+ #endif
750
+
751
+ static VALUE
752
+ loop_run_poll(VALUE argp)
753
+ {
754
+ lp_arg *args = (lp_arg*)argp;
755
+ rb_mt_loop *loop = args->loop;
756
+ hrtime_t now, next_time;
757
+
758
+ if (loop->events.count) {
759
+ uint32_t i;
760
+ args->fds = calloc(loop->events.count, sizeof(struct pollfd));
761
+ if (args->fds == NULL) {
762
+ rb_raise(cb_eClientNoMemoryError, "failed to allocate memory for pollfd");
763
+ }
764
+ for(i = 0; i < loop->events.count; i++) {
765
+ rb_mt_socket_list *list = &loop->events.sockets[i];
766
+ args->fds[i].fd = list->socket;
767
+ args->fds[i].events =
768
+ (list->flags & LCB_READ_EVENT ? POLLIN : 0) |
769
+ (list->flags & LCB_WRITE_EVENT ? POLLOUT : 0);
770
+ }
771
+ args->nfd = loop->events.count;
772
+ }
773
+
774
+ retry:
775
+ next_time = timers_minimum(&loop->timers);
776
+ if (next_time) {
777
+ now = gethrtime();
778
+ args->ts = next_time <= now ? 0 : next_time - now;
779
+ } else {
780
+ args->ts = HRTIME_INFINITY;
781
+ }
782
+
783
+
784
+ #if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
785
+ rb_thread_call_without_gvl((void *(*)(void*))loop_blocking_poll, args, RUBY_UBF_IO, 0);
786
+ #elif defined(HAVE_RB_THREAD_BLOCKING_REGION)
787
+ rb_thread_blocking_region(loop_blocking_poll, args, RUBY_UBF_PROCESS, NULL);
788
+ #else
789
+ if (rb_thread_alone()) {
790
+ TRAP_BEG;
791
+ args->result = xpoll(args->fds, args->nfd, args->ts);
792
+ if (args->result < 0) args->lerrno = errno;
793
+ TRAP_END;
794
+ } else {
795
+ /* 5 millisecond pause */
796
+ hrtime_t mini_pause = 5000000;
797
+ int exact = 0;
798
+ if (args->ts != HRTIME_INFINITY && args->ts < mini_pause) {
799
+ mini_pause = args->ts;
800
+ exact = 1;
801
+ }
802
+ TRAP_BEG;
803
+ args->result = xpoll(args->fds, args->nfd, mini_pause);
804
+ if (args->result < 0) args->lerrno = errno;
805
+ TRAP_END;
806
+ if (args->result == 0 && !exact) {
807
+ args->result = -1;
808
+ args->lerrno = EINTR;
809
+ }
810
+ }
811
+ #endif
812
+
813
+ if (args->result < 0) {
814
+ errno = args->lerrno;
815
+ switch (errno) {
816
+ case EINTR:
817
+ #ifdef ERESTART
818
+ case ERESTART:
819
+ #endif
820
+ #ifndef HAVE_RB_THREAD_BLOCKING_REGION
821
+ rb_thread_schedule();
822
+ #endif
823
+ goto retry;
824
+ }
825
+ rb_sys_fail("poll");
826
+ return Qnil;
827
+ }
828
+
829
+ if (next_time) {
830
+ now = gethrtime();
831
+ }
832
+
833
+ if (args->result > 0) {
834
+ uint32_t cnt = args->result;
835
+ uint32_t fd_n = 0, ev_n = 0;
836
+ while (cnt && fd_n < args->nfd && ev_n < loop->events.count) {
837
+ struct pollfd *res = args->fds + fd_n;
838
+ rb_mt_socket_list *list = loop->events.sockets + ev_n;
839
+ rb_mt_event *sock = list->first;
840
+
841
+ /* if plugin used correctly, this checks are noop */
842
+ if (res->fd < list->socket) {
843
+ fd_n++;
844
+ continue;
845
+ } else if (res->fd > list->socket) {
846
+ ev_n++;
847
+ continue;
848
+ }
849
+
850
+ if (res->revents) {
851
+ short flags =
852
+ ((res->revents & POLLIN_SET) ? LCB_READ_EVENT : 0) |
853
+ ((res->revents & POLLOUT_SET) ? LCB_WRITE_EVENT : 0);
854
+ cnt--;
855
+ loop_enque_events(&loop->callbacks, sock, flags);
856
+ }
857
+ fd_n++;
858
+ ev_n++;
859
+ }
860
+ callbacks_run(&loop->callbacks);
861
+ }
862
+
863
+ if (next_time) {
864
+ timers_run(&loop->timers, now);
865
+ }
866
+ if (loop->events.count == 0 && loop->timers.count == 0) {
867
+ loop->run = 0;
868
+ }
869
+ return Qnil;
870
+ }
871
+
872
+ static VALUE
873
+ loop_poll_cleanup(VALUE argp)
874
+ {
875
+ lp_arg *args = DATA_PTR(argp);
876
+ if (args) {
877
+ callbacks_clean(&args->loop->callbacks);
878
+ lp_arg_free(args);
879
+ DATA_PTR(argp) = 0;
880
+ }
881
+ return Qnil;
882
+ }
883
+ #endif
884
+ /* loop poll implementation end */
885
+
886
+ static void
887
+ loop_run(rb_mt_loop *loop)
888
+ {
889
+
890
+ loop->run = 1;
891
+
892
+ while(loop->run) {
893
+ #ifdef HAVE_POLL
894
+ /* prefer use of poll when it gives some benefits, but use rb_thread_fd_select when it is sufficient */
895
+ lcb_socket_t max = events_max_fd(&loop->events);
896
+ int use_poll = max >= 128;
897
+ if (use_poll) {
898
+ lp_arg *args;
899
+ VALUE argp = lp_arg_alloc(&args);
900
+ args->loop = loop;
901
+ rb_ensure(loop_run_poll, (VALUE)args, loop_poll_cleanup, argp);
902
+ } else
903
+ #endif
904
+ {
905
+ ls_arg *args;
906
+ VALUE argp = ls_arg_alloc(&args);
907
+ args->loop = loop;
908
+ rb_ensure(loop_run_select, (VALUE)args, loop_select_cleanup, argp);
909
+ }
910
+ }
911
+ }
912
+
913
+ static void *
914
+ lcb_io_create_event(struct lcb_io_opt_st *iops)
915
+ {
916
+ rb_mt_event *event = calloc(1, sizeof(*event));
917
+ (void)iops;
918
+ event->loop_index = -1;
919
+ return event;
920
+ }
921
+
922
+ static int
923
+ lcb_io_update_event(struct lcb_io_opt_st *iops,
924
+ lcb_socket_t sock,
925
+ void *eventp,
926
+ short flags,
927
+ void *cb_data,
928
+ void (*handler)(lcb_socket_t sock,
929
+ short which,
930
+ void *cb_data))
931
+ {
932
+ rb_mt_loop *loop = iops->v.v0.cookie;
933
+ rb_mt_event *event = eventp;
934
+ short old_flags = event->flags;
935
+
936
+ if (event->inserted && old_flags == flags &&
937
+ cb_data == event->cb_data && handler == event->handler)
938
+ {
939
+ return 0;
940
+ }
941
+ loop_remove_event(loop, event);
942
+ event->flags = flags;
943
+ event->cb_data = cb_data;
944
+ event->handler = handler;
945
+ event->socket = sock;
946
+ if (!event->inserted) {
947
+ events_insert(&loop->events, event);
948
+ }
949
+ if ((old_flags & flags) != old_flags) {
950
+ events_fix_flags(&loop->events, sock);
951
+ }
952
+ return 0;
953
+ }
954
+
955
+ static void
956
+ lcb_io_delete_event(struct lcb_io_opt_st *iops,
957
+ lcb_socket_t sock,
958
+ void *event)
959
+ {
960
+ loop_remove_event((rb_mt_loop*)iops->v.v0.cookie, (rb_mt_event*)event);
961
+ (void)sock;
962
+ }
963
+
964
+ static void
965
+ lcb_io_destroy_event(struct lcb_io_opt_st *iops,
966
+ void *event)
967
+ {
968
+ lcb_io_delete_event(iops, -1, event);
969
+ free(event);
970
+ }
971
+
972
+ static void *
973
+ lcb_io_create_timer(struct lcb_io_opt_st *iops)
974
+ {
975
+ rb_mt_timer *timer = calloc(1, sizeof(*timer));
976
+ timer->index = -1;
977
+ (void)iops;
978
+ return timer;
979
+ }
980
+
981
+ static int
982
+ lcb_io_update_timer(struct lcb_io_opt_st *iops, void *event,
983
+ lcb_uint32_t usec, void *cb_data,
984
+ void (*handler)(lcb_socket_t sock, short which, void *cb_data))
985
+ {
986
+ rb_mt_loop *loop = iops->v.v0.cookie;
987
+ rb_mt_timer *timer = event;
988
+
989
+ timer->period = usec * (hrtime_t)1000;
990
+ timer->ts = gethrtime() + timer->period;
991
+ timer->cb_data = cb_data;
992
+ timer->handler = handler;
993
+ if (timer->index != -1) {
994
+ timers_heapify_item(&loop->timers, timer->index);
995
+ } else {
996
+ timers_insert(&loop->timers, timer);
997
+ }
998
+ return 0;
999
+ }
1000
+
1001
+ static void
1002
+ lcb_io_delete_timer(struct lcb_io_opt_st *iops, void *event)
1003
+ {
1004
+ rb_mt_loop *loop = iops->v.v0.cookie;
1005
+ rb_mt_timer *timer = event;
1006
+ if (timer->index != -1) {
1007
+ timers_remove_timer(&loop->timers, timer);
1008
+ }
1009
+ }
1010
+
1011
+ static void
1012
+ lcb_io_destroy_timer(struct lcb_io_opt_st *iops, void *timer)
1013
+ {
1014
+ lcb_io_delete_timer(iops, timer);
1015
+ free(timer);
1016
+ }
1017
+
1018
+ static void
1019
+ lcb_io_stop_event_loop(struct lcb_io_opt_st *iops)
1020
+ {
1021
+ rb_mt_loop *loop = iops->v.v0.cookie;
1022
+ loop->run = 0;
1023
+ }
1024
+
1025
+ static void
1026
+ lcb_io_run_event_loop(struct lcb_io_opt_st *iops)
1027
+ {
1028
+ rb_mt_loop *loop = iops->v.v0.cookie;
1029
+ loop_run(loop);
1030
+ }
1031
+
1032
+ static void
1033
+ lcb_destroy_io_opts(struct lcb_io_opt_st *iops)
1034
+ {
1035
+ rb_mt_loop *loop = iops->v.v0.cookie;
1036
+ loop_destroy(loop);
1037
+ free(iops);
1038
+ }
1039
+
1040
+ LIBCOUCHBASE_API lcb_error_t
1041
+ cb_create_ruby_mt_io_opts(int version, lcb_io_opt_t *io, void *arg)
1042
+ {
1043
+ struct lcb_io_opt_st *ret;
1044
+ rb_mt_loop *loop;
1045
+ (void)arg;
1046
+ if (version != 0) {
1047
+ return LCB_PLUGIN_VERSION_MISMATCH;
1048
+ }
1049
+ ret = calloc(1, sizeof(*ret));
1050
+ if (ret == NULL) {
1051
+ free(ret);
1052
+ return LCB_CLIENT_ENOMEM;
1053
+ }
1054
+
1055
+ ret->version = 0;
1056
+ ret->dlhandle = NULL;
1057
+ ret->destructor = lcb_destroy_io_opts;
1058
+ /* consider that struct isn't allocated by the library,
1059
+ * `need_cleanup' flag might be set in lcb_create() */
1060
+ ret->v.v0.need_cleanup = 0;
1061
+ ret->v.v0.recv = cb_io_recv;
1062
+ ret->v.v0.send = cb_io_send;
1063
+ ret->v.v0.recvv = cb_io_recvv;
1064
+ ret->v.v0.sendv = cb_io_sendv;
1065
+ ret->v.v0.socket = cb_io_socket;
1066
+ ret->v.v0.close = cb_io_close;
1067
+ ret->v.v0.connect = cb_io_connect;
1068
+ ret->v.v0.delete_event = lcb_io_delete_event;
1069
+ ret->v.v0.destroy_event = lcb_io_destroy_event;
1070
+ ret->v.v0.create_event = lcb_io_create_event;
1071
+ ret->v.v0.update_event = lcb_io_update_event;
1072
+
1073
+ ret->v.v0.delete_timer = lcb_io_delete_timer;
1074
+ ret->v.v0.destroy_timer = lcb_io_destroy_timer;
1075
+ ret->v.v0.create_timer = lcb_io_create_timer;
1076
+ ret->v.v0.update_timer = lcb_io_update_timer;
1077
+
1078
+ ret->v.v0.run_event_loop = lcb_io_run_event_loop;
1079
+ ret->v.v0.stop_event_loop = lcb_io_stop_event_loop;
1080
+
1081
+ loop = loop_create();
1082
+ if (loop == NULL) {
1083
+ free(ret);
1084
+ return LCB_CLIENT_ENOMEM;
1085
+ }
1086
+ ret->v.v0.cookie = loop;
1087
+ *io = ret;
1088
+ return LCB_SUCCESS;
1089
+ }
1090
+ #endif /* _WIN32 */