Package org.jnode.test.fs.filesystem.tests

Source Code of org.jnode.test.fs.filesystem.tests.ConcurrentAccessFSTest$Worker

/*
* $Id$
*
* Copyright (C) 2003-2014 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.test.fs.filesystem.tests;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Vector;

import org.jnode.fs.FSEntry;
import org.jnode.fs.FSFile;
import org.jnode.test.fs.filesystem.AbstractFSTest;
import org.jnode.test.fs.filesystem.config.FSTestConfig;
import org.jnode.test.support.TestUtils;
import org.junit.Ignore;
import org.junit.Test;

/**
* @author Fabien DUMINY
*/
public class ConcurrentAccessFSTest extends AbstractFSTest {
    protected static final int MAX_SLEEP = 100;

    protected static final int MIN_SLEEP = 10;

    protected static final int NB_READERS = 10;

    protected static final int NB_WRITERS = 10;

    public ConcurrentAccessFSTest(FSTestConfig config) {
        super(config);
    }

    @Test
    public void testRead() throws Throwable {
        setUp();

        FSFile file = prepareFile(config);

        Monitor monitor = new Monitor("testRead");

        createReaders(monitor, file);

        monitor.waitAll();
    }

    @Test @Ignore("Fix concurrency issues")
    public void testWrite() throws Throwable {
        if (!config.isReadOnly()) {
            setUp();

            FSFile file = prepareFile(config);
            Monitor monitor = new Monitor("testWrite");
            createWriters(monitor, file);
            monitor.waitAll();
            assertTrue("integrity test failed", isGoodResultFile(file));
        }
    }

    @Test @Ignore("Fix concurrency issues")
    public void testReadWrite() throws Throwable {
        setUp();

        FSFile file = prepareFile(config);
        Monitor monitor = new Monitor("testReadWrite");
        createReaders(monitor, file);
        if (!config.isReadOnly()) {
            createWriters(monitor, file);
        }
        monitor.waitAll();
        assertTrue("integrity test failed", isGoodResultFile(file));
    }

    protected void createReaders(Monitor monitor, FSFile file) {
        for (int i = 0; i < NB_READERS; i++) {
            monitor.addWorker(new Reader(monitor, file, i * 2, NB_READERS * 2,
                MIN_SLEEP, MAX_SLEEP));
        }
    }

    protected void createWriters(Monitor monitor, FSFile file) {
        for (int i = 0; i < NB_WRITERS; i++)
            monitor.addWorker(new Writer(monitor, file, i * 2, NB_WRITERS * 2,
                MIN_SLEEP, MAX_SLEEP));
    }

    protected boolean isGoodResultFile(FSFile file) throws IOException {
        byte[] expData = TestUtils.getTestData(FILE_SIZE_IN_WORDS);

        ByteBuffer data = ByteBuffer.allocate(expData.length);
        file.read(0, data);

        return TestUtils.equals(expData, data.array());
    }

    protected FSFile prepareFile(FSTestConfig config) throws Exception {
        remountFS(config, false);

        final String fileName = "RWTest";
        FSEntry rootEntry = getFs().getRootEntry();
        FSEntry entry = rootEntry.getDirectory().addFile(fileName);
        FSFile file = entry.getFile();
        file.setLength(FILE_SIZE_IN_WORDS * 2);
        file.flush();
        assertSize("Bad file size", FILE_SIZE_IN_WORDS * 2, file.getLength());

        remountFS(config, getFs().isReadOnly());

        rootEntry = getFs().getRootEntry();
        entry = rootEntry.getDirectory().getEntry(fileName);
        file = entry.getFile();
        assertSize("Bad file size", FILE_SIZE_IN_WORDS * 2, file.getLength());

        return file;
    }

    class FailureRecord {
        final Throwable exception;
        final String workerClass;

        FailureRecord(Throwable exception, String workerClass) {
            this.exception = exception;
            this.workerClass = workerClass;
        }
    }

    class Monitor {
        private Vector<Worker> workers = new Vector<Worker>();
        private Vector<Worker> finishedWorkers = new Vector<Worker>();
        private Vector<FailureRecord> failures = new Vector<FailureRecord>();
        private final String testName;

