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