couchbase 1.2.0.z.beta5 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -167,6 +167,9 @@ VALUE cb_eClientNoMemoryError; /* LCB_CLIENT_ENOMEM = 0x1a */
167
167
  VALUE cb_eClientTmpFailError; /* LCB_CLIENT_ETMPFAIL = 0x1b */
168
168
  VALUE cb_eBadHandleError; /* LCB_EBADHANDLE = 0x1c */
169
169
 
170
+ /* Default Strings */
171
+ VALUE cb_vStrDefault;
172
+ VALUE cb_vStrEmpty;
170
173
 
171
174
  /* Ruby Extension initializer */
172
175
  void
@@ -1075,4 +1078,11 @@ Init_couchbase_ext(void)
1075
1078
  cb_sym_username = ID2SYM(rb_intern("username"));
1076
1079
  cb_sym_version = ID2SYM(rb_intern("version"));
1077
1080
  cb_sym_view = ID2SYM(rb_intern("view"));
1081
+
1082
+ cb_vStrDefault = STR_NEW_CSTR("default");
1083
+ rb_str_freeze(cb_vStrDefault);
1084
+ rb_const_set(cb_mCouchbase, rb_intern("_STR_DEFAULT"), cb_vStrDefault);
1085
+ cb_vStrEmpty = STR_NEW_CSTR("");
1086
+ rb_str_freeze(cb_vStrEmpty);
1087
+ rb_const_set(cb_mCouchbase, rb_intern("_STR_EMPTY"), cb_vStrEmpty);
1078
1088
  }
@@ -51,6 +51,10 @@ extern hrtime_t gethrtime(void);
51
51
  #define va_init_list(a,b) va_start(a)
52
52
  #endif
53
53
 
54
+ #ifndef HAVE_RB_HASH_LOOKUP2
55
+ VALUE rb_hash_lookup2(VALUE, VALUE, VALUE);
56
+ #endif
57
+
54
58
  #define cb_debug_object(OBJ) \
55
59
  rb_funcall(rb_stderr, rb_intern("print"), 1, rb_funcall(OBJ, rb_intern("object_id"), 0)); \
56
60
  rb_funcall(rb_stderr, rb_intern("print"), 1, STR_NEW_CSTR(" ")); \
@@ -71,12 +75,12 @@ struct cb_bucket_st
71
75
  lcb_type_t type;
72
76
  struct lcb_io_opt_st *io;
73
77
  uint16_t port;
74
- char *authority;
75
- char *hostname;
76
- char *pool;
77
- char *bucket;
78
- char *username;
79
- char *password;
78
+ VALUE authority;
79
+ VALUE hostname;
80
+ VALUE pool;
81
+ VALUE bucket;
82
+ VALUE username;
83
+ VALUE password;
80
84
  int async;
81
85
  int quiet;
82
86
  VALUE default_format; /* should update +default_flags+ on change */
@@ -91,9 +95,8 @@ struct cb_bucket_st
91
95
  VALUE exception; /* error delivered by error_callback */
92
96
  VALUE on_error_proc; /* is using to deliver errors in async mode */
93
97
  VALUE environment; /* sym_development or sym_production */
94
- char *key_prefix;
95
98
  VALUE key_prefix_val;
96
- char *node_list;
99
+ VALUE node_list;
97
100
  VALUE object_space;
98
101
  VALUE self; /* the pointer to bucket representation in ruby land */
99
102
  };
@@ -289,12 +292,16 @@ extern VALUE cb_eClientNoMemoryError; /* LCB_CLIENT_ENOMEM = 0x1a */
289
292
  extern VALUE cb_eClientTmpFailError; /* LCB_CLIENT_ETMPFAIL = 0x1b */
290
293
  extern VALUE cb_eBadHandleError; /* LCB_EBADHANDLE = 0x1c */
291
294
 
295
+ /* Default Strings */
296
+ extern VALUE cb_vStrDefault;
297
+ extern VALUE cb_vStrEmpty;
298
+
292
299
  void cb_strip_key_prefix(struct cb_bucket_st *bucket, VALUE key);
293
300
  VALUE cb_check_error(lcb_error_t rc, const char *msg, VALUE key);
294
301
  VALUE cb_check_error_with_status(lcb_error_t rc, const char *msg, VALUE key, lcb_http_status_t status);
295
302
  VALUE cb_gc_protect(struct cb_bucket_st *bucket, VALUE val);
296
303
  VALUE cb_gc_unprotect(struct cb_bucket_st *bucket, VALUE val);
297
- VALUE cb_proc_call(VALUE recv, int argc, ...);
304
+ VALUE cb_proc_call(struct cb_bucket_st *bucket, VALUE recv, int argc, ...);
298
305
  int cb_first_value_i(VALUE key, VALUE value, VALUE arg);
299
306
  void cb_build_headers(struct cb_context_st *ctx, const char * const *headers);
300
307
  void cb_maybe_do_loop(struct cb_bucket_st *bucket);
@@ -303,6 +310,8 @@ VALUE cb_encode_value(VALUE val, uint32_t flags);
303
310
  VALUE cb_decode_value(VALUE blob, uint32_t flags, VALUE force_format);
304
311
  uint32_t cb_flags_set_format(uint32_t flags, ID format);
305
312
  ID cb_flags_get_format(uint32_t flags);
313
+ void cb_async_error_notify(struct cb_bucket_st *bucket, VALUE exc);
314
+
306
315
 
307
316
  void cb_storage_callback(lcb_t handle, const void *cookie, lcb_storage_t operation, lcb_error_t error, const lcb_store_resp_t *resp);
308
317
  void cb_get_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_get_resp_t *resp);
@@ -531,5 +540,7 @@ struct cb_params_st
531
540
  void cb_params_destroy(struct cb_params_st *params);
532
541
  void cb_params_build(struct cb_params_st *params, int argc, VALUE argv);
533
542
 
543
+ LIBCOUCHBASE_API
544
+ lcb_error_t cb_create_ruby_mt_io_opts(int version, lcb_io_opt_t *io, void *arg);
534
545
  #endif
535
546
 
@@ -32,9 +32,7 @@ cb_delete_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lc
32
32
  exc = cb_check_error(error, "failed to remove value", key);
33
33
  if (exc != Qnil) {
34
34
  rb_ivar_set(exc, cb_id_iv_operation, cb_sym_delete);
35
- if (NIL_P(ctx->exception)) {
36
- ctx->exception = cb_gc_protect(bucket, exc);
37
- }
35
+ ctx->exception = cb_gc_protect(bucket, exc);
38
36
  }
39
37
  }
40
38
  if (bucket->async) { /* asynchronous */
@@ -43,7 +41,7 @@ cb_delete_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lc
43
41
  rb_ivar_set(res, cb_id_iv_error, exc);
44
42
  rb_ivar_set(res, cb_id_iv_operation, cb_sym_delete);
45
43
  rb_ivar_set(res, cb_id_iv_key, key);
46
- cb_proc_call(ctx->proc, 1, res);
44
+ cb_proc_call(bucket, ctx->proc, 1, res);
47
45
  }
48
46
  } else { /* synchronous */
49
47
  rb_hash_aset(*rv, key, (error == LCB_SUCCESS) ? Qtrue : Qfalse);
@@ -51,7 +49,7 @@ cb_delete_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lc
51
49
  if (ctx->nqueries == 0) {
52
50
  cb_gc_unprotect(bucket, ctx->proc);
53
51
  if (bucket->async) {
54
- xfree(ctx);
52
+ free(ctx);
55
53
  }
56
54
  }
57
55
  (void)handle;
@@ -122,7 +120,7 @@ cb_bucket_delete(int argc, VALUE *argv, VALUE self)
122
120
  params.bucket = bucket;
123
121
  cb_params_build(&params, RARRAY_LEN(args), args);
124
122
 
125
- ctx = xcalloc(1, sizeof(struct cb_context_st));
123
+ ctx = calloc(1, sizeof(struct cb_context_st));
126
124
  if (ctx == NULL) {
127
125
  rb_raise(cb_eClientNoMemoryError, "failed to allocate memory for context");
128
126
  }
@@ -138,7 +136,7 @@ cb_bucket_delete(int argc, VALUE *argv, VALUE self)
138
136
  cb_params_destroy(&params);
139
137
  exc = cb_check_error(err, "failed to schedule delete request", Qnil);
140
138
  if (exc != Qnil) {
141
- xfree(ctx);
139
+ free(ctx);
142
140
  rb_exc_raise(exc);
143
141
  }
144
142
  bucket->nbytes += params.npayload;
@@ -151,7 +149,7 @@ cb_bucket_delete(int argc, VALUE *argv, VALUE self)
151
149
  lcb_wait(bucket->handle);
152
150
  }
