package com.epickrram.workshop.perf.app.processors;
//////////////////////////////////////////////////////////////////////////////////
// Copyright 2015 Mark Price mark at epickrram.com //
// //
// 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. //
//////////////////////////////////////////////////////////////////////////////////
import com.epickrram.workshop.perf.app.message.Packet;
import com.epickrram.workshop.perf.config.CommandLineArgs;
import com.epickrram.workshop.perf.support.NanoTimer;
import com.lmax.disruptor.RingBuffer;
import org.performancehints.SpinHint;
import java.io.File;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import static com.epickrram.workshop.perf.support.Threads.THREADS;
import static java.nio.channels.FileChannel.MapMode.READ_ONLY;
import static java.nio.channels.FileChannel.open;
import static java.nio.file.StandardOpenOption.READ;
public final class InputReader
{
private final RingBuffer<Packet> messageSink;
private final NanoTimer nanoTimer;
private final CommandLineArgs commandLineArgs;
private final int numberOfIterations;
public InputReader(final RingBuffer<Packet> messageSink, final NanoTimer nanoTimer, final CommandLineArgs commandLineArgs)
{
this.messageSink = messageSink;
this.nanoTimer = nanoTimer;
this.commandLineArgs = commandLineArgs;
this.numberOfIterations = commandLineArgs.getNumberOfIterations();
}
public void processFiles()
{
final File inputFile = new File(commandLineArgs.getInputFile());
try (final FileChannel fileChannel = open(inputFile.toPath(), READ))
{
final MappedByteBuffer mappedFile = fileChannel.map(READ_ONLY, 0L, inputFile.length());
mappedFile.load();
System.out.println("Producer thread has pid: " + THREADS.getCurrentThreadId());
for (int i = 0; i < numberOfIterations; i++)
{
if (i == commandLineArgs.getNumberOfWarmups())
{
System.out.println("Warm-up complete at " + new Date());
System.out.println("Pausing for 10 seconds...");
THREADS.sleep(10L, TimeUnit.SECONDS);
System.out.println("Executing test at " + new Date());
}
processSingleFile(mappedFile, i == numberOfIterations - 1);
}
}
catch (IOException e)
{
throw new RuntimeException("File operation failed", e);
}
}
private void processSingleFile(final MappedByteBuffer mappedFile, final boolean isLastIteration)
{
for (int recordIndex = 0; recordIndex < commandLineArgs.getNumberOfRecords(); recordIndex++)
{
if (!messageSink.hasAvailableCapacity(1))
{
throw new RuntimeException("RingBuffer full!");
}
final long sequence = messageSink.next();
try
{
final Packet packet = messageSink.get(sequence);
packet.reset();
packet.setSequenceInFile(recordIndex);
packet.setSequence(sequence);
final boolean isLastInFile = recordIndex == commandLineArgs.getNumberOfRecords() - 1;
packet.setLastInFile(isLastInFile);
packet.setReceivedNanoTime(nanoTimer.nanoTime());
packet.setLastInStream(isLastInFile && isLastIteration);
mappedFile.position(recordIndex * commandLineArgs.getRecordLength());
mappedFile.limit(mappedFile.position() + commandLineArgs.getRecordLength());
packet.getPayload().put(mappedFile);
}
finally
{
messageSink.publish(sequence);
}
introduceMessagePublishDelay();
}
}
private void introduceMessagePublishDelay()
{
final long stopSpinningAt = System.nanoTime() + TimeUnit.MICROSECONDS.toNanos(10L);
while (System.nanoTime() < stopSpinningAt)
{
SpinHint.spinLoopHint();
}
}
}