/* * Copyright 2014 NAVER Corp. * * 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 com.navercorp.pinpoint.bootstrap.interceptor; import org.junit.Assert; import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.navercorp.pinpoint.bootstrap.interceptor.registry.WeakAtomicReferenceArray; import java.util.HashMap; import java.util.Map; import java.util.Random; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; /** * raceCondition generate fail.~~~~ hmm */ @Ignore public class WeakAtomicReferenceArrayTest { private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final int arraySize = 1024 * 2000; private final int testMapSize = 1; private final Map<Integer, AtomicReferenceTest> map = new HashMap<Integer, AtomicReferenceTest>(); private final AtomicInteger nextMapId = new AtomicInteger(); private int writerThreadSize = 1; private ThreadPoolExecutor writer; private int readThreadSize = 2; private ThreadPoolExecutor reader; private final AtomicInteger failCounter = new AtomicInteger(); @Before public void setUp() throws Exception { writer = (ThreadPoolExecutor) Executors.newFixedThreadPool(writerThreadSize); reader = (ThreadPoolExecutor) Executors.newFixedThreadPool(readThreadSize); writer.prestartAllCoreThreads(); reader.prestartAllCoreThreads(); for (int i = 0; i < testMapSize; i++) { map.put(i, new AtomicReferenceTest()); } } @After public void tearDown() throws Exception { writer.shutdownNow(); reader.shutdownNow(); } static final class Cell { volatile long p0, p1, p2, p3, p4, p5, p6; volatile WeakAtomicReferenceArray<Integer> weakAtomicReferenceArray; volatile long q0, q1, q2, q3, q4, q5, q6; } private static class ChangedValue { private int index; private int value; public ChangedValue(int index, int value) { this.index = index; this.value = value; } } private class AtomicReferenceTest { private final Cell cell = new Cell(); private final AtomicInteger nextId = new AtomicInteger(0); private final AtomicMaxUpdater maxIndex = new AtomicMaxUpdater(); // private final ConcurrentLinkedQueue<Integer> updateIndex = new ConcurrentLinkedQueue<Integer>(); private final WeakAtomicReferenceArray<Integer> ref; private final AtomicInteger afterLast = new AtomicInteger(-1); private final AtomicReference<ChangedValue> lastChangeValue = new AtomicReference<ChangedValue>(); private final Random random = new Random(); public AtomicReferenceTest() { cell.weakAtomicReferenceArray = new WeakAtomicReferenceArray<Integer>(arraySize, Integer.class); ref = cell.weakAtomicReferenceArray; } public boolean nextId() { int nextId = this.nextId.getAndIncrement(); if (nextId < arraySize) { ref.set(nextId, nextId); afterLast.set(nextId); // maxIndex.updateMax(nextId); // updateIndex.offer(nextId); // logger.debug("nextId:{}", nextId); return true; } else { return false; } } public boolean changeId(int index, int value) { if (index < arraySize) { ref.set(index, value); lastChangeValue.set(new ChangedValue(index, value)); // maxIndex.updateMax(nextId); // updateIndex.offer(nextId); // logger.debug("nextId:{}", nextId); return true; } else { return false; } } public boolean get(final int findIndex) { if (findIndex == -1) { return true; } Integer findResult = ref.get(findIndex); return checkInteger(findIndex, findResult); } public boolean checkChangeId() { ChangedValue changedValue = lastChangeValue.get(); if (changedValue == null){ return true; } Integer findResult = ref.get(changedValue.index); return checkInteger(changedValue.value, findResult); } private boolean checkInteger(int findIndex, Integer findResult) { if (findResult == null) { logger.debug("null find:{} result:{}", findIndex, nextId.get()); return false; } final boolean result = findResult == findIndex; if (!result) { logger.debug("not equals findResult:{}, findIndex:{}", findResult, findIndex); } return result; } // public boolean testInsert() { // final Integer findIndex = updateIndex.poll(); // if (findIndex == null) { // return true; // } // Integer findResult = cell.weakAtomicReferenceArray.get(findIndex); // if (random.nextInt(3) == 0) { // updateIndex.offer(findIndex); // } // return checkInteger(findIndex, findResult); // } public boolean randomGet() { if (writerThreadSize != 1) { return true; } // final int maxIndex = getMaxIndex(); final int maxIndex = afterLast.get(); if (maxIndex == -1) { return true; } int randomIndex; if (maxIndex == 0) { randomIndex = 0; } else { randomIndex = Math.abs(random.nextInt()) % (maxIndex); } return get(randomIndex); } public boolean lastGet() { return get(getMaxIndex()); } private int getMaxIndex() { return maxIndex.getIndex(); } } @Test public void testLastGet() { AtomicReferenceTest mock = new AtomicReferenceTest(); Assert.assertTrue(mock.lastGet()); mock.nextId(); Assert.assertTrue(mock.lastGet()); for (int i = 0; i < 10; i++) { Assert.assertTrue(mock.lastGet()); } } @Test public void testRandomGet() { AtomicReferenceTest mock = new AtomicReferenceTest(); Assert.assertTrue(mock.randomGet()); mock.nextId(); mock.nextId(); mock.nextId(); Assert.assertTrue(mock.randomGet()); for (int i = 0; i < 10; i++) { Assert.assertTrue(mock.randomGet()); } } @Test public void testTestMock3() { AtomicReferenceTest mock = new AtomicReferenceTest(); mock.nextId(); mock.nextId(); mock.nextId(); Assert.assertTrue(mock.randomGet()); for (int i = 0; i < 100; i++) { Assert.assertTrue(mock.randomGet()); } } @Test public void test() throws Exception { final AtomicBoolean start = new AtomicBoolean(true); final Runnable writeJob = new Runnable() { @Override public void run() { logger.debug("WriteJob-start"); int i =0; while (start.get()) { AtomicReferenceTest referenceTest = getTestMock(); referenceTest.nextId(); // referenceTest.changeId(0, i); try { Thread.sleep(10); } catch (InterruptedException ignore) { } i++; } logger.debug("WriteJob-end"); } }; final Runnable readJob = new Runnable() { @Override public void run() { logger.debug("ReaderJob-start"); while (start.get()) { AtomicReferenceTest atomicReferenceTest = getTestMock(); if (!atomicReferenceTest.lastGet()) { failCounter.getAndIncrement(); } // if (!atomicReferenceTest.checkChangeId()) { // failCounter.getAndIncrement(); // } // if (!testMock.testInsert()) { // failCounter.getAndIncrement(); // } // if (!testMock.randomGet()) { // failCounter.getAndIncrement(); // } // try { // Thread.sleep(10); // } catch (InterruptedException e) { // e.printStackTrace(); // } } logger.debug("ReaderJob-end"); } }; for (int i = 0; i < readThreadSize; i++) { reader.execute(readJob); } for (int i = 0; i < writerThreadSize; i++) { writer.execute(writeJob); } logger.debug("start"); for (int i = 0; i < 100; i++) { Thread.sleep(1000); logger.debug("failCounter:{}", failCounter.get()); } start.set(false); Thread.sleep(1000); Assert.assertEquals("raceCondition test", failCounter.get(), 0); writer.shutdown(); reader.shutdown(); } private AtomicReferenceTest getTestMock() { int andIncrement = nextMapId.getAndIncrement(); int mapIndex = andIncrement % testMapSize; return map.get(mapIndex); } }