153
151
  exc = ctx->exception;
154
- xfree(ctx);
152
+ free(ctx);
155
153
  if (exc != Qnil) {
156
154
  rb_exc_raise(cb_gc_unprotect(bucket, exc));
157
155
  }
@@ -130,9 +130,15 @@ have_library("couchbase", "lcb_verify_compiler_setup", "libcouchbase/couchbase.h
130
130
  have_header("mach/mach_time.h")
131
131
  have_header("stdint.h") or die("Failed to locate stdint.h")
132
132
  have_header("sys/time.h")
133
+ have_header("fcntl.h")
133
134
  have_func("clock_gettime")
134
135
  have_func("gettimeofday")
135
136
  have_func("QueryPerformanceCounter")
137
+ have_func("rb_hash_lookup2")
138
+ have_func("rb_thread_fd_select")
139
+ have_func("rb_thread_blocking_region")
140
+ have_func("poll", "poll.h")
141
+ have_func("ppoll", "poll.h")
136
142
  define("_GNU_SOURCE")
137
143
  create_header("couchbase_config.h")
138
144
  create_makefile("couchbase_ext")
@@ -32,9 +32,7 @@ cb_get_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_g
32
32
  exc = cb_check_error(error, "failed to get value", key);
33
33
  if (exc != Qnil) {
34
34
  rb_ivar_set(exc, cb_id_iv_operation, cb_sym_get);
35
- if (NIL_P(ctx->exception)) {
36
- ctx->exception = cb_gc_protect(bucket, exc);
37
- }
35
+ ctx->exception = cb_gc_protect(bucket, exc);
38
36
  }
39
37
  }
40
38
 
@@ -59,7 +57,7 @@ cb_get_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_g
59
57
  val = raw;
60
58
  }
61
59
  } else if (cb_flags_get_format(resp->v.v0.flags) == cb_sym_plain) {
62
- val = STR_NEW_CSTR("");
60
+ val = cb_vStrEmpty;
63
61
  }
64
62
  if (bucket->async) { /* asynchronous */
65
63
  if (ctx->proc != Qnil) {
@@ -70,7 +68,7 @@ cb_get_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_g
70
68
  rb_ivar_set(res, cb_id_iv_value, val);
71
69
  rb_ivar_set(res, cb_id_iv_flags, flags);
72
70
  rb_ivar_set(res, cb_id_iv_cas, cas);
73
- cb_proc_call(ctx->proc, 1, res);
71
+ cb_proc_call(bucket, ctx->proc, 1, res);
74
72
  }
75
73
  } else { /* synchronous */
76
74
  if (NIL_P(exc) && error != LCB_KEY_ENOENT) {
@@ -85,7 +83,7 @@ cb_get_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_g
85
83
  if (ctx->nqueries == 0) {
86
84
  cb_gc_unprotect(bucket, ctx->proc);
87
85
  if (bucket->async) {
88
- xfree(ctx);
86
+ free(ctx);
89
87
  }
90
88
  }
91
89
  (void)handle;
@@ -241,7 +239,7 @@ cb_bucket_get(int argc, VALUE *argv, VALUE self)
241
239
  params.bucket = bucket;
242
240
  params.cmd.get.keys_ary = cb_gc_protect(bucket, rb_ary_new());
243
241
  cb_params_build(&params, RARRAY_LEN(args), args);
244
- ctx = xcalloc(1, sizeof(struct cb_context_st));
242
+ ctx = calloc(1, sizeof(struct cb_context_st));
245
243
  if (ctx == NULL) {
246
244
  rb_raise(cb_eClientNoMemoryError, "failed to allocate memory for context");
247
245
  }
@@ -265,7 +263,7 @@ cb_bucket_get(int argc, VALUE *argv, VALUE self)
265
263
  cb_gc_unprotect(bucket, params.cmd.get.keys_ary);
266
264
  exc = cb_check_error(err, "failed to schedule get request", Qnil);
267
265
  if (exc != Qnil) {
268
- xfree(ctx);
266
+ free(ctx);
269
267
  rb_exc_raise(exc);
270
268
  }
271
269
  bucket->nbytes += params.npayload;
@@ -278,7 +276,7 @@ cb_bucket_get(int argc, VALUE *argv, VALUE self)
278
276
  lcb_wait(bucket->handle);
279
277
  }
280
278
  exc = ctx->exception;
281
- xfree(ctx);
279
+ free(ctx);
282
280
  if (exc != Qnil) {
283
281
  cb_gc_unprotect(bucket, exc);
284
282
  rb_exc_raise(exc);
@@ -22,20 +22,19 @@ cb_http_complete_callback(lcb_http_request_t request, lcb_t handle, const void *
22
22
  {
23
23
  struct cb_context_st *ctx = (struct cb_context_st *)cookie;
24
24
  struct cb_bucket_st *bucket = ctx->bucket;
25
- VALUE *rv = ctx->rv, key, val, res;
25
+ VALUE *rv = ctx->rv, key, val, res, exc;
26
26
  lcb_http_status_t status;
27
27
 
28
28
  ctx->request->completed = 1;
29
29
  key = STR_NEW((const char*)resp->v.v0.path, resp->v.v0.npath);
30
30
  val = resp->v.v0.nbytes ? STR_NEW((const char*)resp->v.v0.bytes, resp->v.v0.nbytes) : Qnil;
31
- ctx->exception = cb_check_error_with_status(error,
32
- "failed to execute HTTP request", key, resp->v.v0.status);
33
- if (ctx->exception != Qnil) {
31
+ exc = cb_check_error_with_status(error, "failed to execute HTTP request", key, resp->v.v0.status);
32
+ if (exc != Qnil) {
34
33
  if (val != Qnil) {
35
- rb_ivar_set(ctx->exception, cb_id_iv_body, val);
34
+ rb_ivar_set(exc, cb_id_iv_body, val);
36
35
  }
37
- rb_funcall(ctx->exception, cb_id_parse_body_bang, 0);
38
- cb_gc_protect(bucket, ctx->exception);
36
+ rb_funcall(exc, cb_id_parse_body_bang, 0);
37
+ ctx->exception = cb_gc_protect(bucket, exc);
39
38
  }
40
39
  status = resp->v.v0.status;
41
40
  if (resp->v.v0.headers) {
@@ -55,7 +54,8 @@ cb_http_complete_callback(lcb_http_request_t request, lcb_t handle, const void *
55
54
  res = val;
56
55
  }
57
56
  if (ctx->proc != Qnil) {
58
- cb_proc_call(ctx->proc, 1, res);
57
+ cb_proc_call(bucket, ctx->proc, 1, res);
58
+ cb_gc_unprotect(bucket, ctx->proc);
59
59
  }
60
60
  if (!bucket->async && ctx->exception == Qnil) {
61
61
  *rv = res;
@@ -107,7 +107,7 @@ cb_http_data_callback(lcb_http_request_t request, lcb_t handle, const void *cook
107
107
  } else {
108
108
  res = val;
109
109
  }
110
- cb_proc_call(ctx->proc, 1, res);
110
+ cb_proc_call(bucket, ctx->proc, 1, res);
111
111
  }
112
112
  (void)handle;
113
113
  }
@@ -123,11 +123,11 @@ cb_http_request_free(void *ptr)
123
123
  && !request->completed) {
124
124
  lcb_cancel_http_request(request->bucket->handle, request->request);
125
125
  }
126
- xfree((char *)request->cmd.v.v0.content_type);
127
- xfree((char *)request->cmd.v.v0.path);
128
- xfree((char *)request->cmd.v.v0.body);
129
- xfree(request);
126
+ free((char *)request->cmd.v.v0.content_type);
127
+ free((char *)request->cmd.v.v0.path);
128
+ free((char *)request->cmd.v.v0.body);
130
129
  }
130
+ xfree(request);
131
131
  }
132
132
 
133
133
  void
@@ -242,7 +242,7 @@ cb_http_request_init(int argc, VALUE *argv, VALUE self)
242
242
  }
243
243
  if ((arg = rb_hash_aref(opts, cb_sym_content_type)) != Qnil) {
244
244
  Check_Type(arg, T_STRING);
245
- xfree((char *)request->cmd.v.v0.content_type);
245
+ free((char *)request->cmd.v.v0.content_type);
246
246
  request->cmd.v.v0.content_type = strdup(RSTRING_PTR(arg));
247
247
  }
248
248
  }
