/* * Copyright 2009-2010 Shashank Tulsyan * * 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. * * File: ThrottledRealFile.java * Author: Shashank Tulsyan */ package neembuu.vfs.test; import java.io.IOException; import java.io.PrintWriter; import java.util.Date; import java.util.Iterator; import java.util.concurrent.ConcurrentLinkedQueue; import jpfm.DirectoryStream; import jpfm.SystemUtils; import jpfm.annotations.MightBeBlocking; import jpfm.annotations.NonBlocking; import jpfm.operations.readwrite.ReadRequest; /** * * @author Shashank Tulsyan */ public class ThrottledRealFile extends MonitoredRealFile { private int cps; public static final int INVALID_CPS = DynamicCPSPauser.INVALID_CPS; private final ConcurrentLinkedQueue<ReadRequest> pendingReadRequests = new ConcurrentLinkedQueue<ReadRequest>(); private final Object lock = new Object(); private ReadThread readThread = null; private long totalRequest = 0; private long totalTime = 0; private static final boolean showGraph = true; @Override public synchronized void open() { readThread = new ReadThread(super.getName()); this.readThread.start(); super.open(); } private final class ReadThread extends Thread { public ReadThread(String filename) { super("ThrottledRead@" + filename); } @Override public void run() { ConcurrentLinkedQueue<ReadRequest> stack = new ConcurrentLinkedQueue<ReadRequest>(); ReadRequest previous = null; // if cascading is allowed the definition // of isopen changes // from // isOpen() // to // isOpen() || isOpenByCascading() for (; isOpen() || isOpenByCascading();) { //service till pending call present of this file is open // intution suggests that close() on file will be called only when there are no pending requests. // but let's not make any assumptions. Iterator<ReadRequest> it = pendingReadRequests.iterator(); while (it.hasNext()) { ReadRequest read = it.next(); stack.add(read); if (read.isCompleted()) { it.remove(); continue; } if (stack.peek().isCompleted()) { if (previous == null) { previous = stack.poll(); } totalRequest += stack.peek().getByteBuffer().capacity(); totalTime += stack.peek().getCreationTime() - previous.getCompletionTime(); /*setRequestSpeed( (stack.peek().getByteBuffer().capacity()) / ( (stack.peek().getCreationTime() - previous.getCompletionTime()) * 1.024d ) );*/ if (showGraph) { setRequestSpeed(totalRequest / (totalTime * 1.024d)); setSupplySpeed( (previous.getByteBuffer().capacity()) / ((previous.getCompletionTime() - previous.getCreationTime()) * 1.024d)); } previous = null; } //if(cps != DynamicCPSPauser.INVALID_CPS)System.out.println("pausing 61"); if (isOpen() || isOpenByCascading()) { //pause only if file is open //otherwise just hurriedly complete all pending requests DynamicCPSPauser.pause(cps, read.getByteBuffer().capacity()); } //if(read.isCompleted()){it.remove();continue;} try { superRead(read); //object has been send once, it can be removed now //we send requests only once. it.remove(); continue; } catch (Exception any) { any.printStackTrace(); if (!read.isCompleted()) { read.handleUnexpectedCompletion(any); } } } synchronized (lock) { try { while (pendingReadRequests.size() == 0 && (isOpen() || isOpenByCascading())) { lock.wait(); } } catch (InterruptedException exception) { } } } } } private final PrintWriter supplySpeedList; private final PrintWriter requestSpeedList; public void setSupplySpeed(double supplySpeed) { supplySpeedList.print(supplySpeed); supplySpeedList.print(" "); supplySpeedList.println(new Date(System.currentTimeMillis())); supplySpeedList.flush(); getFilePanel().setSupplySpeed(supplySpeed); } public void setRequestSpeed(double requestSpeed) { requestSpeedList.print(requestSpeed); requestSpeedList.print(" "); requestSpeedList.println(new Date(System.currentTimeMillis())); requestSpeedList.flush(); getFilePanel().setRequestSpeed(requestSpeed); } public ThrottledRealFile(String srcfile, int cps, DirectoryStream fileContainer) throws IOException { super(srcfile, fileContainer); this.cps = cps; //readThread = new ReadThread(srcfile); //this.readThread.start(); if (SystemUtils.IS_OS_WINDOWS) { supplySpeedList = new PrintWriter("j:\\neembuu\\graphs\\" + getName() + ".supplySpeed"); } else if (SystemUtils.IS_OS_LINUX) { supplySpeedList = new PrintWriter("/media/j/neembuu/graphs/" + getName() + ".supplySpeed"); } else // assume mac { supplySpeedList = new PrintWriter("/Volumes/MIDS/neembuu/graphs/" + getName() + ".supplySpeed"); } if (SystemUtils.IS_OS_WINDOWS) { requestSpeedList = new PrintWriter("j:\\neembuu\\graphs\\" + getName() + ".requestSpeed"); } else if (SystemUtils.IS_OS_LINUX) { requestSpeedList = new PrintWriter("/media/j/neembuu/graphs/" + getName() + ".requestSpeed"); } else // assume mac { requestSpeedList = new PrintWriter("/Volumes/MIDS/neembuu/graphs/" + getName() + ".requestSpeed"); } } @NonBlocking @Override public void read(ReadRequest read) throws Exception { //synchronized (this) { /*if (readThread == null) { readThread = new ReadThread("ThrottledFile@"+super.getName()); System.out.println("starting new thread"); readThread.start(); isOpen = true; }*/ //} pendingReadRequests.add(read); synchronized (lock) { lock.notifyAll(); } } @MightBeBlocking private void superRead(ReadRequest read) throws Exception { super.read(read); } // @Blocking(getReasonWhyItDoesNotMatter="not used") // public int read(long fileOffset, ByteBuffer buffer) { // DynamicCPSPauser.pause(cps,buffer.capacity()); // return super.read(fileOffset, buffer); // } protected void setCps(int cps) { this.cps = cps; } @Override public synchronized void close() { if (this.readThread != null) { while (this.readThread.isAlive()) { synchronized (lock) { lock.notifyAll(); // to ensure that the //thread if not waiting and while it was waiting //the file was closed //we want the thread to move again. } } } this.readThread = null; super.close(); } public final void printPendingRequests(java.io.PrintStream pst) { pst.println("++++++++ Pending requests on " + this + " +++++++++ "); Iterator<ReadRequest> it = pendingReadRequests.iterator(); while (it.hasNext()) { pst.println(it.next()); } pst.println("-------- Pending requests on " + this + " --------- "); } /*@Override public String toString() { return super.toString(); }*/ private static final class DynamicCPSPauser { // Conversions for milli and nano seconds private static final int MS_PER_SEC = 1000; private static final int NS_PER_SEC = 1000000000; private static final int NS_PER_MS = NS_PER_SEC / MS_PER_SEC; public static final int INVALID_CPS = -1; public static void pauseInverse( final Object sleepOver, double throttleReciprocal, int bytes) { if (throttleReciprocal <= 0) { return; } if (Double.isInfinite(throttleReciprocal)) { throw new IllegalArgumentException("Specifying infinite throttling"); } long sleepMS = (long) ((bytes * MS_PER_SEC) * throttleReciprocal); int sleepNS = (int) ((bytes * MS_PER_SEC) * throttleReciprocal) % NS_PER_MS; if (sleepMS > 3000) { System.err.println("Throttle values exceeds timeout, better to terminate connection, than to leave a deadlock"); throw new IllegalStateException("Throttle values exceeds timeout, better to terminate connection, than to leave a deadlock"); } try { synchronized (sleepOver) { sleepOver.wait(sleepMS, sleepNS); } //steppedSleep(sleepMS, sleepNS, 200); } catch (InterruptedException ignored) { ignored.printStackTrace(System.err); } } private static void steppedSleep( long sleepMS, int sleepNS, int atomicMSSleepTime) throws InterruptedException { long totalSleep = 0; Thread.sleep(0, sleepNS); while (totalSleep < sleepMS) { Thread.sleep(Math.min(atomicMSSleepTime, sleepMS - atomicMSSleepTime)); totalSleep += atomicMSSleepTime; } } /** * Pause for an appropriate time according to the number of bytes being transferred. * If CPS < 0 simply returns * @param bytes number of bytes being transferred */ public static void pause(long CPS, int bytes) { if (CPS == 0) { throw new IllegalArgumentException("CPS has to be greater than or lesser than 0. This exception will kill this connection."); } if (CPS < 0) { return; } long sleepMS = (bytes * MS_PER_SEC) / CPS; int sleepNS = (int) ((bytes * MS_PER_SEC) / CPS) % NS_PER_MS; try { Thread.sleep(sleepMS, sleepNS); } catch (InterruptedException ignored) { } } } }