summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordormando <dormando@rydia.net>2013-12-18 18:52:47 -0800
committerdormando <dormando@rydia.net>2013-12-20 13:33:34 -0800
commitfacb719f40ecfcb98271a79561d4b94757b7a843 (patch)
treef8c7459901da3f9fc329a3e656c4d5aaa276db51
parentcb9c269bd57bb5417b337228cfbb50f4a8769d09 (diff)
downloadmemcached-facb719f40ecfcb98271a79561d4b94757b7a843.tar.gz
reorder incr/decr operation to be safer.1.4.17
looking for explicit refcount numbers before doing anything. other parts of the code will require refactoring for this to be fully safe.
-rw-r--r--memcached.c34
1 files changed, 22 insertions, 12 deletions
diff --git a/memcached.c b/memcached.c
index ae34ecd..ffe2204 100644
--- a/memcached.c
+++ b/memcached.c
@@ -3163,8 +3163,19 @@ enum delta_result_type do_add_delta(conn *c, const char *key, const size_t nkey,
snprintf(buf, INCR_MAX_STORAGE_LEN, "%llu", (unsigned long long)value);
res = strlen(buf);
/* refcount == 2 means we are the only ones holding the item, and it is
- * linked. */
- if (res + 2 > it->nbytes || it->refcount != 2) { /* need to realloc */
+ * linked. We hold the item's lock in this function, so refcount cannot
+ * increase. */
+ if (res + 2 <= it->nbytes && it->refcount == 2) { /* replace in-place */
+ /* When changing the value without replacing the item, we
+ need to update the CAS on the existing item. */
+ mutex_lock(&cache_lock); /* FIXME */
+ ITEM_set_cas(it, (settings.use_cas) ? get_cas_id() : 0);
+ mutex_unlock(&cache_lock);
+
+ memcpy(ITEM_data(it), buf, res);
+ memset(ITEM_data(it) + res, ' ', it->nbytes - res - 2);
+ do_item_update(it);
+ } else if (it->refcount > 1) {
item *new_it;
new_it = do_item_alloc(ITEM_key(it), it->nkey, atoi(ITEM_suffix(it) + 1), it->exptime, res + 2, hv);
if (new_it == 0) {
@@ -3178,16 +3189,15 @@ enum delta_result_type do_add_delta(conn *c, const char *key, const size_t nkey,
// returning the CAS of the old item below.
ITEM_set_cas(it, (settings.use_cas) ? ITEM_get_cas(new_it) : 0);
do_item_remove(new_it); /* release our reference */
- } else { /* replace in-place */
- /* When changing the value without replacing the item, we
- need to update the CAS on the existing item. */
- mutex_lock(&cache_lock); /* FIXME */
- ITEM_set_cas(it, (settings.use_cas) ? get_cas_id() : 0);
- mutex_unlock(&cache_lock);
-
- memcpy(ITEM_data(it), buf, res);
- memset(ITEM_data(it) + res, ' ', it->nbytes - res - 2);
- do_item_update(it);
+ } else {
+ /* Should never get here. This means we somehow fetched an unlinked
+ * item. TODO: Add a counter? */
+ if (settings.verbose) {
+ fprintf(stderr, "Tried to do incr/decr on invalid item\n");
+ }
+ if (it->refcount == 1)
+ do_item_remove(it);
+ return DELTA_ITEM_NOT_FOUND;
}
if (cas) {