package net.spy.memcached;

import net.spy.SpyObject;

/**
 * Object that provides mutation via CAS over a given memcache client.
 */
public class CASMutator<T> extends SpyObject {

	private final MemcachedClient client;

	/**
	 * Construct a CASMutator that uses the given client.
	 *
	 * @param c the client
	 */
	public CASMutator(MemcachedClient c) {
		super();
		client=c;
	}

	/**
	 * CAS a new value in for a key.
	 *
	 * @param key the key to be CASed
	 * @param initial the value to use when the object is not cached
	 * @param initialExp the expiration time to use when initializing
	 * @param m the mutation to perform on an object if a value exists for the
	 *          key
	 * @return the new value that was set
	 */
	public T cas(final String key, final T initial, long initialExp,
			final CASMutation<T> m) throws Exception {
		T rv=initial;

		boolean done=false;
		while(!done) {
			CASValue casval=client.gets(key);
			T current=null;
			// If there were a CAS value, check to see if it's compatible.
			if(casval != null) {
				@SuppressWarnings("unchecked")
				T tmp = (T)casval.getValue();
				current=tmp;
			}
			// If we have anything mutate and CAS, else add.
			if(current != null) {
				rv=m.getNewValue(current);
				// There are three possibilities here:
				//  1) It worked and we're done.
				//  2) It collided and we need to reload and try again.
				//  3) It disappeared between our fetch and our cas.
				// We're ignoring #3 because it's *extremely* unlikely and the
				// behavior will be fine in this code -- we'll do another gets
				// and follow it up with either an add or another cas depending
				// on whether it exists the next time.
				if(client.cas(key, casval.getCas(), rv) == CASResponse.OK) {
					done=true;
				}
			} else {
				// No value found, try an add.
				if(client.add(key, 0, initial).get()) {
					done=true;
					rv=initial;
				}
			}
		}

		return rv;
	}
}

@@ -123,16 +123,587 @@
 client.%0A
+ *%0A * %3Cp%3EExample usage (reinventing incr):%3C/p%3E%0A *%0A * %3Cpre%3E%0A * // Get or create a client.%0A * MemcachedClient client=%5B...%5D;%0A *%0A * // Get a mutator instance that uses that client.%0A * CASMutator&lt;Long&gt; mutator=new CASMutator&lt;Long&gt;(client);%0A *%0A * // Get a mutation that knows what to do when a value is found.%0A * CASMutation&lt;Long&gt; mutation=new CASMutation&lt;Long&gt;() %7B%0A *     public Long getNewValue(Long current) %7B%0A *         return current + 1;%0A *     %7D%0A * %7D;%0A *%0A * // Do a mutation.%0A * long currentValue=mutator.cas(someKey, 0L, 0, mutation);%0A * %3C/pre%3E%0A
  */%0Apubl

Document cas tools better.