blob: af3f8b0c795384c1fd49e913b55be4b34090a2f0 [file] [log] [blame]
package com.gemstone.gemfire.internal.redis.executor.list;
import java.util.List;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.internal.redis.ByteArrayWrapper;
import com.gemstone.gemfire.internal.redis.Coder;
import com.gemstone.gemfire.internal.redis.Command;
import com.gemstone.gemfire.internal.redis.ExecutionHandlerContext;
import com.gemstone.gemfire.internal.redis.Extendable;
import com.gemstone.gemfire.internal.redis.RedisDataType;
public abstract class PopExecutor extends ListExecutor implements Extendable {
@Override
public void executeCommand(Command command, ExecutionHandlerContext context) {
List<byte[]> commandElems = command.getProcessedCommand();
if (commandElems.size() < 2) {
command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), getArgsError()));
return;
}
ByteArrayWrapper key = command.getKey();
checkDataType(key, RedisDataType.REDIS_LIST, context);
Region<Integer, ByteArrayWrapper> keyRegion = getRegion(context, key);
Region<String, Integer> meta = context.getRegionCache().getListsMetaRegion();
if (keyRegion == null || keyRegion.size() == 0) {
command.setResponse(Coder.getNilResponse(context.getByteBufAllocator()));
return;
}
String indexKey = popType() == ListDirection.LEFT ? key + "head" : key + "tail";
String oppositeKey = popType() == ListDirection.RIGHT ? key + "head" : key + "tail";
Integer index = 0;
int originalIndex = index;
int incr = popType() == ListDirection.LEFT ? 1 : -1;
ByteArrayWrapper valueWrapper = null;
/**
*
* First attempt to hop over an index by moving the index
* down one in the meta data region. The desired index to
* remove is held within the field index
*
*/
boolean indexChanged = false;
do {
index = meta.get(indexKey);
if (index == meta.get(oppositeKey))
break;
indexChanged = meta.replace(indexKey, index, index + incr);
} while(!indexChanged);
/**
*
* Now attempt to remove the value of the index. We must do a
* get to ensure a returned value and then call remove with the
* value to ensure no one else has removed it first. Otherwise,
* try other indexes
*
*/
boolean removed = false;
int i = 0;
do {
valueWrapper = keyRegion.get(index);
if (valueWrapper != null)
removed = keyRegion.remove(index, valueWrapper);
/**
*
* If remove has passed, our job is done and we can break and
* stop looking for a value
*
*/
if (removed)
break;
/**
*
* If the index has not been removed, we need to look at other indexes.
* Two cases exist:
*
* ************************** FIRST MISS ***********************************
* Push occurring at the same time, further index update first
* | This is location of miss
* | |
* | |
* V V
* [-4] [X] [-2] [-1] [0] [1] [2]
* <-- Direction of index update
* If this is the first miss, the index is re obtained from the meta region
* and that index is trying. However, if the index in the meta data region
* is not further out, that index is not used and moves on to the second case
* **************************************************************************
*
* ************************* SUBSEQUENT MISSES ******************************
* Push occurring at the same time, further index update first
* | This is location of miss
* | |
* | |
* V V
* [-4] [X] [-2] [-1] [0] [1] [2]
* Direction of index update -->
* If this is not the first miss then we move down to the other end of the list
* which means the next not empty index will be attempted to be removed
* **************************************************************************
*
* If it is the case that the list is empty, it will exit this loop
*
*/
index += incr;
int metaIndex = meta.get(indexKey);
if (i < 1 && (popType() == ListDirection.LEFT && metaIndex < originalIndex ||
popType() == ListDirection.RIGHT && metaIndex > originalIndex))
index = metaIndex;
i++;
} while (!removed && keyRegion.size() != 0);
if (valueWrapper != null)
command.setResponse(Coder.getBulkStringResponse(context.getByteBufAllocator(), valueWrapper.toBytes()));
else
command.setResponse(Coder.getNilResponse(context.getByteBufAllocator()));
}
protected abstract ListDirection popType();
}