org.commonjava.util.partyline.ManyReadersWithPreExistingWriterTest.java Source code

Java tutorial

Introduction

Here is the source code for org.commonjava.util.partyline.ManyReadersWithPreExistingWriterTest.java

Source

/**
 * Copyright (C) 2015 Red Hat, Inc. (jdcasey@commonjava.org)
 *
 * 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.
 */
package org.commonjava.util.partyline;

import org.apache.commons.io.IOUtils;
import org.commonjava.cdi.util.weft.ContextSensitiveExecutorService;
import org.commonjava.cdi.util.weft.ThreadContext;
import org.commonjava.util.partyline.fixture.ThreadDumper;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;

/**
 * Created by jdcasey on 11/14/17.
 */
public class ManyReadersWithPreExistingWriterTest extends AbstractJointedIOTest {
    private static final int THREADS = 30;

    private static final long WAIT = 0;

    private static final int ITERATIONS = 2;

    private ExecutorService executor = new ContextSensitiveExecutorService(Executors.newCachedThreadPool());

    private final JoinableFileManager mgr = new JoinableFileManager();

    private String content;

    private File file;

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Rule
    public TestRule timeout = ThreadDumper.timeoutRule(2, TimeUnit.MINUTES);

    @Before
    public void setup() throws IOException {
        file = temp.newFile("testfile.txt");

        mgr.startReporting();

        StringBuilder contentBuilder = new StringBuilder();
        for (int i = 0; i < COUNT; i++) {
            contentBuilder.append(System.currentTimeMillis()).append("\n");
        }

        content = contentBuilder.toString();
    }

    @Test
    public void run() throws Exception {
        final List<Long> timings = new ArrayList<>();
        for (int i = 0; i < ITERATIONS; i++) {
            long start = System.currentTimeMillis();
            executeTestIteration();
            long end = System.currentTimeMillis();
            timings.add(end - start);
        }

        long total = 0;
        for (Long time : timings) {
            total += time;
        }

        logger.info("Average: {}ms (per-thread: {}ms)", (total / timings.size()),
                (total / (timings.size() * THREADS)));
    }

    private void executeTestIteration() throws Exception {
        ThreadContext.getContext(true);

        ExecutorCompletionService<String> completionService = new ExecutorCompletionService<String>(executor);

        final AtomicBoolean readFlag = new AtomicBoolean(false);
        final AtomicBoolean writeFlag = new AtomicBoolean(false);

        completionService.submit(writer(writeFlag, readFlag));
        for (int i = 0; i < THREADS; i++) {
            completionService.submit(reader(readFlag));
        }

        writeFlag.set(true);

        for (int i = 0; i < (THREADS + 1); i++) {
            String error = completionService.take().get();
            if (error != null) {
                logger.info(error);
                fail("thread failed.");
            }
            assertThat(error, nullValue());
        }

        ThreadContext.clearContext();
    }

    private Callable<String> reader(final AtomicBoolean readFlag) {
        return () -> {
            String error = null;
            synchronized (this) {
                while (!readFlag.get()) {
                    try {
                        //                            logger.info(
                        //                                    "Waiting for readMutex notification before reading test file..." );
                        wait(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        error = "Failed to wait for readMutex";
                    }
                }
            }

            if (error != null) {
                return error;
            }

            logger.info("Reading test file");
            try (InputStream stream = mgr.openInputStream(file)) {
                String result = IOUtils.toString(stream);
                if (!content.equals(result)) {
                    error = String.format("Content mismatch!\nExpected:\n\n%s\n\nActual:\n\n%s", content, result);
                }
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
                error = "Failed to write test file.";
            }

            return error;
        };
    }

    private Callable<String> writer(final AtomicBoolean writeFlag, final AtomicBoolean readFlag) {
        return () -> {
            String error = null;
            synchronized (this) {
                while (!writeFlag.get()) {
                    try {
                        wait(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        error = "Failed to wait for writeMutex";
                    }
                }
            }

            if (error != null) {
                return error;
            }

            logger.info("Writing test file");
            byte[] data = content.getBytes();
            try (OutputStream stream = mgr.openOutputStream(file)) {
                for (int i = 0; i < data.length; i++) {
                    //                    logger.info( "write: {}", i );
                    stream.write(data[i]);
                    if (WAIT > 0) {
                        synchronized (this) {
                            wait(WAIT);
                        }
                    }

                    // wait for there to be something in the buffer.
                    if (i < 1) {
                        logger.info("Notifying readMutex");
                        readFlag.set(true);
                    }
                }

                logger.info("Write complete. Closing output stream.");
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
                error = "Failed to write test file";
            }

            return error;
        };
    }
}