        Monitor(String testName) {
            this.testName = testName;
        }

        public void addWorker(Worker worker) {
            workers.add(worker);
        }

        public void notifyEnd(Worker worker) {
            finishedWorkers.add(worker);
        }

        public void notifyError(Worker worker, Throwable throwable) {
            failures.add(new FailureRecord(throwable, worker.getClass().getName()));
        }

        public void waitAll() throws Throwable {
            for (Worker worker : workers) {
                new Thread(worker).start();
            }

            while (finishedWorkers.size() != workers.size()) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    // ignore
                }
            }

            if (failures.size() == 1) {
                FailureRecord failure = failures.get(0);
                throw new Error("Worker " + failure.workerClass + " failed",
                    unwrap(failure.exception));
            }
            if (failures.size() > 0) {
                int i = 1;
                for (FailureRecord failure : failures) {
                    Throwable throwable = unwrap(failure.exception);
                    System.err.println("Failure #" + (i++) + " of test '" + testName +
                        " in worker " + failure.workerClass + ": " + throwable.getMessage());
                    throwable.printStackTrace(System.err);
                }
                throw new Error("Multiple workers had errors/exceptions (see earlier messages)");
            }
        }

        public Throwable unwrap(Throwable throwable) {
            if (throwable.getClass().equals(RuntimeException.class) &&
                throwable.getCause() != null) {
                throwable = throwable.getCause();
            }
            return throwable;
        }
    }

    class Reader extends Worker {
        public Reader(Monitor monitor, FSFile file, int offsetStart, int offsetStep,
                      int minSleep, int maxSleep) {
            super(monitor, file, offsetStart, offsetStep, minSleep, maxSleep);
        }

        public void doRun(long offset) throws IOException {
            ByteBuffer dest = ByteBuffer.allocate(2);
            file.read(offset, dest);
        }
    }

    abstract class Worker implements Runnable {
        protected FSFile file;

        protected int maxSleep;

        protected int minSleep;

        protected Monitor monitor;

        protected int offsetStart;

        protected int offsetStep;

        /**
         * @param monitor
         * @param file        the file on which to work
         * @param offsetStart file's offset from which to start
         * @param offsetStep  value to add to file's offset at each iteration
         * @param minSleep    minimum delay to sleep between 2 iterations
         * @param maxSleep    maximum delay to sleep between 2 iterations
         */
        public Worker(Monitor monitor, FSFile file, int offsetStart, int offsetStep,
                      int minSleep, int maxSleep) {
            this.file = file;
            this.offsetStart = offsetStart;
            this.offsetStep = offsetStep;
            this.minSleep = minSleep;
            this.maxSleep = maxSleep;
            this.monitor = monitor;
        }

        protected abstract void doRun(long offset) throws IOException;

        public final void run() {
            long length = file.getLength();
            try {
                for (int i = offsetStart; i < (length - 1); i += offsetStep) {
                    try {
                        doRun(i);
                    } catch (IOException ex) {
                        throw new RuntimeException("Error in worker thread", ex);
                    }
                    int sleep = (int) (minSleep + Math.random() * (maxSleep - minSleep));
                    try {
                        Thread.sleep(sleep);
                    } catch (InterruptedException e) {
                        //ignore
                    }
                }
            } catch (Throwable t) {
                monitor.notifyError(this, t);
            } finally {
                // worker has finished properly
                monitor.notifyEnd(this);
            }
        }
    }

    class Writer extends Worker {
        /**
         * {@inheritDoc}
         */
        public Writer(Monitor monitor, FSFile file, int offsetStart, int offsetStep,
                      int minSleep, int maxSleep) {
            super(monitor, file, offsetStart, offsetStep, minSleep, maxSleep);
        }

        public void doRun(long offset) throws IOException {
            long value = offset / 2;
            byte msbValue = (byte) (value & 0xFF00);
            byte lsbValue = (byte) (value & 0x00FF);
            ByteBuffer src = ByteBuffer.wrap(new byte[]{msbValue, lsbValue});
            file.write(offset, src);
        }
    }
}
TOP

Related Classes of org.jnode.test.fs.filesystem.tests.ConcurrentAccessFSTest$Worker

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.