Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (c) 2008-2013, Hazelcast, Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hazelcast.spi.impl;
import com.hazelcast.core.PartitionAware;
import com.hazelcast.instance.Node;
import com.hazelcast.logging.ILogger;
import com.hazelcast.nio.Packet;
import com.hazelcast.spi.ExecutionService;
import com.hazelcast.spi.Operation;
import com.hazelcast.spi.PartitionAwareOperation;
import com.hazelcast.spi.UrgentSystemOperation;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import static com.hazelcast.instance.OutOfMemoryErrorDispatcher.onOutOfMemory;
/**
* The BasicOperationProcessor belongs to the BasicOperationService and is responsible for scheduling
* operations/packets to the correct threads.
*
* The actual processing of the 'task' that is scheduled, is forwarded to the {@link BasicOperationProcessor}. So
* this class is purely responsible for assigning a 'task' to a particular thread.
*
* The {@link #execute(Object, int, boolean)} accepts an Object instead of a runnable to prevent needing to
* create wrapper runnables around tasks. This is done to reduce the amount of object litter and therefor
* reduce pressure on the gc.
*
* There are 2 category of operation threads:
*
*
partition specific operation threads: these threads are responsible for executing e.g. a map.put.
* Operations for the same partition, always end up in the same thread.
*
*
* generic operation threads: these threads are responsible for executing operations that are not
* specific to a partition. E.g. a heart beat or a map.size.
*
*
*/
public final class BasicOperationScheduler {
public static final int TERMINATION_TIMEOUT_SECONDS = 3;
private final ILogger logger;
private final Node node;
private final ExecutionService executionService;
private final BasicOperationProcessor processor;
//the generic workqueues are shared between all generic operation threads, so that work can be stolen
//and a task gets processed as quickly as possible.
private final BlockingQueue genericWorkQueue = new LinkedBlockingQueue();
private final ConcurrentLinkedQueue genericPriorityWorkQueue = new ConcurrentLinkedQueue();
//all operations for specific partitions will be executed on these threads, .e.g map.put(key,value).
final OperationThread[] partitionOperationThreads;
//all operations that are not specific for a partition will be executed here, e.g heartbeat or map.size
final OperationThread[] genericOperationThreads;
//The genericOperationRandom is used when a generic operation is scheduled, and a generic OperationThread
//needs to be selected.
//todo:
//We could have a look at the ThreadLocalRandom, but it requires java 7. So some kind of reflection
//could to the trick to use something less painful.
private final Random genericOperationRandom = new Random();
private final ResponseThread responseThread;
private volatile boolean shutdown;
//The trigger is used when a priority message is send and offered to the operation-thread priority queue.
//To wakeup the thread, a priorityTaskTrigger is send to the regular blocking queue to wake up the operation
//thread.
private final Runnable priorityTaskTrigger = new Runnable() {
@Override
public void run() {
}
@Override
public String toString() {
return "TriggerTask";
}
};
public BasicOperationScheduler(Node node,
ExecutionService executionService,
BasicOperationProcessor processor) {
this.executionService = executionService;
this.logger = node.getLogger(BasicOperationScheduler.class);
this.node = node;
this.processor = processor;
this.genericOperationThreads = new OperationThread[getGenericOperationThreadCount()];
initOperationThreads(genericOperationThreads, new GenericOperationThreadFactory());
this.partitionOperationThreads = new OperationThread[getPartitionOperationThreadCount()];
initOperationThreads(partitionOperationThreads, new PartitionOperationThreadFactory());
this.responseThread = new ResponseThread();
responseThread.start();
logger.info("Starting with " + genericOperationThreads.length + " generic operation threads and "
+ partitionOperationThreads.length + " partition operation threads.");
}
private static void initOperationThreads(OperationThread[] operationThreads, ThreadFactory threadFactory) {
for (int threadId = 0; threadId < operationThreads.length; threadId++) {
OperationThread operationThread = (OperationThread) threadFactory.newThread(null);
operationThreads[threadId] = operationThread;
operationThread.start();
}
}
private int getGenericOperationThreadCount() {
int threadCount = node.getGroupProperties().GENERIC_OPERATION_THREAD_COUNT.getInteger();
if (threadCount <= 0) {
int coreSize = Runtime.getRuntime().availableProcessors();
threadCount = coreSize * 2;
}
return threadCount;
}
private int getPartitionOperationThreadCount() {
int threadCount = node.getGroupProperties().PARTITION_OPERATION_THREAD_COUNT.getInteger();
if (threadCount <= 0) {
int coreSize = Runtime.getRuntime().availableProcessors();
threadCount = coreSize * 2;
}
return threadCount;
}
int getPartitionIdForExecution(Operation op) {
return op instanceof PartitionAwareOperation ? op.getPartitionId() : -1;
}
boolean isAllowedToRunInCurrentThread(Operation op) {
return isAllowedToRunInCurrentThread(getPartitionIdForExecution(op));
}
boolean isInvocationAllowedFromCurrentThread(Operation op) {
return isInvocationAllowedFromCurrentThread(getPartitionIdForExecution(op));
}
boolean isAllowedToRunInCurrentThread(int partitionId) {
//todo: do we want to allow non partition specific tasks to be run on a partitionSpecific operation thread?
if (partitionId < 0) {
return true;
}
Thread currentThread = Thread.currentThread();
//we are only allowed to execute partition aware actions on an OperationThread.
if (!(currentThread instanceof OperationThread)) {
return false;
}
OperationThread operationThread = (OperationThread) currentThread;
//if the operationThread is a not a partition specific operation thread, then we are not allowed to execute
//partition specific operations on it.
if (!operationThread.isPartitionSpecific) {
return false;
}
//so it is an partition operation thread, now we need to make sure that this operation thread is allowed
//to execute operations for this particular partitionId.
int threadId = operationThread.threadId;
return toPartitionThreadIndex(partitionId) == threadId;
}
boolean isInvocationAllowedFromCurrentThread(int partitionId) {
Thread currentThread = Thread.currentThread();
if (currentThread instanceof OperationThread) {
if (partitionId > -1) {
//todo: we need to check for isPartitionSpecific
int threadId = ((OperationThread) currentThread).threadId;
return toPartitionThreadIndex(partitionId) == threadId;
}
return true;
}
return true;
}
public int getOperationExecutorQueueSize() {
int size = 0;
for (OperationThread t : partitionOperationThreads) {
size += t.workQueue.size();
}
size += genericWorkQueue.size();
return size;
}
public int getPriorityOperationExecutorQueueSize() {
int size = 0;
for (OperationThread t : partitionOperationThreads) {
size += t.priorityWorkQueue.size();
}
size += genericPriorityWorkQueue.size();
return size;
}
public int getResponseQueueSize() {
return responseThread.workQueue.size();
}
public void execute(Operation op) {
String executorName = op.getExecutorName();
if (executorName == null) {
int partitionId = getPartitionIdForExecution(op);
boolean hasPriority = op.isUrgent();
execute(op, partitionId, hasPriority);
} else {
executeOnExternalExecutor(op, executorName);
}
}
private void executeOnExternalExecutor(Operation op, String executorName) {
ExecutorService executor = executionService.getExecutor(executorName);
if (executor == null) {
throw new IllegalStateException("Could not found executor with name: " + executorName);
}
if (op instanceof PartitionAware) {
throw new IllegalStateException("PartitionAwareOperation " + op + " can't be executed on a " +
"custom executor with name: " + executorName);
}
if (op instanceof UrgentSystemOperation) {
throw new IllegalStateException("UrgentSystemOperation " + op + " can't be executed on a custom " +
"executor with name: " + executorName);
}
executor.execute(new LocalOperationProcessor(op));
}
public void execute(Packet packet) {
try {
if (packet.isHeaderSet(Packet.HEADER_RESPONSE)) {
//it is an response packet.
responseThread.workQueue.add(packet);
} else {
//it is an must be an operation packet
int partitionId = packet.getPartitionId();
boolean hasPriority = packet.isUrgent();
execute(packet, partitionId, hasPriority);
}
} catch (RejectedExecutionException e) {
if (node.nodeEngine.isActive()) {
throw e;
}
}
}
private void execute(Object task, int partitionId, boolean priority) {
if (task == null) {
throw new NullPointerException();
}
BlockingQueue workQueue;
Queue priorityWorkQueue;
if (partitionId < 0) {
workQueue = genericWorkQueue;
priorityWorkQueue = genericPriorityWorkQueue;
} else {
OperationThread partitionOperationThread = partitionOperationThreads[toPartitionThreadIndex(partitionId)];
workQueue = partitionOperationThread.workQueue;
priorityWorkQueue = partitionOperationThread.priorityWorkQueue;
}
if (priority) {
offerWork(priorityWorkQueue, task);
offerWork(workQueue, priorityTaskTrigger);
} else {
offerWork(workQueue, task);
}
}
private void offerWork(Queue queue, Object task) {
//in 3.3 we are going to apply backpressure on overload and then we are going to do something
//with the return values of the offer methods.
//Currently the queues are all unbound, so this can't happen anyway.
boolean offer = queue.offer(task);
if (!offer) {
logger.severe("Failed to offer " + task + " to BasicOperationScheduler due to overload");
}
}
private int toPartitionThreadIndex(int partitionId) {
return partitionId % partitionOperationThreads.length;
}
public void shutdown() {
shutdown = true;
interruptAll(partitionOperationThreads);
interruptAll(genericOperationThreads);
awaitTermination(partitionOperationThreads);
awaitTermination(genericOperationThreads);
}
private static void interruptAll(OperationThread[] operationThreads) {
for (OperationThread thread : operationThreads) {
thread.interrupt();
}
}
private static void awaitTermination(OperationThread[] operationThreads) {
for (OperationThread thread : operationThreads) {
try {
thread.awaitTermination(TERMINATION_TIMEOUT_SECONDS, TimeUnit.SECONDS);
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
}
}
}
@Override
public String toString() {
return "BasicOperationScheduler{"
+ "node=" + node.getThisAddress()
+ '}';
}
private class GenericOperationThreadFactory implements ThreadFactory {
private int threadId;
@Override
public OperationThread newThread(Runnable ignore) {
String threadName = node.getThreadPoolNamePrefix("generic-operation") + threadId;
OperationThread thread = new OperationThread(threadName, false, threadId, genericWorkQueue,
genericPriorityWorkQueue);
threadId++;
return thread;
}
}
private class PartitionOperationThreadFactory implements ThreadFactory {
private int threadId;
@Override
public Thread newThread(Runnable ignore) {
String threadName = node.getThreadPoolNamePrefix("partition-operation") + threadId;
//each partition operation thread, has its own workqueues because operations are partition specific and can't
//be executed by other threads.
LinkedBlockingQueue workQueue = new LinkedBlockingQueue();
ConcurrentLinkedQueue priorityWorkQueue = new ConcurrentLinkedQueue();
OperationThread thread = new OperationThread(threadName, true, threadId, workQueue, priorityWorkQueue);
threadId++;
return thread;
}
}
final class OperationThread extends Thread {
private final int threadId;
private final boolean isPartitionSpecific;
private final BlockingQueue workQueue;
private final Queue priorityWorkQueue;
public OperationThread(String name, boolean isPartitionSpecific,
int threadId, BlockingQueue workQueue, Queue priorityWorkQueue) {
super(node.threadGroup, name);
setContextClassLoader(node.getConfigClassLoader());
this.isPartitionSpecific = isPartitionSpecific;
this.workQueue = workQueue;
this.priorityWorkQueue = priorityWorkQueue;
this.threadId = threadId;
}
@Override
public void run() {
try {
doRun();
} catch (OutOfMemoryError e) {
onOutOfMemory(e);
} catch (Throwable t) {
logger.severe(t);
}
}
private void doRun() {
for (; ; ) {
Object task;
try {
task = workQueue.take();
} catch (InterruptedException e) {
if (shutdown) {
return;
}
continue;
}
if (shutdown) {
return;
}
processPriorityMessages();
process(task);
}
}
private void process(Object task) {
try {
processor.process(task);
} catch (Exception e) {
logger.severe("Failed to process task: " + task + " on partitionThread:" + getName());
}
}
private void processPriorityMessages() {
for (; ; ) {
Object task = priorityWorkQueue.poll();
if (task == null) {
return;
}
process(task);
}
}
public void awaitTermination(int timeout, TimeUnit unit) throws InterruptedException {
join(unit.toMillis(timeout));
}
}
private class ResponseThread extends Thread {
private final BlockingQueue workQueue = new LinkedBlockingQueue();
public ResponseThread() {
super(node.threadGroup, node.getThreadNamePrefix("response"));
setContextClassLoader(node.getConfigClassLoader());
}
public void run() {
try {
doRun();
} catch (OutOfMemoryError e) {
onOutOfMemory(e);
} catch (Throwable t) {
logger.severe(t);
}
}
private void doRun() {
for (; ; ) {
Object task;
try {
task = workQueue.take();
} catch (InterruptedException e) {
if (shutdown) {
return;
}
continue;
}
if (shutdown) {
return;
}
process(task);
}
}
private void process(Object task) {
try {
processor.process(task);
} catch (Exception e) {
logger.severe("Failed to process task: " + task + " on partitionThread:" + getName());
}
}
}
/**
* Process the operation that has been send locally to this OperationService.
*/
private class LocalOperationProcessor implements Runnable {
private final Operation op;
private LocalOperationProcessor(Operation op) {
this.op = op;
}
@Override
public void run() {
processor.process(op);
}
}
}