0
/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
0
#include <sys/socket.h>
0
#include <sys/signal.h>
0
#include <sys/resource.h>
0
-#include <netinet/in.h>
0
/* Forward Declarations */
0
static void item_link_q(item *it);
0
static void item_unlink_q(item *it);
0
@@ -44,6 +39,17 @@ void item_init(void) {
0
+/* Enable this for reference-count debugging. */
0
+# define DEBUG_REFCNT(it,op) \
0
+ fprintf(stderr, "item %x refcnt(%c) %d %c%c%c\n", \
0
+ it, op, it->refcount, \
0
+ (it->it_flags & ITEM_LINKED) ? 'L' : ' ', \
0
+ (it->it_flags & ITEM_SLABBED) ? 'S' : ' ', \
0
+ (it->it_flags & ITEM_DELETED) ? 'D' : ' ')
0
+# define DEBUG_REFCNT(it,op) while(0)
0
* Generates the variable-sized part of the header for an object.
0
@@ -65,7 +71,7 @@ static size_t item_make_header(const uint8_t nkey, const int flags, const int nb
0
-item *
item_alloc(char *key, const size_t nkey, const int flags, const rel_time_t exptime, const int nbytes) {
0
+item *
do_item_alloc(char *key, const size_t nkey, const int flags, const rel_time_t exptime, const int nbytes) {
0
@@ -98,9 +104,12 @@ item *item_alloc(char *key, const size_t nkey, const int flags, const rel_time_t
0
for (search = tails[id]; tries > 0 && search != NULL; tries--, search=search->prev) {
0
if (search->refcount == 0) {
0
- if (search->exptime > current_time)
0
+ if (search->exptime > current_time) {
0
+ do_item_unlink(search);
0
@@ -115,7 +124,8 @@ item *item_alloc(char *key, const size_t nkey, const int flags, const rel_time_t
0
assert(it != heads[it->slabs_clsid]);
0
it->next = it->prev = it->h_next = 0;
0
+ it->refcount = 1; /* the caller will have a reference */
0
+ DEBUG_REFCNT(it, '*');
0
@@ -136,6 +146,7 @@ void item_free(item *it) {
0
/* so slab size changer can tell later if item is already free or not */
0
it->it_flags |= ITEM_SLABBED;
0
+ DEBUG_REFCNT(it, 'F');
0
slabs_free(it, ntotal);
0
@@ -192,57 +203,66 @@ static void item_unlink_q(item *it) {
0
-int
item_link(item *it) {
0
+int
do_item_link(item *it) {
0
assert((it->it_flags & (ITEM_LINKED|ITEM_SLABBED)) == 0);
0
assert(it->nbytes < 1048576);
0
it->it_flags |= ITEM_LINKED;
0
it->time = current_time;
0
stats.curr_bytes += ITEM_ntotal(it);
0
stats.total_items += 1;
0
-void
item_unlink(item *it) {
0
+void
do_item_unlink(item *it) {
0
if ((it->it_flags & ITEM_LINKED) != 0) {
0
it->it_flags &= ~ITEM_LINKED;
0
stats.curr_bytes -= ITEM_ntotal(it);
0
assoc_delete(ITEM_key(it), it->nkey);
0
+ if (it->refcount == 0) item_free(it);
0
- if (it->refcount == 0) item_free(it);
0
-void
item_remove(item *it) {
0
+void
do_item_remove(item *it) {
0
assert((it->it_flags & ITEM_SLABBED) == 0);
0
- if (it->refcount != 0) it->refcount--;
0
+ if (it->refcount != 0) {
0
+ DEBUG_REFCNT(it, '-');
0
assert((it->it_flags & ITEM_DELETED) == 0 || it->refcount != 0);
0
if (it->refcount == 0 && (it->it_flags & ITEM_LINKED) == 0) {
0
-void
item_update(item *it) {
0
+void
do_item_update(item *it) {
0
if (it->time < current_time - ITEM_UPDATE_INTERVAL) {
0
assert((it->it_flags & ITEM_SLABBED) == 0);
0
- it->time = current_time;
0
+ if (it->it_flags & ITEM_LINKED) {
0
+ it->time = current_time;
0
-int
item_replace(item *it, item *new_it) {
0
+int
do_item_replace(item *it, item *new_it) {
0
assert((it->it_flags & ITEM_SLABBED) == 0);
0
- return item_link(new_it);
0
+ return do_item_link(new_it);
0
@@ -337,8 +357,59 @@ char* item_stats_sizes(int *bytes) {
0
+/* returns true if a deleted item's delete-locked-time is over, and it
0
+ should be removed from the namespace */
0
+int item_delete_lock_over (item *it) {
0
+ assert(it->it_flags & ITEM_DELETED);
0
+ return (current_time >= it->exptime);
0
+/* wrapper around assoc_find which does the lazy expiration/deletion logic */
0
+item *do_item_get_notedeleted(char *key, size_t nkey, int *delete_locked) {
0
+ item *it = assoc_find(key, nkey);
0
+ if (delete_locked) *delete_locked = 0;
0
+ if (it && (it->it_flags & ITEM_DELETED)) {
0
+ /* it's flagged as delete-locked. let's see if that condition
0
+ is past due, and the 5-second delete_timer just hasn't
0
+ gotten to it yet... */
0
+ if (! item_delete_lock_over(it)) {
0
+ if (delete_locked) *delete_locked = 1;
0
+ if (it && settings.oldest_live && settings.oldest_live <= current_time &&
0
+ it->time <= settings.oldest_live) {
0
+ do_item_unlink(it); // MTSAFE - cache_lock held
0
+ if (it && it->exptime && it->exptime <= current_time) {
0
+ do_item_unlink(it); // MTSAFE - cache_lock held
0
+ DEBUG_REFCNT(it, '+');
0
+item *item_get(char *key, size_t nkey) {
0
+ return item_get_notedeleted(key, nkey, 0);
0
+/* returns an item whether or not it's delete-locked or expired. */
0
+item *do_item_get_nocheck(char *key, size_t nkey) {
0
+ item *it = assoc_find(key, nkey);
0
+ DEBUG_REFCNT(it, '+');
0
/* expires items that are more recent than the oldest_live setting. */
0
-void
item_flush_expired(void) {
0
+void
do_item_flush_expired(void) {
0
if (settings.oldest_live == 0)
0
@@ -353,7 +424,7 @@ void item_flush_expired(void) {
0
if (iter->time >= settings.oldest_live) {
0
if ((iter->it_flags & ITEM_SLABBED) == 0) {
0
/* We've hit the first old item. Continue to the next queue. */