Package org.jscsi.initiator.devices

Source Code of org.jscsi.initiator.devices.Raid1Device$ReadThread

/**
* Copyright (c) 2012, University of Konstanz, Distributed Systems Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the University of Konstanz nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jscsi.initiator.devices;

import java.util.List;
import java.util.Vector;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* <h1>Raid1Device</h1>
* <p>
* Implements a RAID 1 Device with several Devices.
* </p>
*
* @author Bastian Lemke
*/
public class Raid1Device implements Device {

    private final Device[] devices;

    private int blockSize = -1;

    private long blockCount = -1;

    /** The Logger interface. */
    private static final Logger LOGGER = LoggerFactory.getLogger(Raid1Device.class);

    /**
     * Pointer to next device to read data from. Used to distribute reads
     * between the devices.
     */
    private int nextTarget;

    /** Thread pool for write- and read-threads. */
    private final ExecutorService executor;

    /** Thread barrier for write- and read-threads. */
    private CyclicBarrier barrier;

    /**
     * Constructor to create an Raid1Device. The Device has to be initialized
     * before it can be used.
     *
     * @param initDevices
     *            devices to use
     * @throws Exception
     *             if any error occurs
     */
    public Raid1Device(final Device[] initDevices) throws Exception {

        devices = initDevices;
        nextTarget = 0;
        // create one thread per device
        executor = Executors.newFixedThreadPool(devices.length);
    }

    /** {@inheritDoc} */
    public void close() throws Exception {

        if (blockCount == -1) {
            throw new NullPointerException();
        }

        executor.shutdown();
        for (Device device : devices) {
            device.close();
        }
        blockSize = -1;
        blockCount = -1;
        LOGGER.info("Closed " + getName() + ".");
    }

    /** {@inheritDoc} */
    public int getBlockSize() {

        if (blockSize == -1) {
            throw new IllegalStateException("You first have to open the Device!");
        }

        return blockSize;
    }

    /** {@inheritDoc} */
    public String getName() {

        String name = "Raid1Device(";
        for (Device device : devices) {
            name += device.getName() + "+";
        }
        return name.substring(0, name.length() - 1) + ")";
    }

    /** {@inheritDoc} */
    public long getBlockCount() {

        if (blockCount == -1) {
            throw new IllegalStateException("You first have to open the Device!");
        }

        return blockCount;
    }

    /** {@inheritDoc} */
    public void open() throws Exception {

        if (blockCount != -1) {
            throw new IllegalStateException("Raid1Device is already opened!");
        }

        for (Device device : devices) {
            device.open();
        }

        // check if all devices have the same block size
        blockSize = 0;
        for (Device device : devices) {
            if (blockSize == 0) {
                blockSize = (int)device.getBlockSize();
            } else if (blockSize != (int)device.getBlockSize()) {
                throw new IllegalArgumentException("All devices must have the same block size!");
            }
        }

        // find the smallest device
        blockCount = Long.MAX_VALUE;
        for (Device device : devices) {
            blockCount = Math.min(blockCount, device.getBlockCount());
        }

    }

    /** {@inheritDoc} */
    public void read(final long address, final byte[] data) throws Exception {

        if (blockCount == -1) {
            throw new IllegalStateException("You first have to open the Device!");
        }

        int blocks = data.length / blockSize;

        if (address < 0 || address + blocks > blockCount) {
            long adr = address < 0 ? address : address + blocks - 1;
            throw new IllegalArgumentException("Address " + adr + " out of range.");
        }

        if (data.length % blockSize != 0) {
            throw new IllegalArgumentException("Number of bytes is not a multiple of the blocksize!");
        }

        int parts = (blocks >= devices.length) ? devices.length : (int)blocks;
        barrier = new CyclicBarrier(parts + 1);
        int targetBlockCount;
        List<byte[]> targetData = new Vector<byte[]>();
        int targetBlockAddress = (int)address;

        for (int i = 0; i < parts; i++) {
            targetBlockCount = blocks / devices.length;
            if (i < (blocks % devices.length)) {
                targetBlockCount++;
            }
            targetData.add(new byte[targetBlockCount * blockSize]);

            /** Start Thread to read Data from Target */
            if (targetBlockCount != 0) {
                executor.execute(new ReadThread(devices[nextTarget], targetBlockAddress, targetData.get(i)));
            }
            targetBlockAddress += targetBlockCount;
            nextTarget = (nextTarget < devices.length - 1) ? nextTarget + 1 : 0;
        }
        barrier.await();

        /** Merge the results. */
        int pos = 0;
        for (int i = 0; i < targetData.size(); i++) {
            System.arraycopy(targetData.get(i), 0, data, pos, targetData.get(i).length);
            pos += targetData.get(i).length;
        }
    }

    /** {@inheritDoc} */
    public void write(final long address, final byte[] data) throws Exception {

        if (blockCount == -1) {
            throw new IllegalStateException("You first have to open the Device!");
        }

        long blocks = data.length / blockSize;

        if (address < 0 || address + blocks > blockCount) {
            long adr = address < 0 ? address : address + blocks - 1;
            throw new IllegalArgumentException("Address " + adr + " out of range.");
        }

        if (data.length % blockSize != 0) {
            throw new IllegalArgumentException("Number of bytes is not a multiple of the blocksize!");
        }

        barrier = new CyclicBarrier(devices.length + 1);
        for (int i = 0; i < devices.length; i++) {
            executor.execute(new WriteThread(devices[i], (int)address, data));
        }
        barrier.await();
    }

    /**
     * Private class to represent one single read-action in an own thread for
     * one target.
     *
     * @author bastian
     */
    private final class ReadThread implements Runnable {

        private final Device device;

        private final int address;

        private final byte[] data;

        private ReadThread(final Device readDevice, final int readBlockAddress, final byte[] readData) {

            device = readDevice;
            address = readBlockAddress;
            data = readData;
        }

        public void run() {

            try {
                device.read(address, data);
                barrier.await();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Private class to represent one single write-action in an own thread for
     * one target.
     *
     * @author Bastian Lemke
     */
    private final class WriteThread implements Runnable {

        private final Device device;

        private final int address;

        private final byte[] data;

        private WriteThread(final Device writeDevice, final int writeBlockAddress, final byte[] writeData) {

            device = writeDevice;
            address = writeBlockAddress;
            data = writeData;
        }

        public void run() {

            try {
                device.write(address, data);
                barrier.await();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
TOP

Related Classes of org.jscsi.initiator.devices.Raid1Device$ReadThread

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.