1
/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
/* $Id: items.c,v 1.21 2004/03/31 05:01:51 avva Exp $ */
7
#include <sys/socket.h>
8
#include <sys/signal.h>
9
#include <sys/resource.h>
15
#include <netinet/in.h>
21
#include "memcached.h"
25
* NOTE: we assume here for simplicity that slab ids are <=32. That's true in
26
* the powers-of-2 implementation, but if that changes this should be changed too
30
static item *heads[LARGEST_ID];
31
static item *tails[LARGEST_ID];
32
unsigned int sizes[LARGEST_ID];
34
void item_init(void) {
36
for(i=0; i<LARGEST_ID; i++) {
44
item *item_alloc(char *key, int flags, time_t exptime, int nbytes) {
49
len = strlen(key) + 1; if(len % 4) len += 4 - (len % 4);
50
ntotal = sizeof(item) + len + nbytes;
52
id = slabs_clsid(ntotal);
56
it = slabs_alloc(ntotal);
61
/* If requested to not push old items out of cache when memory runs out,
62
* we're out of luck at this point...
65
if (!settings.evict_to_free) return 0;
68
* try to get one off the right LRU
69
* don't necessariuly unlink the tail because it may be locked: refcount>0
70
* search up from tail an item with refcount==0 and unlink it; give up after 50
74
if (id > LARGEST_ID) return 0;
75
if (tails[id]==0) return 0;
77
for (search = tails[id]; tries>0 && search; tries--, search=search->prev) {
78
if (search->refcount==0) {
83
it = slabs_alloc(ntotal);
87
assert(it->slabs_clsid == 0);
91
assert(it != heads[it->slabs_clsid]);
93
it->next = it->prev = it->h_next = 0;
98
strcpy(ITEM_key(it), key);
99
it->exptime = exptime;
104
void item_free(item *it) {
105
unsigned int ntotal = ITEM_ntotal(it);
106
assert((it->it_flags & ITEM_LINKED) == 0);
107
assert(it != heads[it->slabs_clsid]);
108
assert(it != tails[it->slabs_clsid]);
109
assert(it->refcount == 0);
111
/* so slab size changer can tell later if item is already free or not */
113
it->it_flags |= ITEM_SLABBED;
114
slabs_free(it, ntotal);
117
void item_link_q(item *it) { /* item is the new head */
119
assert(it->slabs_clsid <= LARGEST_ID);
120
assert((it->it_flags & ITEM_SLABBED) == 0);
122
head = &heads[it->slabs_clsid];
123
tail = &tails[it->slabs_clsid];
125
assert((*head && *tail) || (*head == 0 && *tail == 0));
128
if (it->next) it->next->prev = it;
130
if (*tail == 0) *tail = it;
131
sizes[it->slabs_clsid]++;
135
void item_unlink_q(item *it) {
137
assert(it->slabs_clsid <= LARGEST_ID);
138
head = &heads[it->slabs_clsid];
139
tail = &tails[it->slabs_clsid];
142
assert(it->prev == 0);
146
assert(it->next == 0);
149
assert(it->next != it);
150
assert(it->prev != it);
152
if (it->next) it->next->prev = it->prev;
153
if (it->prev) it->prev->next = it->next;
154
sizes[it->slabs_clsid]--;
158
int item_link(item *it) {
159
assert((it->it_flags & (ITEM_LINKED|ITEM_SLABBED)) == 0);
160
assert(it->nbytes < 1048576);
161
it->it_flags |= ITEM_LINKED;
163
assoc_insert(ITEM_key(it), it);
165
stats.curr_bytes += ITEM_ntotal(it);
166
stats.curr_items += 1;
167
stats.total_items += 1;
174
void item_unlink(item *it) {
175
if (it->it_flags & ITEM_LINKED) {
176
it->it_flags &= ~ITEM_LINKED;
177
stats.curr_bytes -= ITEM_ntotal(it);
178
stats.curr_items -= 1;
179
assoc_delete(ITEM_key(it));
182
if (it->refcount == 0) item_free(it);
185
void item_remove(item *it) {
186
assert((it->it_flags & ITEM_SLABBED) == 0);
187
if (it->refcount) it->refcount--;
188
assert((it->it_flags & ITEM_DELETED) == 0 || it->refcount);
189
if (it->refcount == 0 && (it->it_flags & ITEM_LINKED) == 0) {
194
void item_update(item *it) {
195
assert((it->it_flags & ITEM_SLABBED) == 0);
202
int item_replace(item *it, item *new_it) {
203
assert((it->it_flags & ITEM_SLABBED) == 0);
206
return item_link(new_it);
209
char *item_cachedump(unsigned int slabs_clsid, unsigned int limit, unsigned int *bytes) {
211
int memlimit = 2*1024*1024;
219
if (slabs_clsid > LARGEST_ID) return 0;
220
it = heads[slabs_clsid];
222
buffer = malloc(memlimit);
223
if (buffer == 0) return 0;
227
if(limit && shown >=limit)
231
sprintf(temp, "ITEM %s [%u b; %lu s]\r\n", ITEM_key(it), it->nbytes - 2, it->time);
233
if (bufcurr + len +5 > memlimit) /* 5 is END\r\n */
235
strcpy(buffer + bufcurr, temp);
241
strcpy(buffer+bufcurr, "END\r\n");
248
void item_stats(char *buffer, int buflen) {
250
char *bufcurr = buffer;
251
time_t now = time(0);
254
strcpy(buffer, "SERVER_ERROR out of memory");
258
for (i=0; i<LARGEST_ID; i++) {
260
bufcurr += sprintf(bufcurr, "STAT items:%u:number %u\r\nSTAT items:%u:age %lu\r\n",
261
i, sizes[i], i, now - tails[i]->time);
263
strcpy(bufcurr, "END");
267
/* dumps out a list of objects of each size, with granularity of 32 bytes */
268
char* item_stats_sizes(int *bytes) {
269
int num_buckets = 32768; /* max 1MB object, divided into 32 bytes size buckets */
270
unsigned int *histogram = (int*) malloc(num_buckets * sizeof(int));
271
char *buf = (char*) malloc(1024*1024*2*sizeof(char));
274
if (histogram == 0 || buf == 0) {
275
if (histogram) free(histogram);
280
/* build the histogram */
281
memset(buf, 0, num_buckets * sizeof(int));
282
for (i=0; i<LARGEST_ID; i++) {
283
item *iter = heads[i];
285
int ntotal = ITEM_ntotal(iter);
286
int bucket = ntotal / 32;
287
if (ntotal % 32) bucket++;
288
if (bucket < num_buckets) histogram[bucket]++;
293
/* write the buffer */
295
for (i=0; i<num_buckets; i++) {
297
*bytes += sprintf(&buf[*bytes], "%u %u\r\n", i*32, histogram[i]);
300
*bytes += sprintf(&buf[*bytes], "END\r\n");