hiredis 0.6.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 9f5223293433c777928e04bd8c99236f393d6f84
4
- data.tar.gz: 2c915fb52bd73e9816f5acce51dd3c72c16da9cc
2
+ SHA256:
3
+ metadata.gz: 3677528ff01fba750831fbc49dd7a6dac178ad46ecd19ab9dbc63d15f924fac4
4
+ data.tar.gz: 94d08177ef96016ff252864e6baba1730c2885e44466da01275bf35e1c13425e
5
5
  SHA512:
6
- metadata.gz: 8ee5ef3080b1bd3231ebe60f5d2f76a59bfeee1281c0a642d48cffd6a408b86980f82a7b384288229bb788c2e0332db2f2bd92568c538c3042719d04cf8c2125
7
- data.tar.gz: 81c4474b55f788acded811dbefdc9b125a906eeb3c5008afd94f8663ff62eaae8f9d1939e58f89166624b7961e1433eeb3d971fde9c0ca6304370b325083f8ac
6
+ metadata.gz: 9e0e6550901bc8c02c8b93e38d61e2d38ac8a094bdb01d2b4e64db82dd6d3c17eac4b9ea72cfd4743217435dcf2637f15e42d7ac666d6241c8c603faf1917e71
7
+ data.tar.gz: d6f45386a24e3e383260cf5539212c6c7412733a891740b45422062b3eb33f1655066956d5a90f47415ec6f90da71b4832c81a9a4f3e957d0343716811c4d372
@@ -27,9 +27,10 @@ static void parent_context_try_free(redisParentContext *pc) {
27
27
  }
28
28
 