@@ -280,24 +280,25 @@ cb_http_request_perform(VALUE self)
280
280
  lcb_error_t err;
281
281
  struct cb_bucket_st *bucket;
282
282
 
283
- ctx = xcalloc(1, sizeof(struct cb_context_st));
283
+ ctx = calloc(1, sizeof(struct cb_context_st));
284
284
  if (ctx == NULL) {
285
285
  rb_raise(cb_eClientNoMemoryError, "failed to allocate memory");
286
286
  }
287
287
  rv = Qnil;
288
288
  ctx->rv = &rv;
289
289
  ctx->bucket = bucket = req->bucket;
290
- ctx->proc = rb_block_given_p() ? rb_block_proc() : req->on_body_callback;
290
+ ctx->proc = rb_block_given_p() ? cb_gc_protect(bucket, rb_block_proc()) : req->on_body_callback;
291
291
  ctx->extended = req->extended;
292
292
  ctx->request = req;
293
293
  ctx->headers_val = cb_gc_protect(bucket, rb_hash_new());
294
+ ctx->exception = Qnil;
294
295
 
295
296
  err = lcb_make_http_request(bucket->handle, (const void *)ctx,
296
297
  req->type, &req->cmd, &req->request);
297
298
  exc = cb_check_error(err, "failed to schedule document request",
298
299
  STR_NEW(req->cmd.v.v0.path, req->cmd.v.v0.npath));
299
300
  if (exc != Qnil) {
300
- xfree(ctx);
301
+ free(ctx);
301
302
  rb_exc_raise(exc);
302
303
  }
303
304
  req->running = 1;
@@ -308,7 +309,7 @@ cb_http_request_perform(VALUE self)
308
309
  lcb_wait(bucket->handle);
