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