package com.ldbc.driver.runtime.executor;
import com.ldbc.driver.ChildOperationGenerator;
import com.ldbc.driver.Db;
import com.ldbc.driver.Operation;
import com.ldbc.driver.SerializingMarshallingException;
import com.ldbc.driver.WorkloadStreams;
import com.ldbc.driver.runtime.ConcurrentErrorReporter;
import com.ldbc.driver.runtime.DefaultQueues;
import com.ldbc.driver.runtime.QueueEventSubmitter;
import com.ldbc.driver.runtime.coordination.GlobalCompletionTimeReader;
import com.ldbc.driver.runtime.coordination.LocalCompletionTimeWriter;
import com.ldbc.driver.runtime.metrics.MetricsService;
import com.ldbc.driver.runtime.scheduling.Spinner;
import com.ldbc.driver.temporal.TimeSource;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import static java.lang.String.format;
public class SingleThreadOperationExecutor implements OperationExecutor
{
static final Operation TERMINATE_OPERATION = new Operation()
{
@Override
public int type()
{
return -1;
}
@Override
public Object marshalResult( String serializedOperationResult ) throws SerializingMarshallingException
{
return null;
}
@Override
public String serializeResult( Object operationResultInstance ) throws SerializingMarshallingException
{
return null;
}
};
private final SingleThreadOperationExecutorThread executorThread;
private final QueueEventSubmitter<Operation> operationQueueEventSubmitter;
private final AtomicLong uncompletedHandlers = new AtomicLong( 0 );
private final AtomicBoolean shutdown = new AtomicBoolean( false );
public SingleThreadOperationExecutor( Db db,
WorkloadStreams.WorkloadStreamDefinition streamDefinition,
LocalCompletionTimeWriter localCompletionTimeWriter,
GlobalCompletionTimeReader globalCompletionTimeReader,
Spinner spinner,
TimeSource timeSource,
ConcurrentErrorReporter errorReporter,
MetricsService metricsService,
ChildOperationGenerator childOperationGenerator,
int boundedQueueSize )
{
Queue<Operation> operationQueue = DefaultQueues.newAlwaysBlockingBounded( boundedQueueSize );
this.operationQueueEventSubmitter = QueueEventSubmitter.queueEventSubmitterFor( operationQueue );
OperationHandlerRunnableContextRetriever operationHandlerRunnableContextInitializer =
new OperationHandlerRunnableContextRetriever(
streamDefinition,
db,
localCompletionTimeWriter,
globalCompletionTimeReader,
spinner,
timeSource,
errorReporter,
metricsService
);
this.executorThread = new SingleThreadOperationExecutorThread(
operationQueue,
errorReporter,
uncompletedHandlers,
operationHandlerRunnableContextInitializer,
childOperationGenerator
);
this.executorThread.start();
}
public final void execute( Operation operation ) throws OperationExecutorException
{
uncompletedHandlers.incrementAndGet();
try
{
operationQueueEventSubmitter.submitEventToQueue( operation );
}
catch ( InterruptedException e )
{
throw new OperationExecutorException( "Error encountered while submitting handler to queue", e );
}
}
@Override
synchronized public final void shutdown( long waitAsMilli ) throws OperationExecutorException
{
if ( shutdown.get() )
{
throw new OperationExecutorException( "Executor has already been shutdown" );
}
try
{
operationQueueEventSubmitter.submitEventToQueue( TERMINATE_OPERATION );
executorThread.join( waitAsMilli );
if ( uncompletedHandlers.get() > 0 )
{
executorThread.forceShutdown();
throw new OperationExecutorException( format(
"Executor shutdown before all handlers could complete - %s uncompleted handlers",
uncompletedHandlers ) );
}
}
catch ( Exception e )
{
throw new OperationExecutorException( "Error encountered while trying to shutdown", e );
}
shutdown.set( true );
}
@Override
public long uncompletedOperationHandlerCount()
{
return uncompletedHandlers.get();
}
}