29
29
  static void parent_context_mark(redisParentContext *pc) {
30
- VALUE root;
30
+ // volatile until rb_gc_mark
31
+ volatile VALUE root;
31
32
  if (pc->context && pc->context->reader) {
32
- root = (VALUE)redisReplyReaderGetObject(pc->context->reader);
33
+ root = (VALUE)redisReaderGetObject(pc->context->reader);
33
34
  if (root != 0 && TYPE(root) == T_ARRAY) {
34
35
  rb_gc_mark(root);
35
36
  }
@@ -448,7 +449,7 @@ static int __get_reply(redisParentContext *pc, VALUE *reply) {
448
449
 
449
450
  static VALUE connection_read(VALUE self) {
450
451
  redisParentContext *pc;
451
- VALUE reply;
452
+ volatile VALUE reply;
452
453
 
453
454
  Data_Get_Struct(self,redisParentContext,pc);
454
455
  if (!pc->context)
@@ -10,7 +10,7 @@ static ID str_force_encoding = 0;
10
10
  * Note that the parent should always be of type T_ARRAY. */
11
11
  static void *tryParentize(const redisReadTask *task, VALUE v) {
12
12
  if (task && task->parent != NULL) {
13
- VALUE parent = (VALUE)task->parent->obj;
13
+ volatile VALUE parent = (VALUE)task->parent->obj;
14
14
  assert(TYPE(parent) == T_ARRAY);
15
15
  rb_ary_store(parent,task->idx,v);
16
16
  }
@@ -18,7 +18,7 @@ static void *tryParentize(const redisReadTask *task, VALUE v) {
18
18
  }
19
19
 
20
20
  static void *createStringObject(const redisReadTask *task, char *str, size_t len) {
21
- VALUE v, enc;
21
+ volatile VALUE v, enc;
22
22
  v = rb_str_new(str,len);
23
23
 
24
24
  /* Force default external encoding if possible. */
@@ -35,12 +35,12 @@ static void *createStringObject(const redisReadTask *task, char *str, size_t len
35
35
  }
36
36
 
37
37
  static void *createArrayObject(const redisReadTask *task, int elements) {
38
- VALUE v = rb_ary_new2(elements);
38
+ volatile VALUE v = rb_ary_new2(elements);
39
39
  return tryParentize(task,v);
40
40
  }
41
41
 
42
42
  static void *createIntegerObject(const redisReadTask *task, long long value) {
43
- VALUE v = LL2NUM(value);
43
+ volatile VALUE v = LL2NUM(value);
44
44
  return tryParentize(task,v);
45
45
  }
46
46
 
@@ -62,7 +62,13 @@ redisReplyObjectFunctions redisExtReplyObjectFunctions = {
62
62
  };
63
63
 
64
64
  static void reader_mark(redisReader *reader) {
65
- VALUE root = (VALUE)reader->reply;
65
+ // volatile until rb_gc_mark
66
+ volatile VALUE root = (VALUE)reader->reply;
67
+ // FIXME - PCO - checking root for 0 is checkign to see if the value is
68
+ // Qfalse. I suspect that is not what is intended here. Checking the
69
+ // redisReader code might clarify. It would be unfortunate if the reply, a
70
+ // void* was using NULL to indicate not set but that may be the nature of
71
+ // the redisReader library. It is worth checking anyway.
66
72
  if (root != 0 && TYPE(root) == T_ARRAY) rb_gc_mark(root);
67
73
  }
68
74
 
@@ -85,7 +91,7 @@ static VALUE reader_feed(VALUE klass, VALUE str) {
85
91
 
86
92
  static VALUE reader_gets(VALUE klass) {
87
93
  redisReader *reader;
88
- VALUE reply;
94
+ volatile VALUE reply;
89
95
 
90
96
  Data_Get_Struct(klass, redisReader, reader);
91
97
  if (redisReaderGetReply(reader,(void**)&reply) != REDIS_OK)
@@ -1,3 +1,3 @@
1
1
  module Hiredis
2
- VERSION = "0.6.1"
2
+ VERSION = "0.6.2"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hiredis
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pieter Noordhuis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-29 00:00:00.000000000 Z
11
+ date: 2018-11-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -76,22 +76,6 @@ files:
76
76
  - lib/hiredis/ruby/connection.rb
77
77
  - lib/hiredis/ruby/reader.rb
78
78
  - lib/hiredis/version.rb
79
- - vendor/hiredis/COPYING
80
- - vendor/hiredis/Makefile
81
- - vendor/hiredis/async.c
82
- - vendor/hiredis/async.h
83
- - vendor/hiredis/dict.c
84
- - vendor/hiredis/dict.h
85
- - vendor/hiredis/fmacros.h
86
- - vendor/hiredis/hiredis.c
87
- - vendor/hiredis/hiredis.h
88
- - vendor/hiredis/net.c
89
- - vendor/hiredis/net.h
90
- - vendor/hiredis/read.c
91
- - vendor/hiredis/read.h
92
- - vendor/hiredis/sds.c
93
- - vendor/hiredis/sds.h
94
- - vendor/hiredis/test.c
95
79
  homepage: http://github.com/redis/hiredis-rb
96
80
  licenses:
97
81
  - BSD-3-Clause
@@ -112,7 +96,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
112
96
  version: '0'
113
97
  requirements: []
114
98
  rubyforge_project:
115
- rubygems_version: 2.5.1
99
+ rubygems_version: 2.7.6
116
100
  signing_key:
117
101
  specification_version: 4
118
102
  summary: Ruby wrapper for hiredis (protocol serialization/deserialization and blocking
@@ -1,29 +0,0 @@
1
- Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
2
- Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
3
-
4
- All rights reserved.
5
-
6
- Redistribution and use in source and binary forms, with or without
7
- modification, are permitted provided that the following conditions are met:
8
-
9
- * Redistributions of source code must retain the above copyright notice,
10
- this list of conditions and the following disclaimer.
11
-
12
- * Redistributions in binary form must reproduce the above copyright notice,
13
- this list of conditions and the following disclaimer in the documentation
14
- and/or other materials provided with the distribution.
15
-
16
- * Neither the name of Redis nor the names of its contributors may be used
17
- to endorse or promote products derived from this software without specific
18
- prior written permission.
19
-
20
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
24
- ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
27
- ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -1,194 +0,0 @@
1
- # Hiredis Makefile
2
- # Copyright (C) 2010-2011 Salvatore Sanfilippo <antirez at gmail dot com>
3
- # Copyright (C) 2010-2011 Pieter Noordhuis <pcnoordhuis at gmail dot com>
4
- # This file is released under the BSD license, see the COPYING file
5
-
6
- OBJ=net.o hiredis.o sds.o async.o read.o
7
- EXAMPLES=hiredis-example hiredis-example-libevent hiredis-example-libev hiredis-example-glib
8
- TESTS=hiredis-test
9
- LIBNAME=libhiredis
10
- PKGCONFNAME=hiredis.pc
11
-
12
- HIREDIS_MAJOR=$(shell grep HIREDIS_MAJOR hiredis.h | awk '{print $$3}')
13
- HIREDIS_MINOR=$(shell grep HIREDIS_MINOR hiredis.h | awk '{print $$3}')
14
- HIREDIS_PATCH=$(shell grep HIREDIS_PATCH hiredis.h | awk '{print $$3}')
15
-
16
- # Installation related variables and target
17
- PREFIX?=/usr/local
18
- INCLUDE_PATH?=include/hiredis
19
- LIBRARY_PATH?=lib
20
- PKGCONF_PATH?=pkgconfig
21
- INSTALL_INCLUDE_PATH= $(PREFIX)/$(INCLUDE_PATH)
22
- INSTALL_LIBRARY_PATH= $(PREFIX)/$(LIBRARY_PATH)
23
- INSTALL_PKGCONF_PATH= $(LIBRARY_PATH)/$(PKGCONF_PATH)
24
-
25
- # redis-server configuration used for testing
26
- REDIS_PORT=56379
27
- REDIS_SERVER=redis-server
28
- define REDIS_TEST_CONFIG
29
- daemonize yes
30
- pidfile /tmp/hiredis-test-redis.pid
31
- port $(REDIS_PORT)
32
- bind 127.0.0.1
33
- unixsocket /tmp/hiredis-test-redis.sock
34
- endef
35
- export REDIS_TEST_CONFIG
36
-
37
- # Fallback to gcc when $CC is not in $PATH.
38
- CC:=$(shell sh -c 'type $(CC) >/dev/null 2>/dev/null && echo $(CC) || echo gcc')
39
- OPTIMIZATION?=-O3
40
- WARNINGS=-Wall -W -Wstrict-prototypes -Wwrite-strings
41
- DEBUG?= -g -ggdb
42
- REAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CFLAGS) $(WARNINGS) $(DEBUG) $(ARCH)
43
- REAL_LDFLAGS=$(LDFLAGS) $(ARCH)
44
-
45
- DYLIBSUFFIX=so
46
- STLIBSUFFIX=a
47
- DYLIB_MINOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR).$(HIREDIS_MINOR)
48
- DYLIB_MAJOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR)
49
- DYLIBNAME=$(LIBNAME).$(DYLIBSUFFIX)
50
- DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)
51
- STLIBNAME=$(LIBNAME).$(STLIBSUFFIX)
52
- STLIB_MAKE_CMD=ar rcs $(STLIBNAME)
53
-
54
- # Platform-specific overrides
55
- uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
56
- ifeq ($(uname_S),SunOS)
57
- REAL_LDFLAGS+= -ldl -lnsl -lsocket
58
- DYLIB_MAKE_CMD=$(CC) -G -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME) $(LDFLAGS)
59
- INSTALL= cp -r
60
- endif
61
- ifeq ($(uname_S),Darwin)
62
- DYLIBSUFFIX=dylib
63
- DYLIB_MINOR_NAME=$(LIBNAME).$(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(DYLIBSUFFIX)
64
- DYLIB_MAJOR_NAME=$(LIBNAME).$(HIREDIS_MAJOR).$(DYLIBSUFFIX)
65
- DYLIB_MAKE_CMD=$(CC) -shared -Wl,-install_name,$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)
66
- endif
67
-
68
- all: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME)
69
-
70
- # Deps (use make dep to generate this)
71
- async.o: async.c fmacros.h async.h hiredis.h read.h sds.h net.h dict.c dict.h
72
- dict.o: dict.c fmacros.h dict.h
73
- hiredis.o: hiredis.c fmacros.h hiredis.h read.h sds.h net.h
74
- net.o: net.c fmacros.h net.h hiredis.h read.h sds.h
75
- read.o: read.c fmacros.h read.h sds.h
76
- sds.o: sds.c sds.h
77
- test.o: test.c fmacros.h hiredis.h read.h sds.h
78
-
79
- $(DYLIBNAME): $(OBJ)
80
- $(DYLIB_MAKE_CMD) $(OBJ)
81
-
82
- $(STLIBNAME): $(OBJ)
83
- $(STLIB_MAKE_CMD) $(OBJ)
84
-
85
- dynamic: $(DYLIBNAME)
86
- static: $(STLIBNAME)
87
-
88
- # Binaries:
89
- hiredis-example-libevent: examples/example-libevent.c adapters/libevent.h $(STLIBNAME)
90
- $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -levent $(STLIBNAME)
91
-
92
- hiredis-example-libev: examples/example-libev.c adapters/libev.h $(STLIBNAME)
93
- $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -lev $(STLIBNAME)
94
-
95
- hiredis-example-glib: examples/example-glib.c adapters/glib.h $(STLIBNAME)
96
- $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) $(shell pkg-config --cflags --libs glib-2.0) -I. $< $(STLIBNAME)
97
-
98
- ifndef AE_DIR
99
- hiredis-example-ae:
100
- @echo "Please specify AE_DIR (e.g. <redis repository>/src)"
101
- @false
102
- else
103
- hiredis-example-ae: examples/example-ae.c adapters/ae.h $(STLIBNAME)
104
- $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(AE_DIR) $< $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o $(AE_DIR)/../deps/jemalloc/lib/libjemalloc.a -pthread $(STLIBNAME)
105
- endif
106
-
107
- ifndef LIBUV_DIR
108
- hiredis-example-libuv:
109
- @echo "Please specify LIBUV_DIR (e.g. ../libuv/)"
110
- @false
111
- else
112
- hiredis-example-libuv: examples/example-libuv.c adapters/libuv.h $(STLIBNAME)
113
- $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(LIBUV_DIR)/include $< $(LIBUV_DIR)/.libs/libuv.a -lpthread $(STLIBNAME)
114
- endif
115
-
116
- hiredis-example: examples/example.c $(STLIBNAME)
117
- $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< $(STLIBNAME)
118
-
119
- examples: $(EXAMPLES)
120
-
121
- hiredis-test: test.o $(STLIBNAME)
122
-
123
- hiredis-%: %.o $(STLIBNAME)
124
- $(CC) -o $@ $(REAL_LDFLAGS) $< $(STLIBNAME)
125
-
126
- test: hiredis-test
127
- ./hiredis-test
128
-
129
- check: hiredis-test
130
- @echo "$$REDIS_TEST_CONFIG" | $(REDIS_SERVER) -
131
- $(PRE) ./hiredis-test -h 127.0.0.1 -p $(REDIS_PORT) -s /tmp/hiredis-test-redis.sock || \
132
- ( kill `cat /tmp/hiredis-test-redis.pid` && false )
133
- kill `cat /tmp/hiredis-test-redis.pid`
134
-
135
- .c.o:
136
- $(CC) -std=c99 -pedantic -c $(REAL_CFLAGS) $<
137
-
138
- clean:
139
- rm -rf $(DYLIBNAME) $(STLIBNAME) $(TESTS) $(PKGCONFNAME) examples/hiredis-example* *.o *.gcda *.gcno *.gcov
140
-
141
- dep:
142
- $(CC) -MM *.c
143
-
144
- ifeq ($(uname_S),SunOS)
145
- INSTALL?= cp -r
146
- endif
147
-
148
- INSTALL?= cp -a
149
-
150
- $(PKGCONFNAME): $(PKGCONF_SRCNAME)
151
- @echo "Generating $@ for pkgconfig..."
152
- @echo prefix=$(PREFIX) > $@
153
- @echo exec_prefix=$${prefix} >> $@
154
- @echo libdir=$(INSTALL_LIBRARY_PATH) >> $@
155
- @echo includedir=$(INSTALL_INCLUDE_PATH) >> $@
156
- @echo >> $@
157
- @echo Name: hiredis >> $@
158
- @echo Description: Minimalistic C client library for the Redis database. >> $@
159
- @echo Version: $(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(HIREDIS_PATCH) >> $@
160
- @echo Libs: -L$${libdir} -lhiredis >> $@
161
- @echo Cflags: -I$${includedir} -D_FILE_OFFSET_BITS=64 >> $@
162
-
163
- install: $(DYLIBNAME) $(STLIBNAME)
164
- mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_LIBRARY_PATH)
165
- $(INSTALL) hiredis.h async.h adapters $(INSTALL_INCLUDE_PATH)
166
- $(INSTALL) $(DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(DYLIB_MINOR_NAME)
167
- cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIB_MAJOR_NAME)
168
- cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MAJOR_NAME) $(DYLIBNAME)
169
- $(INSTALL) $(STLIBNAME) $(INSTALL_LIBRARY_PATH)
170
- mkdir -p $(INSTALL_PKGCONF_PATH)
171
- $(INSTALL) $(PKGCONFNAME) $(INSTALL_PKGCONF_PATH)
172
-
173
- 32bit:
174
- @echo ""
175
- @echo "WARNING: if this fails under Linux you probably need to install libc6-dev-i386"
176
- @echo ""
177
- $(MAKE) CFLAGS="-m32" LDFLAGS="-m32"
178
-
179
- gprof:
180
- $(MAKE) CFLAGS="-pg" LDFLAGS="-pg"
181
-
182
- gcov:
183
- $(MAKE) CFLAGS="-fprofile-arcs -ftest-coverage" LDFLAGS="-fprofile-arcs"
184
-
185
- coverage: gcov
186
- make check
187
- mkdir -p tmp/lcov
188
- lcov -d . -c -o tmp/lcov/hiredis.info
189
- genhtml --legend -o tmp/lcov/report tmp/lcov/hiredis.info
190
-
191
- noopt:
192
- $(MAKE) OPTIMIZATION=""
193
-
194
- .PHONY: all test check clean dep install 32bit gprof gcov noopt
@@ -1,685 +0,0 @@
1
- /*
2
- * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
3
- * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
4
- *
5
- * All rights reserved.
6
- *
7
- * Redistribution and use in source and binary forms, with or without
8
- * modification, are permitted provided that the following conditions are met:
9
- *
10
- * * Redistributions of source code must retain the above copyright notice,
11
- * this list of conditions and the following disclaimer.
12
- * * Redistributions in binary form must reproduce the above copyright
13
- * notice, this list of conditions and the following disclaimer in the
14
- * documentation and/or other materials provided with the distribution.
15
- * * Neither the name of Redis nor the names of its contributors may be used
16
- * to endorse or promote products derived from this software without
17
- * specific prior written permission.
18
- *
19
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
- * POSSIBILITY OF SUCH DAMAGE.
30
- */
31
-
32
- #include "fmacros.h"
33
- #include <stdlib.h>
34
- #include <string.h>
35
- #include <strings.h>
36
- #include <assert.h>
37
- #include <ctype.h>
38
- #include <errno.h>
39
- #include "async.h"
40
- #include "net.h"
41
- #include "dict.c"
42
- #include "sds.h"
43
-
44
- #define _EL_ADD_READ(ctx) do { \
45
- if ((ctx)->ev.addRead) (ctx)->ev.addRead((ctx)->ev.data); \
46
- } while(0)
47
- #define _EL_DEL_READ(ctx) do { \
48
- if ((ctx)->ev.delRead) (ctx)->ev.delRead((ctx)->ev.data); \
49
- } while(0)
50
- #define _EL_ADD_WRITE(ctx) do { \
51
- if ((ctx)->ev.addWrite) (ctx)->ev.addWrite((ctx)->ev.data); \
52
- } while(0)
53
- #define _EL_DEL_WRITE(ctx) do { \
54
- if ((ctx)->ev.delWrite) (ctx)->ev.delWrite((ctx)->ev.data); \
55
- } while(0)
56
- #define _EL_CLEANUP(ctx) do { \
57
- if ((ctx)->ev.cleanup) (ctx)->ev.cleanup((ctx)->ev.data); \
58
- } while(0);
59
-
60
- /* Forward declaration of function in hiredis.c */
61
- int __redisAppendCommand(redisContext *c, const char *cmd, size_t len);
62
-
63
- /* Functions managing dictionary of callbacks for pub/sub. */
64
- static unsigned int callbackHash(const void *key) {
65
- return dictGenHashFunction((const unsigned char *)key,
66
- sdslen((const sds)key));
67
- }
68
-
69
- static void *callbackValDup(void *privdata, const void *src) {
70
- ((void) privdata);
71
- redisCallback *dup = malloc(sizeof(*dup));
72
- memcpy(dup,src,sizeof(*dup));
73
- return dup;
74
- }
75
-
76
- static int callbackKeyCompare(void *privdata, const void *key1, const void *key2) {
77
- int l1, l2;
78
- ((void) privdata);
79
-
80
- l1 = sdslen((const sds)key1);
81
- l2 = sdslen((const sds)key2);
82
- if (l1 != l2) return 0;
83
- return memcmp(key1,key2,l1) == 0;
84
- }
85
-
86
- static void callbackKeyDestructor(void *privdata, void *key) {
87
- ((void) privdata);
88
- sdsfree((sds)key);
89
- }
90
-
91
- static void callbackValDestructor(void *privdata, void *val) {
92
- ((void) privdata);
93
- free(val);
94
- }
95
-
96
- static dictType callbackDict = {
97
- callbackHash,
98
- NULL,
99
- callbackValDup,
100
- callbackKeyCompare,
101
- callbackKeyDestructor,
102
- callbackValDestructor
103
- };
104
-
105
- static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
106
- redisAsyncContext *ac;
107
-
108
- ac = realloc(c,sizeof(redisAsyncContext));
109
- if (ac == NULL)
110
- return NULL;
111
-
112
- c = &(ac->c);
113
-
114
- /* The regular connect functions will always set the flag REDIS_CONNECTED.
115
- * For the async API, we want to wait until the first write event is
116
- * received up before setting this flag, so reset it here. */
117
- c->flags &= ~REDIS_CONNECTED;
118
-
119
- ac->err = 0;
120
- ac->errstr = NULL;
121
- ac->data = NULL;
122
-
123
- ac->ev.data = NULL;
124
- ac->ev.addRead = NULL;
125
- ac->ev.delRead = NULL;
126
- ac->ev.addWrite = NULL;
127
- ac->ev.delWrite = NULL;
128
- ac->ev.cleanup = NULL;
129
-
130
- ac->onConnect = NULL;
131
- ac->onDisconnect = NULL;
132
-
133
- ac->replies.head = NULL;
134
- ac->replies.tail = NULL;
135
- ac->sub.invalid.head = NULL;
136
- ac->sub.invalid.tail = NULL;
137
- ac->sub.channels = dictCreate(&callbackDict,NULL);
138
- ac->sub.patterns = dictCreate(&callbackDict,NULL);
139
- return ac;
140
- }
141
-
142
- /* We want the error field to be accessible directly instead of requiring
143
- * an indirection to the redisContext struct. */
144
- static void __redisAsyncCopyError(redisAsyncContext *ac) {
145
- if (!ac)
146
- return;
147
-
148
- redisContext *c = &(ac->c);
149
- ac->err = c->err;
150
- ac->errstr = c->errstr;
151
- }
152
-
153
- redisAsyncContext *redisAsyncConnect(const char *ip, int port) {
154
- redisContext *c;
155
- redisAsyncContext *ac;
156
-
157
- c = redisConnectNonBlock(ip,port);
158
- if (c == NULL)
159
- return NULL;
160
-
161
- ac = redisAsyncInitialize(c);
162
- if (ac == NULL) {
163
- redisFree(c);
164
- return NULL;
165
- }
166
-
167
- __redisAsyncCopyError(ac);
168
- return ac;
169
- }
170
-
171
- redisAsyncContext *redisAsyncConnectBind(const char *ip, int port,
172
- const char *source_addr) {
173
- redisContext *c = redisConnectBindNonBlock(ip,port,source_addr);
174
- redisAsyncContext *ac = redisAsyncInitialize(c);
175
- __redisAsyncCopyError(ac);
176
- return ac;
177
- }
178
-
179
- redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port,
180
- const char *source_addr) {
181
- redisContext *c = redisConnectBindNonBlockWithReuse(ip,port,source_addr);
182
- redisAsyncContext *ac = redisAsyncInitialize(c);
183
- __redisAsyncCopyError(ac);
184
- return ac;
185
- }
186
-
187
- redisAsyncContext *redisAsyncConnectUnix(const char *path) {
188
- redisContext *c;
189
- redisAsyncContext *ac;
190
-
191
- c = redisConnectUnixNonBlock(path);
192
- if (c == NULL)
193
- return NULL;
194
-
195
- ac = redisAsyncInitialize(c);
196
- if (ac == NULL) {
197
- redisFree(c);
198
- return NULL;
199
- }
200
-
201
- __redisAsyncCopyError(ac);
202
- return ac;
203
- }
204
-
205
- int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) {
206
- if (ac->onConnect == NULL) {
207
- ac->onConnect = fn;
208
-
209
- /* The common way to detect an established connection is to wait for
210
- * the first write event to be fired. This assumes the related event
211
- * library functions are already set. */
212
- _EL_ADD_WRITE(ac);
213
- return REDIS_OK;
214
- }
215
- return REDIS_ERR;
216
- }
217
-
218
- int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn) {
219
- if (ac->onDisconnect == NULL) {
220
- ac->onDisconnect = fn;
221
- return REDIS_OK;
222
- }
223
- return REDIS_ERR;
224
- }
225
-
226
- /* Helper functions to push/shift callbacks */
227
- static int __redisPushCallback(redisCallbackList *list, redisCallback *source) {
228
- redisCallback *cb;
229
-
230
- /* Copy callback from stack to heap */
231
- cb = malloc(sizeof(*cb));
232
- if (cb == NULL)
233
- return REDIS_ERR_OOM;
234
-
235
- if (source != NULL) {
236
- memcpy(cb,source,sizeof(*cb));
237
- cb->next = NULL;
238
- }
239
-
240
- /* Store callback in list */
241
- if (list->head == NULL)
242
- list->head = cb;
243
- if (list->tail != NULL)
244
- list->tail->next = cb;
245
- list->tail = cb;
246
- return REDIS_OK;
247
- }
248
-
249
- static int __redisShiftCallback(redisCallbackList *list, redisCallback *target) {
250
- redisCallback *cb = list->head;
251
- if (cb != NULL) {
252
- list->head = cb->next;
253
- if (cb == list->tail)
254
- list->tail = NULL;
255
-
256
- /* Copy callback from heap to stack */
257
- if (target != NULL)
258
- memcpy(target,cb,sizeof(*cb));
259
- free(cb);
260
- return REDIS_OK;
261
- }
262
- return REDIS_ERR;
263
- }
264
-
265
- static void __redisRunCallback(redisAsyncContext *ac, redisCallback *cb, redisReply *reply) {
266
- redisContext *c = &(ac->c);
267
- if (cb->fn != NULL) {
268
- c->flags |= REDIS_IN_CALLBACK;
269
- cb->fn(ac,reply,cb->privdata);
270
- c->flags &= ~REDIS_IN_CALLBACK;
271
- }
272
- }
273
-
274
- /* Helper function to free the context. */
275
- static void __redisAsyncFree(redisAsyncContext *ac) {
276
- redisContext *c = &(ac->c);
277
- redisCallback cb;
278
- dictIterator *it;
279
- dictEntry *de;
280
-
281
- /* Execute pending callbacks with NULL reply. */
282
- while (__redisShiftCallback(&ac->replies,&cb) == REDIS_OK)
283
- __redisRunCallback(ac,&cb,NULL);
284
-
285
- /* Execute callbacks for invalid commands */
286
- while (__redisShiftCallback(&ac->sub.invalid,&cb) == REDIS_OK)
287
- __redisRunCallback(ac,&cb,NULL);
288
-
289
- /* Run subscription callbacks callbacks with NULL reply */
290
- it = dictGetIterator(ac->sub.channels);
291
- while ((de = dictNext(it)) != NULL)
292
- __redisRunCallback(ac,dictGetEntryVal(de),NULL);
293
- dictReleaseIterator(it);
294
- dictRelease(ac->sub.channels);
295
-
296
- it = dictGetIterator(ac->sub.patterns);
297
- while ((de = dictNext(it)) != NULL)
298
- __redisRunCallback(ac,dictGetEntryVal(de),NULL);
299
- dictReleaseIterator(it);
300
- dictRelease(ac->sub.patterns);
301
-
302
- /* Signal event lib to clean up */
303
- _EL_CLEANUP(ac);
304
-
305
- /* Execute disconnect callback. When redisAsyncFree() initiated destroying
306
- * this context, the status will always be REDIS_OK. */
307
- if (ac->onDisconnect && (c->flags & REDIS_CONNECTED)) {
308
- if (c->flags & REDIS_FREEING) {
309
- ac->onDisconnect(ac,REDIS_OK);
310
- } else {
311
- ac->onDisconnect(ac,(ac->err == 0) ? REDIS_OK : REDIS_ERR);
312
- }
313
- }
314
-
315
- /* Cleanup self */
316
- redisFree(c);
317
- }
318
-
319
- /* Free the async context. When this function is called from a callback,
320
- * control needs to be returned to redisProcessCallbacks() before actual
321
- * free'ing. To do so, a flag is set on the context which is picked up by
322
- * redisProcessCallbacks(). Otherwise, the context is immediately free'd. */
323
- void redisAsyncFree(redisAsyncContext *ac) {
324
- redisContext *c = &(ac->c);
325
- c->flags |= REDIS_FREEING;
326
- if (!(c->flags & REDIS_IN_CALLBACK))
327
- __redisAsyncFree(ac);
328
- }
329
-
330
- /* Helper function to make the disconnect happen and clean up. */
331
- static void __redisAsyncDisconnect(redisAsyncContext *ac) {
332
- redisContext *c = &(ac->c);
333
-
334
- /* Make sure error is accessible if there is any */
335
- __redisAsyncCopyError(ac);
336
-
337
- if (ac->err == 0) {
338
- /* For clean disconnects, there should be no pending callbacks. */
339
- assert(__redisShiftCallback(&ac->replies,NULL) == REDIS_ERR);
340
- } else {
341
- /* Disconnection is caused by an error, make sure that pending
342
- * callbacks cannot call new commands. */
343
- c->flags |= REDIS_DISCONNECTING;
344
- }
345
-
346
- /* For non-clean disconnects, __redisAsyncFree() will execute pending
347
- * callbacks with a NULL-reply. */
348
- __redisAsyncFree(ac);
349
- }
350
-
351
- /* Tries to do a clean disconnect from Redis, meaning it stops new commands
352
- * from being issued, but tries to flush the output buffer and execute
353
- * callbacks for all remaining replies. When this function is called from a
354
- * callback, there might be more replies and we can safely defer disconnecting
355
- * to redisProcessCallbacks(). Otherwise, we can only disconnect immediately
356
- * when there are no pending callbacks. */
357
- void redisAsyncDisconnect(redisAsyncContext *ac) {
358
- redisContext *c = &(ac->c);
359
- c->flags |= REDIS_DISCONNECTING;
360
- if (!(c->flags & REDIS_IN_CALLBACK) && ac->replies.head == NULL)
361
- __redisAsyncDisconnect(ac);
362
- }
363
-
364
- static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply, redisCallback *dstcb) {
365
- redisContext *c = &(ac->c);
366
- dict *callbacks;
367
- dictEntry *de;
368
- int pvariant;
369
- char *stype;
370
- sds sname;
371
-
372
- /* Custom reply functions are not supported for pub/sub. This will fail
373
- * very hard when they are used... */
374
- if (reply->type == REDIS_REPLY_ARRAY) {
375
- assert(reply->elements >= 2);
376
- assert(reply->element[0]->type == REDIS_REPLY_STRING);
377
- stype = reply->element[0]->str;
378
- pvariant = (tolower(stype[0]) == 'p') ? 1 : 0;
379
-
380
- if (pvariant)
381
- callbacks = ac->sub.patterns;
382
- else
383
- callbacks = ac->sub.channels;
384
-
385
- /* Locate the right callback */
386
- assert(reply->element[1]->type == REDIS_REPLY_STRING);
387
- sname = sdsnewlen(reply->element[1]->str,reply->element[1]->len);
388
- de = dictFind(callbacks,sname);
389
- if (de != NULL) {
390
- memcpy(dstcb,dictGetEntryVal(de),sizeof(*dstcb));
391
-
392
- /* If this is an unsubscribe message, remove it. */
393
- if (strcasecmp(stype+pvariant,"unsubscribe") == 0) {
394
- dictDelete(callbacks,sname);
395
-
396
- /* If this was the last unsubscribe message, revert to
397
- * non-subscribe mode. */
398
- assert(reply->element[2]->type == REDIS_REPLY_INTEGER);
399
- if (reply->element[2]->integer == 0)
400
- c->flags &= ~REDIS_SUBSCRIBED;
401
- }
402
- }
403
- sdsfree(sname);
404
- } else {
405
- /* Shift callback for invalid commands. */
406
- __redisShiftCallback(&ac->sub.invalid,dstcb);
407
- }
408
- return REDIS_OK;
409
- }
410
-
411
- void redisProcessCallbacks(redisAsyncContext *ac) {
412
- redisContext *c = &(ac->c);
413
- redisCallback cb = {NULL, NULL, NULL};
414
- void *reply = NULL;
415
- int status;
416
-
417
- while((status = redisGetReply(c,&reply)) == REDIS_OK) {
418
- if (reply == NULL) {
419
- /* When the connection is being disconnected and there are
420
- * no more replies, this is the cue to really disconnect. */
421
- if (c->flags & REDIS_DISCONNECTING && sdslen(c->obuf) == 0) {
422
- __redisAsyncDisconnect(ac);
423
- return;
424
- }
425
-
426
- /* If monitor mode, repush callback */
427
- if(c->flags & REDIS_MONITORING) {
428
- __redisPushCallback(&ac->replies,&cb);
429
- }
430
-
431
- /* When the connection is not being disconnected, simply stop
432
- * trying to get replies and wait for the next loop tick. */
433
- break;
434
- }
435
-
436
- /* Even if the context is subscribed, pending regular callbacks will
437
- * get a reply before pub/sub messages arrive. */
438
- if (__redisShiftCallback(&ac->replies,&cb) != REDIS_OK) {
439
- /*
440
- * A spontaneous reply in a not-subscribed context can be the error
441
- * reply that is sent when a new connection exceeds the maximum
442
- * number of allowed connections on the server side.
443
- *
444
- * This is seen as an error instead of a regular reply because the
445
- * server closes the connection after sending it.
446
- *
447
- * To prevent the error from being overwritten by an EOF error the
448
- * connection is closed here. See issue #43.
449
- *
450
- * Another possibility is that the server is loading its dataset.
451
- * In this case we also want to close the connection, and have the
452
- * user wait until the server is ready to take our request.
453
- */
454
- if (((redisReply*)reply)->type == REDIS_REPLY_ERROR) {
455
- c->err = REDIS_ERR_OTHER;
456
- snprintf(c->errstr,sizeof(c->errstr),"%s",((redisReply*)reply)->str);
457
- __redisAsyncDisconnect(ac);
458
- return;
459
- }
460
- /* No more regular callbacks and no errors, the context *must* be subscribed or monitoring. */
461
- assert((c->flags & REDIS_SUBSCRIBED || c->flags & REDIS_MONITORING));
462
- if(c->flags & REDIS_SUBSCRIBED)
463
- __redisGetSubscribeCallback(ac,reply,&cb);
464
- }
465
-
466
- if (cb.fn != NULL) {
467
- __redisRunCallback(ac,&cb,reply);
468
- c->reader->fn->freeObject(reply);
469
-
470
- /* Proceed with free'ing when redisAsyncFree() was called. */
471
- if (c->flags & REDIS_FREEING) {
472
- __redisAsyncFree(ac);
473
- return;
474
- }
475
- } else {
476
- /* No callback for this reply. This can either be a NULL callback,
477
- * or there were no callbacks to begin with. Either way, don't
478
- * abort with an error, but simply ignore it because the client
479
- * doesn't know what the server will spit out over the wire. */
480
- c->reader->fn->freeObject(reply);
481
- }
482
- }
483
-
484
- /* Disconnect when there was an error reading the reply */
485
- if (status != REDIS_OK)
486
- __redisAsyncDisconnect(ac);
487
- }
488
-
489
- /* Internal helper function to detect socket status the first time a read or
490
- * write event fires. When connecting was not succesful, the connect callback
491
- * is called with a REDIS_ERR status and the context is free'd. */
492
- static int __redisAsyncHandleConnect(redisAsyncContext *ac) {
493
- redisContext *c = &(ac->c);
494
-
495
- if (redisCheckSocketError(c) == REDIS_ERR) {
496
- /* Try again later when connect(2) is still in progress. */
497
- if (errno == EINPROGRESS)
498
- return REDIS_OK;
499
-
500
- if (ac->onConnect) ac->onConnect(ac,REDIS_ERR);
501
- __redisAsyncDisconnect(ac);
502
- return REDIS_ERR;
503
- }
504
-
505
- /* Mark context as connected. */
506
- c->flags |= REDIS_CONNECTED;
507
- if (ac->onConnect) ac->onConnect(ac,REDIS_OK);
508
- return REDIS_OK;
509
- }
510
-
511
- /* This function should be called when the socket is readable.
512
- * It processes all replies that can be read and executes their callbacks.
513
- */
514
- void redisAsyncHandleRead(redisAsyncContext *ac) {
515
- redisContext *c = &(ac->c);
516
-
517
- if (!(c->flags & REDIS_CONNECTED)) {
518
- /* Abort connect was not successful. */
519
- if (__redisAsyncHandleConnect(ac) != REDIS_OK)
520
- return;
521
- /* Try again later when the context is still not connected. */
522
- if (!(c->flags & REDIS_CONNECTED))
523
- return;
524
- }
525
-
526
- if (redisBufferRead(c) == REDIS_ERR) {
527
- __redisAsyncDisconnect(ac);
528
- } else {
529
- /* Always re-schedule reads */
530
- _EL_ADD_READ(ac);
531
- redisProcessCallbacks(ac);
532
- }
533
- }
534
-
535
- void redisAsyncHandleWrite(redisAsyncContext *ac) {
536
- redisContext *c = &(ac->c);
537
- int done = 0;
538
-
539
- if (!(c->flags & REDIS_CONNECTED)) {
540
- /* Abort connect was not successful. */
541
- if (__redisAsyncHandleConnect(ac) != REDIS_OK)
542
- return;
543
- /* Try again later when the context is still not connected. */
544
- if (!(c->flags & REDIS_CONNECTED))
545
- return;
546
- }
547
-
548
- if (redisBufferWrite(c,&done) == REDIS_ERR) {
549
- __redisAsyncDisconnect(ac);
550
- } else {
551
- /* Continue writing when not done, stop writing otherwise */
552
- if (!done)
553
- _EL_ADD_WRITE(ac);
554
- else
555
- _EL_DEL_WRITE(ac);
556
-
557
- /* Always schedule reads after writes */
558
- _EL_ADD_READ(ac);
559
- }
560
- }
561
-
562
- /* Sets a pointer to the first argument and its length starting at p. Returns
563
- * the number of bytes to skip to get to the following argument. */
564
- static const char *nextArgument(const char *start, const char **str, size_t *len) {
565
- const char *p = start;
566
- if (p[0] != '$') {
567
- p = strchr(p,'$');
568
- if (p == NULL) return NULL;
569
- }
570
-
571
- *len = (int)strtol(p+1,NULL,10);
572
- p = strchr(p,'\r');
573
- assert(p);
574
- *str = p+2;
575
- return p+2+(*len)+2;
576
- }
577
-
578
- /* Helper function for the redisAsyncCommand* family of functions. Writes a
579
- * formatted command to the output buffer and registers the provided callback
580
- * function with the context. */
581
- static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) {
582
- redisContext *c = &(ac->c);
583
- redisCallback cb;
584
- int pvariant, hasnext;
585
- const char *cstr, *astr;
586
- size_t clen, alen;
587
- const char *p;
588
- sds sname;
589
- int ret;
590
-
591
- /* Don't accept new commands when the connection is about to be closed. */
592
- if (c->flags & (REDIS_DISCONNECTING | REDIS_FREEING)) return REDIS_ERR;
593
-
594
- /* Setup callback */
595
- cb.fn = fn;
596
- cb.privdata = privdata;
597
-
598
- /* Find out which command will be appended. */
599
- p = nextArgument(cmd,&cstr,&clen);
600
- assert(p != NULL);
601
- hasnext = (p[0] == '$');
602
- pvariant = (tolower(cstr[0]) == 'p') ? 1 : 0;
603
- cstr += pvariant;
604
- clen -= pvariant;
605
-
606
- if (hasnext && strncasecmp(cstr,"subscribe\r\n",11) == 0) {
607
- c->flags |= REDIS_SUBSCRIBED;
608
-
609
- /* Add every channel/pattern to the list of subscription callbacks. */
610
- while ((p = nextArgument(p,&astr,&alen)) != NULL) {
611
- sname = sdsnewlen(astr,alen);
612
- if (pvariant)
613
- ret = dictReplace(ac->sub.patterns,sname,&cb);
614
- else
615
- ret = dictReplace(ac->sub.channels,sname,&cb);
616
-
617
- if (ret == 0) sdsfree(sname);
618
- }
619
- } else if (strncasecmp(cstr,"unsubscribe\r\n",13) == 0) {
620
- /* It is only useful to call (P)UNSUBSCRIBE when the context is
621
- * subscribed to one or more channels or patterns. */
622
- if (!(c->flags & REDIS_SUBSCRIBED)) return REDIS_ERR;
623
-
624
- /* (P)UNSUBSCRIBE does not have its own response: every channel or
625
- * pattern that is unsubscribed will receive a message. This means we
626
- * should not append a callback function for this command. */
627
- } else if(strncasecmp(cstr,"monitor\r\n",9) == 0) {
628
- /* Set monitor flag and push callback */
629
- c->flags |= REDIS_MONITORING;
630
- __redisPushCallback(&ac->replies,&cb);
631
- } else {
632
- if (c->flags & REDIS_SUBSCRIBED)
633
- /* This will likely result in an error reply, but it needs to be
634
- * received and passed to the callback. */
635
- __redisPushCallback(&ac->sub.invalid,&cb);
636
- else
637
- __redisPushCallback(&ac->replies,&cb);
638
- }
639
-
640
- __redisAppendCommand(c,cmd,len);
641
-
642
- /* Always schedule a write when the write buffer is non-empty */
643
- _EL_ADD_WRITE(ac);
644
-
645
- return REDIS_OK;
646
- }
647
-
648
- int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap) {
649
- char *cmd;
650
- int len;
651
- int status;
652
- len = redisvFormatCommand(&cmd,format,ap);
653
-
654
- /* We don't want to pass -1 or -2 to future functions as a length. */
655
- if (len < 0)
656
- return REDIS_ERR;
657
-
658
- status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
659
- free(cmd);
660
- return status;
661
- }
662
-
663
- int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...) {
664
- va_list ap;
665
- int status;
666
- va_start(ap,format);
667
- status = redisvAsyncCommand(ac,fn,privdata,format,ap);
668
- va_end(ap);
669
- return status;
670
- }
671
-
672
- int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen) {
673
- sds cmd;
674
- int len;
675
- int status;
676
- len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen);
677
- status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
678
- sdsfree(cmd);
679
- return status;
680
- }
681
-
682
- int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) {
683
- int status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
684
- return status;
685
- }