couchbase 1.3.4-x64-mingw32

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.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.travis.yml +22 -0
  4. data/.yardopts +5 -0
  5. data/CONTRIBUTING.markdown +75 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE +201 -0
  8. data/Makefile +3 -0
  9. data/README.markdown +649 -0
  10. data/RELEASE_NOTES.markdown +796 -0
  11. data/Rakefile +20 -0
  12. data/couchbase.gemspec +49 -0
  13. data/examples/chat-em/Gemfile +7 -0
  14. data/examples/chat-em/README.markdown +45 -0
  15. data/examples/chat-em/server.rb +82 -0
  16. data/examples/chat-goliath-grape/Gemfile +5 -0
  17. data/examples/chat-goliath-grape/README.markdown +50 -0
  18. data/examples/chat-goliath-grape/app.rb +67 -0
  19. data/examples/chat-goliath-grape/config/app.rb +20 -0
  20. data/examples/transcoders/Gemfile +3 -0
  21. data/examples/transcoders/README.markdown +59 -0
  22. data/examples/transcoders/cb-zcat +40 -0
  23. data/examples/transcoders/cb-zcp +45 -0
  24. data/examples/transcoders/gzip_transcoder.rb +49 -0
  25. data/examples/transcoders/options.rb +54 -0
  26. data/ext/couchbase_ext/.gitignore +4 -0
  27. data/ext/couchbase_ext/arguments.c +956 -0
  28. data/ext/couchbase_ext/arithmetic.c +307 -0
  29. data/ext/couchbase_ext/bucket.c +1370 -0
  30. data/ext/couchbase_ext/context.c +65 -0
  31. data/ext/couchbase_ext/couchbase_ext.c +1364 -0
  32. data/ext/couchbase_ext/couchbase_ext.h +644 -0
  33. data/ext/couchbase_ext/delete.c +163 -0
  34. data/ext/couchbase_ext/eventmachine_plugin.c +452 -0
  35. data/ext/couchbase_ext/extconf.rb +168 -0
  36. data/ext/couchbase_ext/get.c +316 -0
  37. data/ext/couchbase_ext/gethrtime.c +129 -0
  38. data/ext/couchbase_ext/http.c +432 -0
  39. data/ext/couchbase_ext/multithread_plugin.c +1090 -0
  40. data/ext/couchbase_ext/observe.c +171 -0
  41. data/ext/couchbase_ext/plugin_common.c +171 -0
  42. data/ext/couchbase_ext/result.c +129 -0
  43. data/ext/couchbase_ext/stats.c +163 -0
  44. data/ext/couchbase_ext/store.c +542 -0
  45. data/ext/couchbase_ext/timer.c +192 -0
  46. data/ext/couchbase_ext/touch.c +186 -0
  47. data/ext/couchbase_ext/unlock.c +176 -0
  48. data/ext/couchbase_ext/utils.c +551 -0
  49. data/ext/couchbase_ext/version.c +142 -0
  50. data/lib/action_dispatch/middleware/session/couchbase_store.rb +38 -0
  51. data/lib/active_support/cache/couchbase_store.rb +430 -0
  52. data/lib/couchbase.rb +155 -0
  53. data/lib/couchbase/bucket.rb +457 -0
  54. data/lib/couchbase/cluster.rb +119 -0
  55. data/lib/couchbase/connection_pool.rb +58 -0
  56. data/lib/couchbase/constants.rb +12 -0
  57. data/lib/couchbase/result.rb +26 -0
  58. data/lib/couchbase/transcoder.rb +120 -0
  59. data/lib/couchbase/utils.rb +62 -0
  60. data/lib/couchbase/version.rb +21 -0
  61. data/lib/couchbase/view.rb +506 -0
  62. data/lib/couchbase/view_row.rb +272 -0
  63. data/lib/ext/multi_json_fix.rb +56 -0
  64. data/lib/rack/session/couchbase.rb +108 -0
  65. data/tasks/benchmark.rake +6 -0
  66. data/tasks/compile.rake +158 -0
  67. data/tasks/test.rake +100 -0
  68. data/tasks/util.rake +21 -0
  69. data/test/profile/.gitignore +1 -0
  70. data/test/profile/Gemfile +6 -0
  71. data/test/profile/benchmark.rb +195 -0
  72. data/test/setup.rb +178 -0
  73. data/test/test_arithmetic.rb +185 -0
  74. data/test/test_async.rb +316 -0
  75. data/test/test_bucket.rb +250 -0
  76. data/test/test_cas.rb +235 -0
  77. data/test/test_couchbase.rb +77 -0
  78. data/test/test_couchbase_connection_pool.rb +77 -0
  79. data/test/test_couchbase_rails_cache_store.rb +361 -0
  80. data/test/test_delete.rb +120 -0
  81. data/test/test_errors.rb +82 -0
  82. data/test/test_eventmachine.rb +70 -0
  83. data/test/test_format.rb +164 -0
  84. data/test/test_get.rb +407 -0
  85. data/test/test_stats.rb +57 -0
  86. data/test/test_store.rb +216 -0
  87. data/test/test_timer.rb +42 -0
  88. data/test/test_touch.rb +97 -0
  89. data/test/test_unlock.rb +119 -0
  90. data/test/test_utils.rb +58 -0
  91. data/test/test_version.rb +52 -0
  92. metadata +336 -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 */