threadsafe-hiredis 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5bc8e6a8bc77f130b62f8170d5dfcbb3f7a99ba0
4
+ data.tar.gz: 2c9c825547533005fabb0e1b4358f256152c645c
5
+ SHA512:
6
+ metadata.gz: 0c23eee10b1367c6e9e3ea3fb35c63479572120945c460e9e80b827e4233fb6463174841ada9f218106f72c52fedd23990f6bd4f8e9ae39f9c42a923587ab439
7
+ data.tar.gz: 38d23de708fee5554d7505b3de4fd593baa61ba9610bff0960bda9fad3e93ac26318735afa79a8049a403dc0826cbbb552446d6fda7d93bb1f09e52c8858101e
data/COPYING ADDED
@@ -0,0 +1,28 @@
1
+ Copyright (c) 2010-2012, Pieter Noordhuis
2
+
3
+ All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ * Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ * Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ * Neither the name of Redis nor the names of its contributors may be used to
16
+ endorse or promote products derived from this software without specific prior
17
+ written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
23
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/Rakefile ADDED
@@ -0,0 +1,51 @@
1
+ require "bundler"
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require "rbconfig"
5
+ require "rake/testtask"
6
+ require "rake/extensiontask"
7
+
8
+ unless defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
9
+
10
+ Rake::ExtensionTask.new('hiredis_ext') do |task|
11
+ # Pass --with-foo-config args to extconf.rb
12
+ task.config_options = ARGV[1..-1] || []
13
+ task.lib_dir = File.join(*['lib', 'hiredis', 'ext'])
14
+ end
15
+
16
+ namespace :hiredis do
17
+ task :clean do
18
+ # Fetch hiredis if not present
19
+ if !File.directory?("vendor/hiredis/.git")
20
+ system("git submodule update --init")
21
+ end
22
+ RbConfig::CONFIG['configure_args'] =~ /with-make-prog\=(\w+)/
23
+ make_program = $1 || ENV['make']
24
+ unless make_program then
25
+ make_program = (/mswin/ =~ RUBY_PLATFORM) ? 'nmake' : 'make'
26
+ end
27
+ system("cd vendor/hiredis && #{make_program} clean")
28
+ end
29
+ end
30
+
31
+ # "rake clean" should also clean bundled hiredis
32
+ Rake::Task[:clean].enhance(['hiredis:clean'])
33
+
34
+ # Build from scratch
35
+ task :rebuild => [:clean, :compile]
36
+
37
+ else
38
+
39
+ task :rebuild do
40
+ # no-op
41
+ end
42
+
43
+ end
44
+
45
+ task :default => [:rebuild, :test]
46
+
47
+ desc "Run tests"
48
+ Rake::TestTask.new(:test) do |t|
49
+ t.pattern = 'test/**/*_test.rb'
50
+ t.verbose = true
51
+ end
@@ -0,0 +1,513 @@
1
+ #include <sys/socket.h>
2
+ #include <errno.h>
3
+ #include "hiredis_ext.h"
4
+
5
+ typedef struct redisParentContext {
6
+ redisContext *context;
7
+ struct timeval *timeout;
8
+ } redisParentContext;
9
+
10
+ static void parent_context_try_free_context(redisParentContext *pc) {
11
+ if (pc->context) {
12
+ redisFree(pc->context);
13
+ pc->context = NULL;
14
+ }
15
+ }
16
+
17
+ static void parent_context_try_free_timeout(redisParentContext *pc) {
18
+ if (pc->timeout) {
19
+ free(pc->timeout);
20
+ pc->timeout = NULL;
21
+ }
22
+ }
23
+
24
+ static void parent_context_try_free(redisParentContext *pc) {
25
+ parent_context_try_free_context(pc);
26
+ parent_context_try_free_timeout(pc);
27
+ }
28
+
29
+ static void parent_context_mark(redisParentContext *pc) {
30
+ VALUE root;
31
+ if (pc->context && pc->context->reader) {
32
+ root = (VALUE)redisReplyReaderGetObject(pc->context->reader);
33
+ if (root != 0 && TYPE(root) == T_ARRAY) {
34
+ rb_gc_mark(root);
35
+ }
36
+ }
37
+ }
38
+
39
+ static void parent_context_free(redisParentContext *pc) {
40
+ parent_context_try_free(pc);
41
+ free(pc);
42
+ }
43
+
44
+ static void parent_context_raise(redisParentContext *pc) {
45
+ int err;
46
+ char errstr[1024];
47
+
48
+ /* Copy error and free context */
49
+ err = pc->context->err;
50
+ snprintf(errstr,sizeof(errstr),"%s",pc->context->errstr);
51
+ parent_context_try_free(pc);
52
+
53
+ switch(err) {
54
+ case REDIS_ERR_IO:
55
+ /* Raise native Ruby I/O error */
56
+ rb_sys_fail(0);
57
+ break;
58
+ case REDIS_ERR_EOF:
59
+ /* Raise native Ruby EOFError */
60
+ rb_raise(rb_eEOFError,"%s",errstr);
61
+ break;
62
+ default:
63
+ /* Raise something else */
64
+ rb_raise(rb_eRuntimeError,"%s",errstr);
65
+ }
66
+ }
67
+
68
+ static VALUE connection_parent_context_alloc(VALUE klass) {
69
+ redisParentContext *pc = malloc(sizeof(*pc));
70
+ pc->context = NULL;
71
+ pc->timeout = NULL;
72
+ return Data_Wrap_Struct(klass, parent_context_mark, parent_context_free, pc);
73
+ }
74
+
75
+
76
+ /*
77
+ * The rb_fdset_t API was introduced in Ruby 1.9.1. The rb_fd_thread_select
78
+ * function was introduced in a later version. Therefore, there are one more
79
+ * versions where we cannot simply test HAVE_RB_FD_INIT and be done, we have to
80
+ * explicitly test for HAVE_RB_THREAD_FD_SELECT (also see extconf.rb).
81
+ */
82
+ #ifdef HAVE_RB_THREAD_FD_SELECT
83
+ typedef rb_fdset_t _fdset_t;
84
+ #define _fd_zero(f) rb_fd_zero(f)
85
+ #define _fd_set(n, f) rb_fd_set(n, f)
86
+ #define _fd_clr(n, f) rb_fd_clr(n, f)
87
+ #define _fd_isset(n, f) rb_fd_isset(n, f)
88
+ #define _fd_copy(d, s, n) rb_fd_copy(d, s, n)
89
+ #define _fd_ptr(f) rb_fd_ptr(f)
90
+ #define _fd_init(f) rb_fd_init(f)
91
+ #define _fd_term(f) rb_fd_term(f)
92
+ #define _fd_max(f) rb_fd_max(f)
93
+ #define _thread_fd_select(n, r, w, e, t) rb_thread_fd_select(n, r, w, e, t)
94
+ #else
95
+ typedef fd_set _fdset_t;
96
+ #define _fd_zero(f) FD_ZERO(f)
97
+ #define _fd_set(n, f) FD_SET(n, f)
98
+ #define _fd_clr(n, f) FD_CLR(n, f)
99
+ #define _fd_isset(n, f) FD_ISSET(n, f)
100
+ #define _fd_copy(d, s, n) (*(d) = *(s))
101
+ #define _fd_ptr(f) (f)
102
+ #define _fd_init(f) FD_ZERO(f)
103
+ #define _fd_term(f) (void)(f)
104
+ #define _fd_max(f) FD_SETSIZE
105
+ #define _thread_fd_select(n, r, w, e, t) rb_thread_select(n, r, w, e, t)
106
+ #endif
107
+
108
+ static int __wait_readable(int fd, const struct timeval *timeout, int *isset) {
109
+ struct timeval to;
110
+ struct timeval *toptr = NULL;
111
+
112
+ _fdset_t fds;
113
+
114
+ /* Be cautious: a call to rb_fd_init to initialize the rb_fdset_t structure
115
+ * must be paired with a call to rb_fd_term to free it. */
116
+ _fd_init(&fds);
117
+ _fd_set(fd, &fds);
118
+
119
+ /* rb_thread_{fd_,}select modifies the passed timeval, so we pass a copy */
120
+ if (timeout != NULL) {
121
+ memcpy(&to, timeout, sizeof(to));
122
+ toptr = &to;
123
+ }
124
+
125
+ if (_thread_fd_select(fd + 1, &fds, NULL, NULL, toptr) < 0) {
126
+ _fd_term(&fds);
127
+ return -1;
128
+ }
129
+
130
+ if (_fd_isset(fd, &fds) && isset) {
131
+ *isset = 1;
132
+ }
133
+
134
+ _fd_term(&fds);
135
+ return 0;
136
+ }
137
+
138
+ static int __wait_writable(int fd, const struct timeval *timeout, int *isset) {
139
+ struct timeval to;
140
+ struct timeval *toptr = NULL;
141
+
142
+ _fdset_t fds;
143
+
144
+ /* Be cautious: a call to rb_fd_init to initialize the rb_fdset_t structure
145
+ * must be paired with a call to rb_fd_term to free it. */
146
+ _fd_init(&fds);
147
+ _fd_set(fd, &fds);
148
+
149
+ /* rb_thread_{fd_,}select modifies the passed timeval, so we pass a copy */
150
+ if (timeout != NULL) {
151
+ memcpy(&to, timeout, sizeof(to));
152
+ toptr = &to;
153
+ }
154
+
155
+ if (_thread_fd_select(fd + 1, NULL, &fds, NULL, toptr) < 0) {
156
+ _fd_term(&fds);
157
+ return -1;
158
+ }
159
+
160
+ if (_fd_isset(fd, &fds) && isset) {
161
+ *isset = 1;
162
+ }
163
+
164
+ _fd_term(&fds);
165
+ return 0;
166
+ }
167
+
168
+ static VALUE connection_generic_connect(VALUE self, redisContext *c, VALUE arg_timeout) {
169
+ redisParentContext *pc;
170
+ struct timeval tv;
171
+ struct timeval *timeout = NULL;
172
+ int writable = 0;
173
+ int optval = 0;
174
+ socklen_t optlen = sizeof(optval);
175
+
176
+ Data_Get_Struct(self,redisParentContext,pc);
177
+
178
+ if (c->err) {
179
+ char buf[1024];
180
+ int err;
181
+
182
+ /* Copy error and free context */
183
+ err = c->err;
184
+ snprintf(buf,sizeof(buf),"%s",c->errstr);
185
+ redisFree(c);
186
+
187
+ if (err == REDIS_ERR_IO) {
188
+ /* Raise native Ruby I/O error */
189
+ rb_sys_fail(0);
190
+ } else {
191
+ /* Raise something else */
192
+ rb_raise(rb_eRuntimeError,"%s",buf);
193
+ }
194
+ }
195
+
196
+ /* Default to context-wide timeout setting */
197
+ if (pc->timeout != NULL) {
198
+ timeout = pc->timeout;
199
+ }
200
+
201
+ /* Override timeout when timeout argument is available */
202
+ if (arg_timeout != Qnil) {
203
+ tv.tv_sec = NUM2INT(arg_timeout) / 1000000;
204
+ tv.tv_usec = NUM2INT(arg_timeout) % 1000000;
205
+ timeout = &tv;
206
+ }
207
+
208
+ /* Wait for socket to become writable */
209
+ if (__wait_writable(c->fd, timeout, &writable) < 0) {
210
+ goto sys_fail;
211
+ }
212
+
213
+ if (!writable) {
214
+ errno = ETIMEDOUT;
215
+ goto sys_fail;
216
+ }
217
+
218
+ /* Check for socket error */
219
+ if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0) {
220
+ goto sys_fail;
221
+ }
222
+
223
+ if (optval) {
224
+ errno = optval;
225
+ goto sys_fail;
226
+ }
227
+
228
+ parent_context_try_free_context(pc);
229
+ pc->context = c;
230
+ pc->context->reader->fn = &redisExtReplyObjectFunctions;
231
+ return Qnil;
232
+
233
+ sys_fail:
234
+ redisFree(c);
235
+ rb_sys_fail(0);
236
+ }
237
+
238
+ static VALUE connection_connect(int argc, VALUE *argv, VALUE self) {
239
+ redisContext *c;
240
+ VALUE arg_host = Qnil;
241
+ VALUE arg_port = Qnil;
242
+ VALUE arg_timeout = Qnil;
243
+
244
+ if (argc == 2 || argc == 3) {
245
+ arg_host = argv[0];
246
+ arg_port = argv[1];
247
+
248
+ if (argc == 3) {
249
+ arg_timeout = argv[2];
250
+
251
+ /* Sanity check */
252
+ if (NUM2INT(arg_timeout) <= 0) {
253
+ rb_raise(rb_eArgError, "timeout should be positive");
254
+ }
255
+ }
256
+ } else {
257
+ rb_raise(rb_eArgError, "invalid number of arguments");
258
+ }
259
+
260
+ c = redisConnectNonBlock(StringValuePtr(arg_host), NUM2INT(arg_port));
261
+ return connection_generic_connect(self,c,arg_timeout);
262
+ }
263
+
264
+ static VALUE connection_connect_unix(int argc, VALUE *argv, VALUE self) {
265
+ redisContext *c;
266
+ VALUE arg_path = Qnil;
267
+ VALUE arg_timeout = Qnil;
268
+
269
+ if (argc == 1 || argc == 2) {
270
+ arg_path = argv[0];
271
+
272
+ if (argc == 2) {
273
+ arg_timeout = argv[1];
274
+
275
+ /* Sanity check */
276
+ if (NUM2INT(arg_timeout) <= 0) {
277
+ rb_raise(rb_eArgError, "timeout should be positive");
278
+ }
279
+ }
280
+ } else {
281
+ rb_raise(rb_eArgError, "invalid number of arguments");
282
+ }
283
+
284
+ c = redisConnectUnixNonBlock(StringValuePtr(arg_path));
285
+ return connection_generic_connect(self,c,arg_timeout);
286
+ }
287
+
288
+ static VALUE connection_is_connected(VALUE self) {
289
+ redisParentContext *pc;
290
+ Data_Get_Struct(self,redisParentContext,pc);
291
+ if (pc->context && !pc->context->err)
292
+ return Qtrue;
293
+ else
294
+ return Qfalse;
295
+ }
296
+
297
+ static VALUE connection_disconnect(VALUE self) {
298
+ redisParentContext *pc;
299
+ Data_Get_Struct(self,redisParentContext,pc);
300
+ if (!pc->context)
301
+ rb_raise(rb_eRuntimeError,"%s","not connected");
302
+ parent_context_try_free(pc);
303
+ return Qnil;
304
+ }
305
+
306
+ static VALUE connection_write(VALUE self, VALUE command) {
307
+ redisParentContext *pc;
308
+ int argc;
309
+ char **argv = NULL;
310
+ size_t *alen = NULL;
311
+ int i;
312
+
313
+ /* Commands should be an array of commands, where each command
314
+ * is an array of string arguments. */
315
+ if (TYPE(command) != T_ARRAY)
316
+ rb_raise(rb_eArgError,"%s","not an array");
317
+
318
+ Data_Get_Struct(self,redisParentContext,pc);
319
+ if (!pc->context)
320
+ rb_raise(rb_eRuntimeError,"%s","not connected");
321
+
322
+ argc = (int)RARRAY_LEN(command);
323
+ argv = malloc(argc*sizeof(char*));
324
+ alen = malloc(argc*sizeof(size_t));
325
+ for (i = 0; i < argc; i++) {
326
+ /* Replace arguments in the arguments array to prevent their string
327
+ * equivalents to be garbage collected before this loop is done. */
328
+ VALUE entry = rb_obj_as_string(rb_ary_entry(command, i));
329
+ rb_ary_store(command, i, entry);
330
+ argv[i] = RSTRING_PTR(entry);
331
+ alen[i] = RSTRING_LEN(entry);
332
+ }
333
+ redisAppendCommandArgv(pc->context,argc,(const char**)argv,alen);
334
+ free(argv);
335
+ free(alen);
336
+ return Qnil;
337
+ }
338
+
339
+ static VALUE connection_flush(VALUE self) {
340
+ redisParentContext *pc;
341
+ redisContext *c;
342
+ int wdone = 0;
343
+
344
+ Data_Get_Struct(self,redisParentContext,pc);
345
+ if (!pc->context)
346
+ rb_raise(rb_eRuntimeError, "not connected");
347
+
348
+ c = pc->context;
349
+ while (!wdone) {
350
+ errno = 0;
351
+
352
+ if (redisBufferWrite(c, &wdone) == REDIS_ERR) {
353
+ /* Socket error */
354
+ parent_context_raise(pc);
355
+ }
356
+
357
+ if (errno == EAGAIN) {
358
+ int writable = 0;
359
+
360
+ if (__wait_writable(c->fd, pc->timeout, &writable) < 0) {
361
+ rb_sys_fail(0);
362
+ }
363
+
364
+ if (!writable) {
365
+ errno = EAGAIN;
366
+ rb_sys_fail(0);
367
+ }
368
+ }
369
+ }
370
+
371
+ return Qnil;
372
+ }
373
+
374
+ static int __get_reply(redisParentContext *pc, VALUE *reply) {
375
+ redisContext *c = pc->context;
376
+ int wdone = 0;
377
+ void *aux = NULL;
378
+
379
+ /* Try to read pending replies */
380
+ if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) {
381
+ /* Protocol error */
382
+ return -1;
383
+ }
384
+
385
+ if (aux == NULL) {
386
+ /* Write until the write buffer is drained */
387
+ while (!wdone) {
388
+ errno = 0;
389
+
390
+ if (redisBufferWrite(c, &wdone) == REDIS_ERR) {
391
+ /* Socket error */
392
+ return -1;
393
+ }
394
+
395
+ if (errno == EAGAIN) {
396
+ int writable = 0;
397
+
398
+ if (__wait_writable(c->fd, pc->timeout, &writable) < 0) {
399
+ rb_sys_fail(0);
400
+ }
401
+
402
+ if (!writable) {
403
+ errno = EAGAIN;
404
+ rb_sys_fail(0);
405
+ }
406
+ }
407
+ }
408
+
409
+ /* Read until there is a full reply */
410
+ while (aux == NULL) {
411
+ errno = 0;
412
+
413
+ if (redisBufferRead(c) == REDIS_ERR) {
414
+ /* Socket error */
415
+ return -1;
416
+ }
417
+
418
+ if (errno == EAGAIN) {
419
+ int readable = 0;
420
+
421
+ if (__wait_readable(c->fd, pc->timeout, &readable) < 0) {
422
+ rb_sys_fail(0);
423
+ }
424
+
425
+ if (!readable) {
426
+ errno = EAGAIN;
427
+ rb_sys_fail(0);
428
+ }
429
+
430
+ /* Retry */
431
+ continue;
432
+ }
433
+
434
+ if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) {
435
+ /* Protocol error */
436
+ return -1;
437
+ }
438
+ }
439
+ }
440
+
441
+ /* Set reply object */
442
+ if (reply != NULL) {
443
+ *reply = (VALUE)aux;
444
+ }
445
+
446
+ return 0;
447
+ }
448
+
449
+ static VALUE connection_read(VALUE self) {
450
+ redisParentContext *pc;
451
+ VALUE reply;
452
+
453
+ Data_Get_Struct(self,redisParentContext,pc);
454
+ if (!pc->context)
455
+ rb_raise(rb_eRuntimeError, "not connected");
456
+
457
+ if (__get_reply(pc,&reply) == -1)
458
+ parent_context_raise(pc);
459
+
460
+ return reply;
461
+ }
462
+
463
+ static VALUE connection_set_timeout(VALUE self, VALUE usecs) {
464
+ redisParentContext *pc;
465
+ struct timeval *ptr;
466
+
467
+ Data_Get_Struct(self,redisParentContext,pc);
468
+
469
+ if (NUM2INT(usecs) < 0) {
470
+ rb_raise(rb_eArgError, "timeout cannot be negative");
471
+ } else {
472
+ parent_context_try_free_timeout(pc);
473
+
474
+ /* A timeout equal to zero means not to time out. This translates to a
475
+ * NULL timeout for select(2). Only allocate and populate the timeout
476
+ * when it is a positive integer. */
477
+ if (NUM2INT(usecs) > 0) {
478
+ ptr = malloc(sizeof(*ptr));
479
+ ptr->tv_sec = NUM2INT(usecs) / 1000000;
480
+ ptr->tv_usec = NUM2INT(usecs) % 1000000;
481
+ pc->timeout = ptr;
482
+ }
483
+ }
484
+
485
+ return Qnil;
486
+ }
487
+
488
+ static VALUE connection_fileno(VALUE self) {
489
+ redisParentContext *pc;
490
+
491
+ Data_Get_Struct(self,redisParentContext,pc);
492
+
493
+ if (!pc->context)
494
+ rb_raise(rb_eRuntimeError, "not connected");
495
+
496
+ return INT2NUM(pc->context->fd);
497
+ }
498
+
499
+ VALUE klass_connection;
500
+ void InitConnection(VALUE mod) {
501
+ klass_connection = rb_define_class_under(mod, "Connection", rb_cObject);
502
+ rb_global_variable(&klass_connection);
503
+ rb_define_alloc_func(klass_connection, connection_parent_context_alloc);
504
+ rb_define_method(klass_connection, "connect", connection_connect, -1);
505
+ rb_define_method(klass_connection, "connect_unix", connection_connect_unix, -1);
506
+ rb_define_method(klass_connection, "connected?", connection_is_connected, 0);
507
+ rb_define_method(klass_connection, "disconnect", connection_disconnect, 0);
508
+ rb_define_method(klass_connection, "timeout=", connection_set_timeout, 1);
509
+ rb_define_method(klass_connection, "fileno", connection_fileno, 0);
510
+ rb_define_method(klass_connection, "write", connection_write, 1);
511
+ rb_define_method(klass_connection, "flush", connection_flush, 0);
512
+ rb_define_method(klass_connection, "read", connection_read, 0);
513
+ }
@@ -0,0 +1,33 @@
1
+ require 'mkmf'
2
+
3
+ RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
4
+
5
+ hiredis_dir = File.join(File.dirname(__FILE__), %w{.. .. vendor hiredis})
6
+ unless File.directory?(hiredis_dir)
7
+ STDERR.puts "vendor/hiredis missing, please checkout its submodule..."
8
+ exit 1
9
+ end
10
+
11
+ RbConfig::CONFIG['configure_args'] =~ /with-make-prog\=(\w+)/
12
+ make_program = $1 || ENV['make']
13
+ make_program ||= case RUBY_PLATFORM
14
+ when /mswin/
15
+ 'nmake'
16
+ when /(bsd|solaris)/
17
+ 'gmake'
18
+ else
19
+ 'make'
20
+ end
21
+
22
+ # Make sure hiredis is built...
23
+ Dir.chdir(hiredis_dir) do
24
+ success = system("#{make_program} static")
25
+ raise "Building hiredis failed" if !success
26
+ end
27
+
28
+ # Statically link to hiredis (mkmf can't do this for us)
29
+ $CFLAGS << " -I#{hiredis_dir}"
30
+ $LDFLAGS << " #{hiredis_dir}/libhiredis.a"
31
+
32
+ have_func("rb_thread_fd_select")
33
+ create_makefile('hiredis/ext/hiredis_ext')
@@ -0,0 +1,15 @@
1
+ #include <stdlib.h>
2
+ #include <string.h>
3
+ #include <assert.h>
4
+ #include "hiredis_ext.h"
5
+
6
+ VALUE mod_hiredis;
7
+ VALUE mod_ext;
8
+ void Init_hiredis_ext() {
9
+ mod_hiredis = rb_define_module("Hiredis");
10
+ mod_ext = rb_define_module_under(mod_hiredis,"Ext");
11
+ rb_global_variable(&mod_hiredis);
12
+ rb_global_variable(&mod_ext);
13
+ InitReader(mod_ext);
14
+ InitConnection(mod_ext);
15
+ }
@@ -0,0 +1,44 @@
1
+ #ifndef __HIREDIS_EXT_H
2
+ #define __HIREDIS_EXT_H
3
+
4
+ /* Defined for Rubinius. This indicates a char* obtained
5
+ * through RSTRING_PTR is never modified in place. With this
6
+ * define Rubinius can disable the slow copy back mechanisms
7
+ * to make sure strings are updated at the Ruby side.
8
+ */
9
+ #define RSTRING_NOT_MODIFIED
10
+
11
+ #include "hiredis.h"
12
+ #include "ruby.h"
13
+
14
+ /* Defined in hiredis_ext.c */
15
+ extern VALUE mod_hiredis;
16
+
17
+ /* Defined in reader.c */
18
+ extern redisReplyObjectFunctions redisExtReplyObjectFunctions;
19
+ extern VALUE klass_reader;
20
+ extern void InitReader(VALUE module);
21
+
22
+ /* Defined in connection.c */
23
+ extern VALUE klass_connection;
24
+ extern VALUE error_eof;
25
+ extern void InitConnection(VALUE module);
26
+
27
+ /* Borrowed from Nokogiri */
28
+ #ifndef RSTRING_PTR
29
+ #define RSTRING_PTR(s) (RSTRING(s)->ptr)
30
+ #endif
31
+
32
+ #ifndef RSTRING_LEN
33
+ #define RSTRING_LEN(s) (RSTRING(s)->len)
34
+ #endif
35
+
36
+ #ifndef RARRAY_PTR
37
+ #define RARRAY_PTR(a) RARRAY(a)->ptr
38
+ #endif
39
+
40
+ #ifndef RARRAY_LEN
41
+ #define RARRAY_LEN(a) RARRAY(a)->len
42
+ #endif
43
+
44
+ #endif