]> git.saurik.com Git - redis.git/blame - src/object.c
vm_blocked_clients count fixed in INFO, thanks to Pietern Noordhuis
[redis.git] / src / object.c
CommitLineData
e2641e09 1#include "redis.h"
2#include <pthread.h>
3
4robj *createObject(int type, void *ptr) {
5 robj *o;
6
7 if (server.vm_enabled) pthread_mutex_lock(&server.obj_freelist_mutex);
8 if (listLength(server.objfreelist)) {
9 listNode *head = listFirst(server.objfreelist);
10 o = listNodeValue(head);
11 listDelNode(server.objfreelist,head);
12 if (server.vm_enabled) pthread_mutex_unlock(&server.obj_freelist_mutex);
13 } else {
2cffe299 14 if (server.vm_enabled) pthread_mutex_unlock(&server.obj_freelist_mutex);
e2641e09 15 o = zmalloc(sizeof(*o));
16 }
17 o->type = type;
18 o->encoding = REDIS_ENCODING_RAW;
19 o->ptr = ptr;
20 o->refcount = 1;
21 if (server.vm_enabled) {
22 /* Note that this code may run in the context of an I/O thread
23 * and accessing server.lruclock in theory is an error
24 * (no locks). But in practice this is safe, and even if we read
25 * garbage Redis will not fail. */
26 o->lru = server.lruclock;
27 o->storage = REDIS_VM_MEMORY;
28 }
29 return o;
30}
31
32robj *createStringObject(char *ptr, size_t len) {
33 return createObject(REDIS_STRING,sdsnewlen(ptr,len));
34}
35
36robj *createStringObjectFromLongLong(long long value) {
37 robj *o;
38 if (value >= 0 && value < REDIS_SHARED_INTEGERS) {
39 incrRefCount(shared.integers[value]);
40 o = shared.integers[value];
41 } else {
42 if (value >= LONG_MIN && value <= LONG_MAX) {
43 o = createObject(REDIS_STRING, NULL);
44 o->encoding = REDIS_ENCODING_INT;
45 o->ptr = (void*)((long)value);
46 } else {
47 o = createObject(REDIS_STRING,sdsfromlonglong(value));
48 }
49 }
50 return o;
51}
52
53robj *dupStringObject(robj *o) {
54 redisAssert(o->encoding == REDIS_ENCODING_RAW);
55 return createStringObject(o->ptr,sdslen(o->ptr));
56}
57
58robj *createListObject(void) {
59 list *l = listCreate();
60 robj *o = createObject(REDIS_LIST,l);
61 listSetFreeMethod(l,decrRefCount);
62 o->encoding = REDIS_ENCODING_LINKEDLIST;
63 return o;
64}
65
66robj *createZiplistObject(void) {
67 unsigned char *zl = ziplistNew();
68 robj *o = createObject(REDIS_LIST,zl);
69 o->encoding = REDIS_ENCODING_ZIPLIST;
70 return o;
71}
72
73robj *createSetObject(void) {
74 dict *d = dictCreate(&setDictType,NULL);
75 return createObject(REDIS_SET,d);
76}
77
78robj *createHashObject(void) {
79 /* All the Hashes start as zipmaps. Will be automatically converted
80 * into hash tables if there are enough elements or big elements
81 * inside. */
82 unsigned char *zm = zipmapNew();
83 robj *o = createObject(REDIS_HASH,zm);
84 o->encoding = REDIS_ENCODING_ZIPMAP;
85 return o;
86}
87
88robj *createZsetObject(void) {
89 zset *zs = zmalloc(sizeof(*zs));
90
91 zs->dict = dictCreate(&zsetDictType,NULL);
92 zs->zsl = zslCreate();
93 return createObject(REDIS_ZSET,zs);
94}
95
96void freeStringObject(robj *o) {
97 if (o->encoding == REDIS_ENCODING_RAW) {
98 sdsfree(o->ptr);
99 }
100}
101
102void freeListObject(robj *o) {
103 switch (o->encoding) {
104 case REDIS_ENCODING_LINKEDLIST:
105 listRelease((list*) o->ptr);
106 break;
107 case REDIS_ENCODING_ZIPLIST:
108 zfree(o->ptr);
109 break;
110 default:
111 redisPanic("Unknown list encoding type");
112 }
113}
114
115void freeSetObject(robj *o) {
116 dictRelease((dict*) o->ptr);
117}
118
119void freeZsetObject(robj *o) {
120 zset *zs = o->ptr;
121
122 dictRelease(zs->dict);
123 zslFree(zs->zsl);
124 zfree(zs);
125}
126
127void freeHashObject(robj *o) {
128 switch (o->encoding) {
129 case REDIS_ENCODING_HT:
130 dictRelease((dict*) o->ptr);
131 break;
132 case REDIS_ENCODING_ZIPMAP:
133 zfree(o->ptr);
134 break;
135 default:
136 redisPanic("Unknown hash encoding type");
137 break;
138 }
139}
140
141void incrRefCount(robj *o) {
142 o->refcount++;
143}
144
145void decrRefCount(void *obj) {
146 robj *o = obj;
147
148 /* Object is a swapped out value, or in the process of being loaded. */
149 if (server.vm_enabled &&
150 (o->storage == REDIS_VM_SWAPPED || o->storage == REDIS_VM_LOADING))
151 {
152 vmpointer *vp = obj;
153 if (o->storage == REDIS_VM_LOADING) vmCancelThreadedIOJob(o);
154 vmMarkPagesFree(vp->page,vp->usedpages);
155 server.vm_stats_swapped_objects--;
156 zfree(vp);
157 return;
158 }
159
160 if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0");
161 /* Object is in memory, or in the process of being swapped out.
162 *
163 * If the object is being swapped out, abort the operation on
164 * decrRefCount even if the refcount does not drop to 0: the object
165 * is referenced at least two times, as value of the key AND as
166 * job->val in the iojob. So if we don't invalidate the iojob, when it is
167 * done but the relevant key was removed in the meantime, the
168 * complete jobs handler will not find the key about the job and the
169 * assert will fail. */
170 if (server.vm_enabled && o->storage == REDIS_VM_SWAPPING)
171 vmCancelThreadedIOJob(o);
172 if (--(o->refcount) == 0) {
173 switch(o->type) {
174 case REDIS_STRING: freeStringObject(o); break;
175 case REDIS_LIST: freeListObject(o); break;
176 case REDIS_SET: freeSetObject(o); break;
177 case REDIS_ZSET: freeZsetObject(o); break;
178 case REDIS_HASH: freeHashObject(o); break;
179 default: redisPanic("Unknown object type"); break;
180 }
181 if (server.vm_enabled) pthread_mutex_lock(&server.obj_freelist_mutex);
182 if (listLength(server.objfreelist) > REDIS_OBJFREELIST_MAX ||
183 !listAddNodeHead(server.objfreelist,o))
184 zfree(o);
185 if (server.vm_enabled) pthread_mutex_unlock(&server.obj_freelist_mutex);
186 }
187}
188
189int checkType(redisClient *c, robj *o, int type) {
190 if (o->type != type) {
191 addReply(c,shared.wrongtypeerr);
192 return 1;
193 }
194 return 0;
195}
196
197/* Try to encode a string object in order to save space */
198robj *tryObjectEncoding(robj *o) {
199 long value;
200 sds s = o->ptr;
201
202 if (o->encoding != REDIS_ENCODING_RAW)
203 return o; /* Already encoded */
204
205 /* It's not safe to encode shared objects: shared objects can be shared
206 * everywhere in the "object space" of Redis. Encoded objects can only
207 * appear as "values" (and not, for instance, as keys) */
208 if (o->refcount > 1) return o;
209
210 /* Currently we try to encode only strings */
211 redisAssert(o->type == REDIS_STRING);
212
213 /* Check if we can represent this string as a long integer */
214 if (isStringRepresentableAsLong(s,&value) == REDIS_ERR) return o;
215
216 /* Ok, this object can be encoded */
217 if (value >= 0 && value < REDIS_SHARED_INTEGERS) {
218 decrRefCount(o);
219 incrRefCount(shared.integers[value]);
220 return shared.integers[value];
221 } else {
222 o->encoding = REDIS_ENCODING_INT;
223 sdsfree(o->ptr);
224 o->ptr = (void*) value;
225 return o;
226 }
227}
228
229/* Get a decoded version of an encoded object (returned as a new object).
230 * If the object is already raw-encoded just increment the ref count. */
231robj *getDecodedObject(robj *o) {
232 robj *dec;
233
234 if (o->encoding == REDIS_ENCODING_RAW) {
235 incrRefCount(o);
236 return o;
237 }
238 if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_INT) {
239 char buf[32];
240
241 ll2string(buf,32,(long)o->ptr);
242 dec = createStringObject(buf,strlen(buf));
243 return dec;
244 } else {
245 redisPanic("Unknown encoding type");
246 }
247}
248
249/* Compare two string objects via strcmp() or alike.
250 * Note that the objects may be integer-encoded. In such a case we
251 * use ll2string() to get a string representation of the numbers on the stack
252 * and compare the strings, it's much faster than calling getDecodedObject().
253 *
254 * Important note: if objects are not integer encoded, but binary-safe strings,
255 * sdscmp() from sds.c will apply memcmp() so this function ca be considered
256 * binary safe. */
257int compareStringObjects(robj *a, robj *b) {
258 redisAssert(a->type == REDIS_STRING && b->type == REDIS_STRING);
259 char bufa[128], bufb[128], *astr, *bstr;
260 int bothsds = 1;
261
262 if (a == b) return 0;
263 if (a->encoding != REDIS_ENCODING_RAW) {
264 ll2string(bufa,sizeof(bufa),(long) a->ptr);
265 astr = bufa;
266 bothsds = 0;
267 } else {
268 astr = a->ptr;
269 }
270 if (b->encoding != REDIS_ENCODING_RAW) {
271 ll2string(bufb,sizeof(bufb),(long) b->ptr);
272 bstr = bufb;
273 bothsds = 0;
274 } else {
275 bstr = b->ptr;
276 }
277 return bothsds ? sdscmp(astr,bstr) : strcmp(astr,bstr);
278}
279
280/* Equal string objects return 1 if the two objects are the same from the
281 * point of view of a string comparison, otherwise 0 is returned. Note that
282 * this function is faster then checking for (compareStringObject(a,b) == 0)
283 * because it can perform some more optimization. */
284int equalStringObjects(robj *a, robj *b) {
285 if (a->encoding != REDIS_ENCODING_RAW && b->encoding != REDIS_ENCODING_RAW){
286 return a->ptr == b->ptr;
287 } else {
288 return compareStringObjects(a,b) == 0;
289 }
290}
291
292size_t stringObjectLen(robj *o) {
293 redisAssert(o->type == REDIS_STRING);
294 if (o->encoding == REDIS_ENCODING_RAW) {
295 return sdslen(o->ptr);
296 } else {
297 char buf[32];
298
299 return ll2string(buf,32,(long)o->ptr);
300 }
301}
302
303int getDoubleFromObject(robj *o, double *target) {
304 double value;
305 char *eptr;
306
307 if (o == NULL) {
308 value = 0;
309 } else {
310 redisAssert(o->type == REDIS_STRING);
311 if (o->encoding == REDIS_ENCODING_RAW) {
312 value = strtod(o->ptr, &eptr);
313 if (eptr[0] != '\0') return REDIS_ERR;
314 } else if (o->encoding == REDIS_ENCODING_INT) {
315 value = (long)o->ptr;
316 } else {
317 redisPanic("Unknown string encoding");
318 }
319 }
320
321 *target = value;
322 return REDIS_OK;
323}
324
325int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg) {
326 double value;
327 if (getDoubleFromObject(o, &value) != REDIS_OK) {
328 if (msg != NULL) {
329 addReplySds(c, sdscatprintf(sdsempty(), "-ERR %s\r\n", msg));
330 } else {
331 addReplySds(c, sdsnew("-ERR value is not a double\r\n"));
332 }
333 return REDIS_ERR;
334 }
335
336 *target = value;
337 return REDIS_OK;
338}
339
340int getLongLongFromObject(robj *o, long long *target) {
341 long long value;
342 char *eptr;
343
344 if (o == NULL) {
345 value = 0;
346 } else {
347 redisAssert(o->type == REDIS_STRING);
348 if (o->encoding == REDIS_ENCODING_RAW) {
349 value = strtoll(o->ptr, &eptr, 10);
350 if (eptr[0] != '\0') return REDIS_ERR;
351 } else if (o->encoding == REDIS_ENCODING_INT) {
352 value = (long)o->ptr;
353 } else {
354 redisPanic("Unknown string encoding");
355 }
356 }
357
358 *target = value;
359 return REDIS_OK;
360}
361
362int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg) {
363 long long value;
364 if (getLongLongFromObject(o, &value) != REDIS_OK) {
365 if (msg != NULL) {
366 addReplySds(c, sdscatprintf(sdsempty(), "-ERR %s\r\n", msg));
367 } else {
368 addReplySds(c, sdsnew("-ERR value is not an integer\r\n"));
369 }
370 return REDIS_ERR;
371 }
372
373 *target = value;
374 return REDIS_OK;
375}
376
377int getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *msg) {
378 long long value;
379
380 if (getLongLongFromObjectOrReply(c, o, &value, msg) != REDIS_OK) return REDIS_ERR;
381 if (value < LONG_MIN || value > LONG_MAX) {
382 if (msg != NULL) {
383 addReplySds(c, sdscatprintf(sdsempty(), "-ERR %s\r\n", msg));
384 } else {
385 addReplySds(c, sdsnew("-ERR value is out of range\r\n"));
386 }
387 return REDIS_ERR;
388 }
389
390 *target = value;
391 return REDIS_OK;
392}
393
394char *strEncoding(int encoding) {
395 switch(encoding) {
396 case REDIS_ENCODING_RAW: return "raw";
397 case REDIS_ENCODING_INT: return "int";
398 case REDIS_ENCODING_HT: return "hashtable";
399 case REDIS_ENCODING_ZIPMAP: return "zipmap";
400 case REDIS_ENCODING_LINKEDLIST: return "linkedlist";
401 case REDIS_ENCODING_ZIPLIST: return "ziplist";
402 default: return "unknown";
403 }
404}