309
310
  if (req->completed) {
310
311
  exc = ctx->exception;
311
- xfree(ctx);
312
+ free(ctx);
312
313
  if (exc != Qnil) {
313
314
  cb_gc_unprotect(bucket, exc);
314
315
  rb_exc_raise(exc);
@@ -340,7 +341,7 @@ cb_http_request_continue(VALUE self)
340
341
  if (req->completed) {
341
342
  exc = req->ctx->exception;
342
343
  rv = req->ctx->rv;
343
- xfree(req->ctx);
344
+ free(req->ctx);
344
345
  if (exc != Qnil) {
345
346
  cb_gc_unprotect(req->bucket, exc);
346
347
  rb_exc_raise(exc);
@@ -0,0 +1,1201 @@
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
+ #include <sys/types.h>
28
+ #include <sys/socket.h>
29
+ #include <unistd.h>
30
+ #ifdef HAVE_FCNTL_H
31
+ #include <fcntl.h>
32
+ #endif
33
+ #ifdef HAVE_POLL
34
+ #include <poll.h>
35
+ #endif
36
+ #define INVALID_SOCKET (-1)
37
+
38
+ /* Copied from libev plugin */
39
+ static lcb_ssize_t
40
+ lcb_io_recv(struct lcb_io_opt_st *iops, lcb_socket_t sock,
41
+ void *buffer, lcb_size_t len, int flags)
42
+ {
43
+ lcb_ssize_t ret = recv(sock, buffer, len, flags);
44
+ if (ret < 0) {
45
+ iops->v.v0.error = errno;
46
+ }
47
+ return ret;
48
+ }
49
+
50
+ static lcb_ssize_t
51
+ lcb_io_recvv(struct lcb_io_opt_st *iops, lcb_socket_t sock,
52
+ struct lcb_iovec_st *iov, lcb_size_t niov)
53
+ {
54
+ struct msghdr msg;
55
+ struct iovec vec[2];
56
+ lcb_ssize_t ret;
57
+
58
+ if (niov != 2) {
59
+ return -1;
60
+ }
61
+ memset(&msg, 0, sizeof(msg));
62
+ msg.msg_iov = vec;
63
+ msg.msg_iovlen = iov[1].iov_len ? (lcb_size_t)2 : (lcb_size_t)1;
64
+ msg.msg_iov[0].iov_base = iov[0].iov_base;
65
+ msg.msg_iov[0].iov_len = iov[0].iov_len;
66
+ msg.msg_iov[1].iov_base = iov[1].iov_base;
67
+ msg.msg_iov[1].iov_len = iov[1].iov_len;
68
+ ret = recvmsg(sock, &msg, 0);
69
+
70
+ if (ret < 0) {
71
+ iops->v.v0.error = errno;
72
+ }
73
+
74
+ return ret;
75
+ }
76
+
77
+ static lcb_ssize_t
78
+ lcb_io_send(struct lcb_io_opt_st *iops, lcb_socket_t sock,
79
+ const void *msg, lcb_size_t len, int flags)
80
+ {
81
+ lcb_ssize_t ret = send(sock, msg, len, flags);
82
+ if (ret < 0) {
83
+ iops->v.v0.error = errno;
84
+ }
85
+ return ret;
86
+ }
87
+
88
+ static lcb_ssize_t
89
+ lcb_io_sendv(struct lcb_io_opt_st *iops, lcb_socket_t sock,
90
+ struct lcb_iovec_st *iov, lcb_size_t niov)
91
+ {
92
+ struct msghdr msg;
93
+ struct iovec vec[2];
94
+ lcb_ssize_t ret;
95
+
96
+ if (niov != 2) {
97
+ return -1;
98
+ }
99
+ memset(&msg, 0, sizeof(msg));
100
+ msg.msg_iov = vec;
101
+ msg.msg_iovlen = iov[1].iov_len ? (lcb_size_t)2 : (lcb_size_t)1;
102
+ msg.msg_iov[0].iov_base = iov[0].iov_base;
103
+ msg.msg_iov[0].iov_len = iov[0].iov_len;
104
+ msg.msg_iov[1].iov_base = iov[1].iov_base;
105
+ msg.msg_iov[1].iov_len = iov[1].iov_len;
106
+ ret = sendmsg(sock, &msg, 0);
107
+
108
+ if (ret < 0) {
109
+ iops->v.v0.error = errno;
110
+ }
111
+ return ret;
112
+ }
113
+
114
+ static int
115
+ make_socket_nonblocking(lcb_socket_t sock)
116
+ {
117
+ int flags;
118
+ if ((flags = fcntl(sock, F_GETFL, NULL)) < 0) {
119
+ return -1;
120
+ }
121
+ if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) {
122
+ return -1;
123
+ }
124
+
125
+ return 0;
126
+ }
127
+
128
+ static int
129
+ close_socket(lcb_socket_t sock)
130
+ {
131
+ return close(sock);
132
+ }
133
+
134
+ static lcb_socket_t
135
+ lcb_io_socket(struct lcb_io_opt_st *iops, int domain, int type,
136
+ int protocol)
137
+ {
138
+ lcb_socket_t sock = socket(domain, type, protocol);
139
+ if (sock == INVALID_SOCKET) {
140
+ iops->v.v0.error = errno;
141
+ } else {
142
+ if (make_socket_nonblocking(sock) != 0) {
143
+ int error = errno;
144
+ iops->v.v0.close(iops, sock);
145
+ iops->v.v0.error = error;
146
+ sock = INVALID_SOCKET;
147
+ }
148
+ }
149
+
150
+ return sock;
151
+ }
152
+
153
+ static void
154
+ lcb_io_close(struct lcb_io_opt_st *iops, lcb_socket_t sock)
155
+ {
156
+ close_socket(sock);
157
+ (void)iops;
158
+ }
159
+
160
+ static int
161
+ lcb_io_connect(struct lcb_io_opt_st *iops, lcb_socket_t sock,
162
+ const struct sockaddr *name, unsigned int namelen)
163
+ {
164
+ int ret = connect(sock, name, (socklen_t)namelen);
165
+ if (ret < 0) {
166
+ iops->v.v0.error = errno;
167
+ }
168
+ return ret;
169
+ }
170
+
171
+ /* events sorted array */
172
+ typedef struct rb_mt_event rb_mt_event;
173
+ struct rb_mt_event {
174
+ void *cb_data;
175
+ void (*handler)(lcb_socket_t sock, short which, void *cb_data);
176
+ lcb_socket_t socket;
177
+ int loop_index;
178
+ short flags;
179
+ short actual_flags;
180
+ short inserted;
181
+ rb_mt_event *next;
182
+ };
183
+
184
+ typedef struct rb_mt_socket_list rb_mt_socket_list;
185
+ struct rb_mt_socket_list {
186
+ lcb_socket_t socket;
187
+ short flags;
188
+ rb_mt_event *first;
189
+ };
190
+
191
+ typedef struct rb_mt_events rb_mt_events;
192
+ struct rb_mt_events {
193
+ uint32_t capa;
194
+ uint32_t count;
195
+ rb_mt_socket_list *sockets;
196
+ };
197
+
198
+ static int
199
+ events_init(rb_mt_events *events)
200
+ {
201
+ rb_mt_socket_list *new_socks = malloc(4 * sizeof(*new_socks));
202
+ if (new_socks == NULL) {
203
+ return 0;
204
+ }
205
+ events->capa = 4;
206
+ events->count = 0;
207
+ events->sockets = new_socks;
208
+ return 1;
209
+ }
210
+
211
+ static void
212
+ events_finalize(rb_mt_events *events)
213
+ {
214
+ if (events->sockets) {
215
+ uint32_t i;
216
+ for(i = 0; i < events->count; i++) {
217
+ rb_mt_socket_list *list = &events->sockets[i];
218
+ while(list->first) {
219
+ rb_mt_event *next = list->first->next;
220
+ free(list->first);
221
+ list->first = next;
222
+ }
223
+ }
224
+ free(events->sockets);
225
+ events->sockets = NULL;
226
+ }
227
+ events->capa = 0;
228
+ events->count = 0;
229
+ }
230
+
231
+ static uint32_t
232
+ events_index(rb_mt_events *events, lcb_socket_t socket)
233
+ {
234
+ uint32_t m, l = 0, r = events->count;
235
+ while(l < r) {
236
+ m = l + (r - l) / 2;
237
+ if (events->sockets[m].socket >= socket) {
238
+ r = m;
239
+ } else {
240
+ l = m + 1;
241
+ }
242
+ }
243
+ return l;
244
+ }
245
+
246
+ static void
247
+ events_insert(rb_mt_events *events, rb_mt_event *event)
248
+ {
249
+ uint32_t i = events_index(events, event->socket);
250
+ rb_mt_socket_list *list = &events->sockets[i];
251
+ if (i == events->count || list->socket != event->socket) {
252
+ if (events->capa == events->count) {
253
+ uint32_t new_capa = events->capa << 1;
254
+ rb_mt_socket_list *new_socks = realloc(events->sockets, new_capa * sizeof(*new_socks));
255
+ if (new_socks == NULL) {
256
+ rb_raise(cb_eClientNoMemoryError, "failed to allocate memory for events array");
257
+ }
258
+ events->sockets = new_socks;
259
+ events->capa = new_capa;
260
+ list = &events->sockets[i];
261
+ }
262
+ if (i < events->count) {
263
+ MEMMOVE(events->sockets+i+1, events->sockets+i, rb_mt_socket_list, events->count - i);
264
+ }
265
+ events->count++;
266
+ list->socket = event->socket;
267
+ list->flags = event->flags;
268
+ list->first = event;
269
+ event->next = NULL;
270
+ } else {
271
+ list->flags |= event->flags;
272
+ event->next = list->first;
273
+ list->first = event;
274
+ }
275
+ event->inserted = 1;
276
+ }
277
+
278
+ static void
279
+ event_list_fix_flags(rb_mt_socket_list *list)
280
+ {
281
+ short flags = 0;
282
+ rb_mt_event *event = list->first;
283
+ while (event) {
284
+ flags |= event->flags;
285
+ event = event->next;
286
+ }
287
+ list->flags = flags;
288
+ }
289
+
290
+ static void
291
+ events_remove(rb_mt_events *events, rb_mt_event *event)
292
+ {
293
+ uint32_t i = events_index(events, event->socket);
294
+ rb_mt_socket_list *list = &events->sockets[i];
295
+ rb_mt_event **next;
296
+ if (list->socket != event->socket) {
297
+ rb_raise(rb_eIndexError, "There is no socket in event loop");
298
+ }
299
+ next = &list->first;
300
+ for(;;) {
301
+ if (*next == NULL) {
302
+ rb_raise(rb_eIndexError, "There is no event in event loop");
303
+ }
304
+ if (*next == event) {
305
+ *next = event->next;
306
+ event->next = NULL;
307
+ event->inserted = 0;
308
+ break;
309
+ }
310
+ next = &event->next;
311
+ }
312
+ if (list->first == NULL) {
313
+ MEMMOVE(events->sockets + i, events->sockets + i + 1, rb_mt_socket_list, events->count - i - 1);
314
+ events->count--;
315
+ } else {
316
+ event_list_fix_flags(list);
317
+ }
318
+ }
319
+
320
+ static void
321
+ events_fix_flags(rb_mt_events *events, lcb_socket_t socket)
322
+ {
323
+ uint32_t i = events_index(events, socket);
324
+ rb_mt_socket_list *list = &events->sockets[i];
325
+ if (list->socket != socket) {
326
+ rb_raise(rb_eIndexError, "There is no socket in event loop");
327
+ }
328
+ event_list_fix_flags(list);
329
+ }
330
+
331
+ static inline lcb_socket_t
332
+ events_max_fd(rb_mt_events *events)
333
+ {
334
+ if (events->count) {
335
+ return events->sockets[events->count - 1].socket;
336
+ } else {
337
+ return -1;
338
+ }
339
+ }
340
+
341
+ /* events sorted array end */
342
+
343
+ /* timers heap */
344
+ typedef struct rb_mt_timer rb_mt_timer;
345
+ struct rb_mt_timer {
346
+ void *cb_data;
347
+ void (*handler)(lcb_socket_t sock, short which, void *cb_data);
348
+ int index;
349
+ hrtime_t ts;
350
+ hrtime_t period;
351
+ };
352
+
353
+ typedef struct rb_mt_timers rb_mt_timers;
354
+ struct rb_mt_timers {
355
+ uint32_t capa;
356
+ uint32_t count;
357
+ rb_mt_timer **timers;
358
+ };
359
+
360
+ static int
361
+ timers_init(rb_mt_timers *timers)
362
+ {
363
+ rb_mt_timer **new_timers = malloc(4 * sizeof(*new_timers));
364
+ if (new_timers == NULL) {
365
+ return 0;
366
+ }
367
+ timers->capa = 4;
368
+ timers->count = 0;
369
+ timers->timers = new_timers;
370
+ return 1;
371
+ }
372
+
373
+ static void
374
+ timers_finalize(rb_mt_timers *timers)
375
+ {
376
+ if (timers->timers) {
377
+ uint32_t i;
378
+ for(i = 0; i < timers->count; i++) {
379
+ free(timers->timers[i]);
380
+ }
381
+ free(timers->timers);
382
+ timers->timers = NULL;
383
+ }
384
+ timers->count = 0;
385
+ timers->capa = 0;
386
+ }
387
+
388
+ #define tms_at(_timers, at) (_timers)->timers[(at)]
389
+ #define tms_ts_at(timers, at) tms_at((timers), (at))->ts
390
+
391
+ static void
392
+ timers_move_last(rb_mt_timers *timers, uint32_t to)
393
+ {
394
+ if (to < timers->count - 1) {
395
+ rb_mt_timer *last = tms_at(timers, timers->count - 1);
396
+ tms_at(timers, to) = last;
397
+ last->index = to;
398
+ }
399
+ timers->count--;
400
+ }
401
+
402
+ static inline void
403
+ timers_swap(rb_mt_timers *timers, uint32_t i, uint32_t j)
404
+ {
405
+ rb_mt_timer *itmp = tms_at(timers, j);
406
+ rb_mt_timer *jtmp = tms_at(timers, i);
407
+ tms_at(timers, i) = itmp;
408
+ tms_at(timers, j) = jtmp;
409
+ itmp->index = i;
410
+ jtmp->index = j;
411
+ }
412
+
413
+ static void timers_heapify_up(rb_mt_timers *timers, uint32_t pos);
414
+
415
+ static void
416
+ timers_insert(rb_mt_timers *timers, rb_mt_timer *timer)
417
+ {
418
+ if (timers->count == timers->capa) {
419
+ rb_mt_timer **new_timers;
420
+ size_t new_capa = timers->capa << 1;
421
+ new_timers = realloc(timers->timers, new_capa * sizeof(rb_mt_timer*));
422
+ if (new_timers == NULL) {
423
+ rb_raise(cb_eClientNoMemoryError, "failed to allocate memory for timers heap");
424
+ }
425
+ timers->timers = new_timers;
426
+ timers->capa = new_capa;
427
+ }
428
+ tms_at(timers, timers->count) = timer;
429
+ timer->index = timers->count;
430
+ timers->count++;
431
+ timers_heapify_up(timers, timer->index);
432
+ }
433
+
434
+ static void
435
+ timers_heapify_up(rb_mt_timers *timers, uint32_t pos)
436
+ {
437
+ hrtime_t cur_ts = tms_ts_at(timers, pos);
438
+ uint32_t higher = (pos - 1) / 2;
439
+ while (pos && tms_ts_at(timers, higher) > cur_ts) {
440
+ timers_swap(timers, higher, pos);
441
+ pos = higher;
442
+ higher = (pos - 1) / 2;
443
+ }
444
+ }
445
+
446
+ static void
447
+ timers_heapify_down(rb_mt_timers *timers, uint32_t pos)
448
+ {
449
+ uint32_t count = timers->count;
450
+ uint32_t middle = (timers->count - 2) / 2;
451
+ hrtime_t cur_ts = tms_ts_at(timers, pos);
452
+ if (count == 1) return;
453
+ while (pos <= middle) {
454
+ uint32_t min_pos = pos;
455
+ hrtime_t ch_ts, min_ts = cur_ts;
456
+
457
+ if ((ch_ts = tms_ts_at(timers, pos * 2 + 1)) < min_ts) {
458
+ min_pos = pos * 2 + 1;
459
+ min_ts = ch_ts;
460
+ }
461
+
462
+ if (pos * 2 + 2 < count && tms_ts_at(timers, pos * 2 + 2) < min_ts) {
463
+ min_pos = pos * 2 + 2;
464
+ }
465
+
466
+ if (min_pos == pos) break;
467
+ timers_swap(timers, pos, min_pos);
468
+ pos = min_pos;
469
+ }
470
+ }
471
+
472
+ static void
473
+ timers_heapify_item(rb_mt_timers *timers, uint32_t pos)
474
+ {
475
+ if (pos && tms_ts_at(timers, pos) < tms_ts_at(timers, (pos - 1) / 2)) {
476
+ timers_heapify_up(timers, pos);
477
+ } else {
478
+ timers_heapify_down(timers, pos);
479
+ }
480
+ }
481
+
482
+ static inline hrtime_t
483
+ timers_minimum(rb_mt_timers *timers)
484
+ {
485
+ if (timers->count) {
486
+ return tms_ts_at(timers, 0);
487
+ } else {
488
+ return 0;
489
+ }
490
+ }
491
+
492
+ static inline rb_mt_timer *
493
+ timers_first(rb_mt_timers *timers)
494
+ {
495
+ if (timers->count) {
496
+ return tms_at(timers, 0);
497
+ } else {
498
+ return 0;
499
+ }
500
+ }
501
+
502
+ static void
503
+ timers_remove_timer(rb_mt_timers *timers, rb_mt_timer *timer)
504
+ {
505
+ uint32_t at = timer->index;
506
+ timer->index = -1;
507
+ if (at < timers->count - 1) {
508
+ timers_move_last(timers, at);
509
+ timers_heapify_item(timers, at);
510
+ } else {
511
+ timers->count--;
512
+ }
513
+ }
514
+
515
+ static void
516
+ timers_run(rb_mt_timers *timers, hrtime_t now)
517
+ {
518
+ hrtime_t next_time = timers_minimum(timers);
519
+ while (next_time && next_time < now) {
520
+ rb_mt_timer *first = timers_first(timers);
521
+
522
+ first->ts = now + first->period;
523
+ timers_heapify_item(timers, 0);
524
+
525
+ first->handler(-1, 0, first->cb_data);
526
+
527
+ next_time = timers_minimum(timers);
528
+ }
529
+ }
530
+ /* timers heap end */
531
+
532
+ /* callbacks array */
533
+ typedef struct rb_mt_callbacks rb_mt_callbacks;
534
+ struct rb_mt_callbacks {
535
+ uint32_t capa;
536
+ uint32_t count;
537
+ rb_mt_event **events;
538
+ };
539
+
540
+ static int
541
+ callbacks_init(rb_mt_callbacks *callbacks)
542
+ {
543
+ rb_mt_event **new_events = calloc(4, sizeof(*new_events));
544
+ if (new_events == NULL) {
545
+ return 0;
546
+ }
547
+ callbacks->events = new_events;
548
+ callbacks->capa = 4;
549
+ callbacks->count = 0;
550
+ return 1;
551
+ }
552
+
553
+ static void
554
+ callbacks_finalize(rb_mt_callbacks *callbacks)
555
+ {
556
+ if (callbacks->events) {
557
+ free(callbacks->events);
558
+ callbacks->events = NULL;
559
+ }
560
+ callbacks->capa = 0;
561
+ callbacks->count = 0;
562
+ }
563
+
564
+ static void
565
+ callbacks_push(rb_mt_callbacks *callbacks, rb_mt_event *event)
566
+ {
567
+ if (callbacks->count == callbacks->capa) {
568
+ uint32_t new_capa = callbacks->capa << 1;
569
+ rb_mt_event **new_events = realloc(callbacks->events, new_capa * sizeof(*new_events));
570
+ if (new_events == NULL) {
571
+ rb_raise(cb_eClientNoMemoryError, "failed to allocate memory for callbacks array");
572
+ }
573
+ callbacks->capa = new_capa;
574
+ callbacks->events = new_events;
575
+ }
576
+ callbacks->events[callbacks->count] = event;
577
+ callbacks->count++;
578
+ }
579
+
580
+ static void
581
+ callbacks_remove(rb_mt_callbacks *callbacks, rb_mt_event *event)
582
+ {
583
+ int i = event->loop_index;
584
+ if (i >= 0) {
585
+ if (callbacks->events[i] != event) {
586
+ rb_raise(rb_eIndexError, "callback index belongs to different callback");
587
+ }
588
+ event->loop_index = -1;
589
+ callbacks->events[i] = NULL;
590
+ }
591
+ }
592
+
593
+ static void
594
+ callbacks_run(rb_mt_callbacks *callbacks)
595
+ {
596
+ uint32_t i;
597
+ for(i = 0; i < callbacks->count; i++) {
598
+ rb_mt_event *cb = callbacks->events[i];
599
+ if (cb) {
600
+ cb->handler(cb->socket, cb->actual_flags, cb->cb_data);
601
+ }
602
+ }
603
+ callbacks->count = 0;
604
+ }
605
+
606
+ static void
607
+ callbacks_clean(rb_mt_callbacks *callbacks)
608
+ {
609
+ uint32_t i;
610
+ for(i = 0; i < callbacks->count; i++) {
611
+ if (callbacks->events[i]) {
612
+ callbacks->events[i]->loop_index = -1;
613
+ callbacks->events[i] = NULL;
614
+ }
615
+ }
616
+ callbacks->count = 0;
617
+ }
618
+ /* callbacks array end */
619
+
620
+ typedef struct rb_mt_loop rb_mt_loop;
621
+ struct rb_mt_loop {
622
+ rb_mt_events events;
623
+ rb_mt_timers timers;
624
+ rb_mt_callbacks callbacks;
625
+ short run;
626
+ };
627
+
628
+ static rb_mt_loop*
629
+ loop_create()
630
+ {
631
+ rb_mt_loop *loop = calloc(1, sizeof(*loop));
632
+ if (loop == NULL) return NULL;
633
+ if (!events_init(&loop->events)) goto free_loop;
634
+ if (!timers_init(&loop->timers)) goto free_events;
635
+ if (!callbacks_init(&loop->callbacks)) goto free_timers;
636
+ return loop;
637
+
638
+ free_timers:
639
+ timers_finalize(&loop->timers);
640
+ free_events:
641
+ events_finalize(&loop->events);
642
+ free_loop:
643
+ free(loop);
644
+ return NULL;
645
+ }
646
+
647
+ static void
648
+ loop_destroy(rb_mt_loop *loop)
649
+ {
650
+ events_finalize(&loop->events);
651
+ timers_finalize(&loop->timers);
652
+ callbacks_finalize(&loop->callbacks);
653
+ free(loop);
654
+ }
655
+
656
+ static void
657
+ loop_remove_event(rb_mt_loop *loop, rb_mt_event *event)
658
+ {
659
+ if (event->inserted) {
660
+ events_remove(&loop->events, event);
661
+ }
662
+ callbacks_remove(&loop->callbacks, event);
663
+ }
664
+
665
+ static void
666
+ loop_enque_events(rb_mt_callbacks *callbacks, rb_mt_event *sock, short flags)
667
+ {
668
+ while (sock) {
669
+ short actual = sock->flags & flags;
670
+ if (actual) {
671
+ sock->actual_flags = actual;
672
+ callbacks_push(callbacks, (rb_mt_event*)sock);
673
+ }
674
+ sock = sock->next;
675
+ }
676
+ }
677
+
678
+ /* loop select implementation */
679
+ #ifndef HAVE_RB_THREAD_FD_SELECT
680
+ typedef fd_set rb_fdset_t;
681
+ #define rb_fd_init FD_ZERO
682
+ #define rb_fd_set FD_SET
683
+ #define rb_fd_isset FD_ISSET
684
+ #define rb_fd_term(set) (void)0
685
+ #define rb_thread_fd_select rb_thread_select
686
+ #endif
687
+
688
+ typedef struct loop_select_arg {
689
+ rb_mt_loop *loop;
690
+ rb_fdset_t in, out;
691
+ } ls_arg;
692
+
693
+ static VALUE
694
+ loop_run_select(VALUE argp)
695
+ {
696
+ ls_arg *args = (ls_arg*) argp;
697
+ rb_mt_loop *loop = args->loop;
698
+ rb_fdset_t *in = NULL, *out = NULL;
699
+ struct timeval timeout;
700
+ struct timeval *timeoutp = NULL;
701
+ int result, max = 0;
702
+ hrtime_t now, next_time;
703
+
704
+ next_time = timers_minimum(&loop->timers);
705
+ if (next_time) {
706
+ now = gethrtime();
707
+ if (next_time <= now) {
708
+ timeout.tv_sec = 0;
709
+ timeout.tv_usec = 0;
710
+ } else {
711
+ hrtime_t hrto = (next_time - now) / 1000;
712
+ timeout.tv_sec = (long)(hrto / 1000000);
713
+ timeout.tv_usec = (long)(hrto % 1000000);
714
+ }
715
+ timeoutp = &timeout;
716
+ }
717
+
718
+ if (loop->events.count) {
719
+ uint32_t i;
720
+ rb_fd_init(&args->in);
721
+ rb_fd_init(&args->out);
722
+ for(i = 0; i < loop->events.count; i++) {
723
+ rb_mt_socket_list *list = &loop->events.sockets[i];
724
+ if (list->flags & LCB_READ_EVENT) {
725
+ in = &args->in;
726
+ rb_fd_set(list->socket, in);
727
+ }
728
+ if (list->flags & LCB_WRITE_EVENT) {
729
+ out = &args->out;
730
+ rb_fd_set(list->socket, out);
731
+ }
732
+ }
733
+ max = events_max_fd(&loop->events) + 1;
734
+ }
735
+
736
+ result = rb_thread_fd_select(max, in, out, NULL, timeoutp);
737
+
738
+ if (result < 0) {
739
+ rb_sys_fail("rb_thread_fd_select");
740
+ }
741
+ /* fix current time so that socket callbacks will not cause timers timeouts */
742
+ if (next_time) {
743
+ now = gethrtime();
744
+ }
745
+
746
+ if (result > 0) {
747
+ uint32_t i;
748
+ for(i = 0; i < loop->events.count && result; i++) {
749
+ rb_mt_socket_list *list = loop->events.sockets + i;
750
+ rb_mt_event *sock = list->first;
751
+ short flags = 0;
752
+ if (in && rb_fd_isset(list->socket, in)) {
753
+ flags |= LCB_READ_EVENT;
754
+ result--;
755
+ }
756
+ if (out && rb_fd_isset(list->socket, out)) {
757
+ flags |= LCB_WRITE_EVENT;
758
+ result--;
759
+ }
760
+ if (flags) {
761
+ loop_enque_events(&loop->callbacks, sock, flags);
762
+ }
763
+ }
764
+ callbacks_run(&loop->callbacks);
765
+ }
766
+
767
+ if (next_time) {
768
+ timers_run(&loop->timers, now);
769
+ }
770
+ if (loop->events.count == 0 && loop->timers.count == 0) {
771
+ loop->run = 0;
772
+ }
773
+ return Qnil;
774
+ }
775
+
776
+ static VALUE
777
+ loop_select_cleanup(VALUE argp)
778
+ {
779
+ ls_arg *args = (ls_arg*) argp;
780
+ rb_fd_term(&args->in);
781
+ rb_fd_term(&args->out);
782
+ callbacks_clean(&args->loop->callbacks);
783
+ return Qnil;
784
+ }
785
+ /* loop select implementaion end */
786
+
787
+ /* loop poll implementation */
788
+ #ifdef HAVE_POLL
789
+ /* code influenced by ruby's source and cool.io */
790
+ #define POLLIN_SET (POLLIN | POLLHUP | POLLERR)
791
+ #define POLLOUT_SET (POLLOUT | POLLHUP | POLLERR)
792
+
793
+ #ifndef HAVE_PPOLL
794
+ #if SIZEOF_TIME_T == SIZEOF_LONG
795
+ typedef unsigned long unsigned_time_t;
796
+ #elif SIZEOF_TIME_T == SIZEOF_INT
797
+ typedef unsigned int unsigned_time_t;
798
+ #elif SIZEOF_TIME_T == SIZEOF_LONG_LONG
799
+ typedef unsigned LONG_LONG unsigned_time_t;
800
+ #else
801
+ # error cannot find integer type which size is same as time_t.
802
+ #endif
803
+ #define TIMET_MAX (~(time_t)0 <= 0 ? (time_t)((~(unsigned_time_t)0) >> 1) : (time_t)(~(unsigned_time_t)0))
804
+ static int
805
+ ppoll(struct pollfd *fds, nfds_t nfds,
806
+ const struct timespec *ts, const sigset_t *sigmask)
807
+ {
808
+ int timeout_ms;
809
+
810
+ if (ts) {
811
+ int tmp, tmp2;
812
+
813
+ if (ts->tv_sec > TIMET_MAX/1000) {
814
+ timeout_ms = -1;
815
+ } else {
816
+ tmp = ts->tv_sec * 1000;
817
+ tmp2 = (ts->tv_nsec + 999999) / (1000 * 1000);
818
+ if (TIMET_MAX - tmp < tmp2) {
819
+ timeout_ms = -1;
820
+ } else {
821
+ timeout_ms = tmp + tmp2;
822
+ }
823
+ }
824
+ } else {
825
+ timeout_ms = -1;
826
+ }
827
+
828
+ (void)sigmask;
829
+
830
+ return poll(fds, nfds, timeout_ms);
831
+ }
832
+ #endif
833
+
834
+ typedef struct poll_args lp_arg;
835
+ struct poll_args {
836
+ rb_mt_loop *loop;
837
+ struct pollfd *fds;
838
+ nfds_t nfd;
839
+ struct timespec *ts;
840
+ int result;
841
+ int lerrno;
842
+ };
843
+
844
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
845
+ static VALUE
846
+ loop_blocking_poll(void *argp)
847
+ {
848
+ lp_arg *args = argp;
849
+ args->result = ppoll(args->fds, args->nfd, args->ts, NULL);
850
+ if (args->result < 0) args->lerrno = errno;
851
+ return Qnil;
852
+ }
853
+ #endif
854
+
855
+ static VALUE
856
+ loop_run_poll(VALUE argp)
857
+ {
858
+ lp_arg *args = (struct poll_args *)argp;
859
+ rb_mt_loop *loop = args->loop;
860
+ struct timespec ts;
861
+ hrtime_t now, next_time;
862
+
863
+ if (loop->events.count) {
864
+ uint32_t i;
865
+ args->fds = calloc(loop->events.count, sizeof(struct pollfd));
866
+ if (args->fds == NULL) {
867
+ rb_raise(cb_eClientNoMemoryError, "failed to allocate memory for pollfd");
868
+ }
869
+ for(i = 0; i < loop->events.count; i++) {
870
+ rb_mt_socket_list *list = &loop->events.sockets[i];
871
+ args->fds[i].fd = list->socket;
872
+ args->fds[i].events =
873
+ (list->flags & LCB_READ_EVENT ? POLLIN : 0) |
874
+ (list->flags & LCB_WRITE_EVENT ? POLLOUT : 0);
875
+ }
876
+ args->nfd = loop->events.count;
877
+ }
878
+
879
+ retry:
880
+ next_time = timers_minimum(&loop->timers);
881
+ if (next_time) {
882
+ now = gethrtime();
883
+ if (next_time <= now) {
884
+ ts.tv_sec = 0;
885
+ ts.tv_nsec = 0;
886
+ } else {
887
+ hrtime_t hrto = next_time - now;
888
+ ts.tv_sec = (long)(hrto / 1000000000);
889
+ ts.tv_nsec = (long)(hrto % 1000000000);
890
+ }
891
+ args->ts = &ts;
892
+ } else {
893
+ args->ts = NULL;
894
+ }
895
+
896
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
897
+ rb_thread_blocking_region(loop_blocking_poll, args, RUBY_UBF_PROCESS, NULL);
898
+ #else
899
+ if (rb_thread_alone()) {
900
+ TRAP_BEG;
901
+ args->result = ppoll(args->fds, args->nfd, args->ts, NULL);
902
+ if (args->result < 0) args->lerrno = errno;
903
+ TRAP_END;
904
+ } else {
905
+ struct timespec mini_pause;
906
+ int exact = 0;
907
+ mini_pause.tv_sec = 0;
908
+ /* 5 millisecond pause */
909
+ mini_pause.tv_nsec = 5000000;
910
+ if (args->ts && ts.tv_sec == 0 && ts.tv_nsec < 5000000) {
911
+ mini_pause.tv_nsec = ts.tv_nsec;
912
+ exact = 1;
913
+ }
914
+ TRAP_BEG;
915
+ args->result = ppoll(args->fds, args->nfd, &mini_pause, NULL);
916
+ if (args->result < 0) args->lerrno = errno;
917
+ TRAP_END;
918
+ if (args->result == 0 && !exact) {
919
+ args->result = -1;
920
+ args->lerrno = EINTR;
921
+ }
922
+ }
923
+ #endif
924
+
925
+ if (args->result < 0) {
926
+ errno = args->lerrno;
927
+ switch (errno) {
928
+ case EINTR:
929
+ #ifdef ERESTART
930
+ case ERESTART:
931
+ #endif
932
+ #ifndef HAVE_RB_THREAD_BLOCKING_REGION
933
+ rb_thread_schedule();
934
+ #endif
935
+ goto retry;
936
+ }
937
+ rb_sys_fail("poll");
938
+ return Qnil;
939
+ }
940
+
941
+ if (next_time) {
942
+ now = gethrtime();
943
+ }
944
+
945
+ if (args->result > 0) {
946
+ uint32_t cnt = args->result;
947
+ uint32_t fd_n = 0, ev_n = 0;
948
+ while (cnt && fd_n < args->nfd && ev_n < loop->events.count) {
949
+ struct pollfd *res = args->fds + fd_n;
950
+ rb_mt_socket_list *list = loop->events.sockets + ev_n;
951
+ rb_mt_event *sock = list->first;
952
+
953
+ /* if plugin used correctly, this checks are noop */
954
+ if (res->fd < list->socket) {
955
+ fd_n++;
956
+ continue;
957
+ } else if (res->fd > list->socket) {
958
+ ev_n++;
959
+ continue;
960
+ }
961
+
962
+ if (res->revents) {
963
+ short flags =
964
+ ((res->revents & POLLIN_SET) ? LCB_READ_EVENT : 0) |
965
+ ((res->revents & POLLOUT_SET) ? LCB_WRITE_EVENT : 0);
966
+ cnt--;
967
+ loop_enque_events(&loop->callbacks, sock, flags);
968
+ }
969
+ fd_n++;
970
+ ev_n++;
971
+ }
972
+ callbacks_run(&loop->callbacks);
973
+ }
974
+
975
+ if (next_time) {
976
+ timers_run(&loop->timers, now);
977
+ }
978
+ if (loop->events.count == 0 && loop->timers.count == 0) {
979
+ loop->run = 0;
980
+ }
981
+ return Qnil;
982
+ }
983
+
984
+ static VALUE
985
+ loop_poll_cleanup(VALUE argp)
986
+ {
987
+ lp_arg *args = (struct poll_args *)argp;
988
+ if (args->fds) {
989
+ free(args->fds);
990
+ }
991
+ callbacks_clean(&args->loop->callbacks);
992
+ return Qnil;
993
+ }
994
+ #endif
995
+ /* loop poll implementation end */
996
+
997
+ static void
998
+ loop_run(rb_mt_loop *loop)
999
+ {
1000
+
1001
+ loop->run = 1;
1002
+
1003
+ while(loop->run) {
1004
+ #ifdef HAVE_POLL
1005
+ /* prefer use of poll when it gives some benefits, but use rb_thread_fd_select when it is sufficient */
1006
+ lcb_socket_t max = events_max_fd(&loop->events);
1007
+ int use_poll = max >= 128;
1008
+ if (use_poll) {
1009
+ lp_arg args;
1010
+ memset(&args, 0, sizeof(args));
1011
+ args.loop = loop;
1012
+ rb_ensure(loop_run_poll, (VALUE)&args, loop_poll_cleanup, (VALUE)&args);
1013
+ } else
1014
+ #endif
1015
+ {
1016
+ ls_arg args;
1017
+ memset(&args, 0, sizeof(args));
1018
+ args.loop = loop;
1019
+ rb_ensure(loop_run_select, (VALUE)&args, loop_select_cleanup, (VALUE)&args);
1020
+ }
1021
+ }
1022
+ }
1023
+
1024
+ static void *
1025
+ lcb_io_create_event(struct lcb_io_opt_st *iops)
1026
+ {
1027
+ rb_mt_event *event = calloc(1, sizeof(*event));
1028
+ (void)iops;
1029
+ event->loop_index = -1;
1030
+ return event;
1031
+ }
1032
+
1033
+ static int
1034
+ lcb_io_update_event(struct lcb_io_opt_st *iops,
1035
+ lcb_socket_t sock,
1036
+ void *eventp,
1037
+ short flags,
1038
+ void *cb_data,
1039
+ void (*handler)(lcb_socket_t sock,
1040
+ short which,
1041
+ void *cb_data))
1042
+ {
1043
+ rb_mt_loop *loop = iops->v.v0.cookie;
1044
+ rb_mt_event *event = eventp;
1045
+ short old_flags = event->flags;
1046
+
1047
+ if (event->inserted && old_flags == flags &&
1048
+ cb_data == event->cb_data && handler == event->handler)
1049
+ {
1050
+ return 0;
1051
+ }
1052
+ loop_remove_event(loop, event);
1053
+ event->flags = flags;
1054
+ event->cb_data = cb_data;
1055
+ event->handler = handler;
1056
+ event->socket = sock;
1057
+ if (!event->inserted) {
1058
+ events_insert(&loop->events, event);
1059
+ }
1060
+ if ((old_flags & flags) != old_flags) {
1061
+ events_fix_flags(&loop->events, sock);
1062
+ }
1063
+ return 0;
1064
+ }
1065
+
1066
+ static void
1067
+ lcb_io_delete_event(struct lcb_io_opt_st *iops,
1068
+ lcb_socket_t sock,
1069
+ void *event)
1070
+ {
1071
+ loop_remove_event((rb_mt_loop*)iops->v.v0.cookie, (rb_mt_event*)event);
1072
+ (void)sock;
1073
+ }
1074
+
1075
+ static void
1076
+ lcb_io_destroy_event(struct lcb_io_opt_st *iops,
1077
+ void *event)
1078
+ {
1079
+ lcb_io_delete_event(iops, -1, event);
1080
+ free(event);
1081
+ }
1082
+
1083
+ static void *
1084
+ lcb_io_create_timer(struct lcb_io_opt_st *iops)
1085
+ {
1086
+ rb_mt_timer *timer = calloc(1, sizeof(*timer));
1087
+ timer->index = -1;
1088
+ (void)iops;
1089
+ return timer;
1090
+ }
1091
+
1092
+ static int
1093
+ lcb_io_update_timer(struct lcb_io_opt_st *iops, void *event,
1094
+ lcb_uint32_t usec, void *cb_data,
1095
+ void (*handler)(lcb_socket_t sock, short which, void *cb_data))
1096
+ {
1097
+ rb_mt_loop *loop = iops->v.v0.cookie;
1098
+ rb_mt_timer *timer = event;
1099
+
1100
+ timer->period = usec * (hrtime_t)1000;
1101
+ timer->ts = gethrtime() + timer->period;
1102
+ timer->cb_data = cb_data;
1103
+ timer->handler = handler;
1104
+ if (timer->index != -1) {
1105
+ timers_heapify_item(&loop->timers, timer->index);
1106
+ } else {
1107
+ timers_insert(&loop->timers, timer);
1108
+ }
1109
+ return 0;
1110
+ }
1111
+
1112
+ static void
1113
+ lcb_io_delete_timer(struct lcb_io_opt_st *iops, void *event)
1114
+ {
1115
+ rb_mt_loop *loop = iops->v.v0.cookie;
1116
+ rb_mt_timer *timer = event;
1117
+ if (timer->index != -1) {
1118
+ timers_remove_timer(&loop->timers, timer);
1119
+ }
1120
+ }
1121
+
1122
+ static void
1123
+ lcb_io_destroy_timer(struct lcb_io_opt_st *iops, void *timer)
1124
+ {
1125
+ lcb_io_delete_timer(iops, timer);
1126
+ free(timer);
1127
+ }
1128
+
1129
+ static void
1130
+ lcb_io_stop_event_loop(struct lcb_io_opt_st *iops)
1131
+ {
1132
+ rb_mt_loop *loop = iops->v.v0.cookie;
1133
+ loop->run = 0;
1134
+ }
1135
+
1136
+ static void
1137
+ lcb_io_run_event_loop(struct lcb_io_opt_st *iops)
1138
+ {
1139
+ rb_mt_loop *loop = iops->v.v0.cookie;
1140
+ loop_run(loop);
1141
+ }
1142
+
1143
+ static void
1144
+ lcb_destroy_io_opts(struct lcb_io_opt_st *iops)
1145
+ {
1146
+ rb_mt_loop *loop = iops->v.v0.cookie;
1147
+ loop_destroy(loop);
1148
+ free(iops);
1149
+ }
1150
+
1151
+ LIBCOUCHBASE_API lcb_error_t
1152
+ cb_create_ruby_mt_io_opts(int version, lcb_io_opt_t *io, void *arg)
1153
+ {
1154
+ struct lcb_io_opt_st *ret;
1155
+ rb_mt_loop *loop;
1156
+ (void)arg;
1157
+ if (version != 0) {
1158
+ return LCB_PLUGIN_VERSION_MISMATCH;
1159
+ }
1160
+ ret = calloc(1, sizeof(*ret));
1161
+ if (ret == NULL) {
1162
+ free(ret);
1163
+ return LCB_CLIENT_ENOMEM;
1164
+ }
1165
+
1166
+ ret->version = 0;
1167
+ ret->dlhandle = NULL;
1168
+ ret->destructor = lcb_destroy_io_opts;
1169
+ /* consider that struct isn't allocated by the library,
1170
+ * `need_cleanup' flag might be set in lcb_create() */
1171
+ ret->v.v0.need_cleanup = 0;
1172
+ ret->v.v0.recv = lcb_io_recv;
1173
+ ret->v.v0.send = lcb_io_send;
1174
+ ret->v.v0.recvv = lcb_io_recvv;
1175
+ ret->v.v0.sendv = lcb_io_sendv;
1176
+ ret->v.v0.socket = lcb_io_socket;
1177
+ ret->v.v0.close = lcb_io_close;
1178
+ ret->v.v0.connect = lcb_io_connect;
1179
+ ret->v.v0.delete_event = lcb_io_delete_event;
1180
+ ret->v.v0.destroy_event = lcb_io_destroy_event;
1181
+ ret->v.v0.create_event = lcb_io_create_event;
1182
+ ret->v.v0.update_event = lcb_io_update_event;
1183
+
1184
+ ret->v.v0.delete_timer = lcb_io_delete_timer;
1185
+ ret->v.v0.destroy_timer = lcb_io_destroy_timer;
1186
+ ret->v.v0.create_timer = lcb_io_create_timer;
1187
+ ret->v.v0.update_timer = lcb_io_update_timer;
1188
+
1189
+ ret->v.v0.run_event_loop = lcb_io_run_event_loop;
1190
+ ret->v.v0.stop_event_loop = lcb_io_stop_event_loop;
1191
+
1192
+ loop = loop_create();
1193
+ if (loop == NULL) {
1194
+ free(ret);
1195
+ return LCB_CLIENT_ENOMEM;
1196
+ }
1197
+ ret->v.v0.cookie = loop;
1198
+ *io = ret;
1199
+ return LCB_SUCCESS;
1200
+ }
1201
+ #endif /* _WIN32 */