/*
* Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved.
*
* 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.hazelcast.internal.util.concurrent;
import com.hazelcast.test.HazelcastSerialClassRunner;
import com.hazelcast.test.HazelcastTestSupport;
import com.hazelcast.test.TestThread;
import com.hazelcast.test.annotation.NightlyTest;
import com.hazelcast.util.concurrent.BackoffIdleStrategy;
import com.hazelcast.util.concurrent.IdleStrategy;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.junit.Assert.assertEquals;
@RunWith(HazelcastSerialClassRunner.class)
@Category(NightlyTest.class)
public class MPSCQueueStressTest extends HazelcastTestSupport {
private static final long DURATION_SECONDS = 30;
private final AtomicBoolean stop = new AtomicBoolean();
@Test
public void test_singleProducer_block() throws Exception {
test(1, null);
}
@Test
public void test_twoProducers_block() throws Exception {
test(2, null);
}
@Test
public void test_multipleProducers_block() throws Exception {
test(10, null);
}
@Test
public void test_singleProducer_backoff() throws Exception {
test(1, new BackoffIdleStrategy(100, 1000, 1000, MILLISECONDS.toNanos(1)));
}
@Test
public void test_twoProducers_backoff() throws Exception {
test(2, new BackoffIdleStrategy(100, 1000, 1000, MILLISECONDS.toNanos(1)));
}
@Test
public void test_multipleProducers_backoff() throws Exception {
test(10, new BackoffIdleStrategy(100, 1000, 1000, MILLISECONDS.toNanos(1)));
}
public void test(int producerCount, IdleStrategy idleStrategy) throws Exception {
MPSCQueue<Item> queue = new MPSCQueue<Item>(idleStrategy);
ConsumerThread consumers = new ConsumerThread(queue, producerCount);
queue.setConsumerThread(consumers);
consumers.start();
List<ProducerThread> producers = new LinkedList<ProducerThread>();
for (int k = 0; k < producerCount; k++) {
ProducerThread producer = new ProducerThread(queue, k);
producer.start();
producers.add(producer);
}
sleepAndStop(stop, DURATION_SECONDS);
long totalProduced = 0;
for (ProducerThread producer : producers) {
producer.assertSucceedsEventually();
totalProduced += producer.itemCount;
}
consumers.assertSucceedsEventually();
assertEquals(totalProduced, consumers.itemCount);
}
static class Item {
private final long value;
private final int producerId;
Item(int producerId, long value) {
this.value = value;
this.producerId = producerId;
}
}
class ProducerThread extends TestThread {
private final MPSCQueue<Item> queue;
private final int id;
private long itemCount;
ProducerThread(MPSCQueue<Item> queue, int id) {
super("Producer-" + id);
this.queue = queue;
this.id = id;
}
@Override
public void doRun() {
Random random = new Random();
while (!stop.get()) {
itemCount++;
queue.offer(new Item(id, itemCount));
while (queue.size() > 100000) {
sleepMillis(random.nextInt(100));
}
if (random.nextInt(1000) == 0) {
sleepMillis(random.nextInt(100));
}
if (itemCount % 10000 == 0) {
System.out.println(getName() + " at " + itemCount);
}
}
queue.offer(new Item(id, -1));
System.out.println(getName() + " Done");
}
}
class ConsumerThread extends TestThread {
private final MPSCQueue<Item> queue;
private final int producerCount;
private final long[] producerSequence;
private long itemCount;
private volatile int completedProducers = 0;
ConsumerThread(MPSCQueue<Item> queue, int producerCount) {
super("Consumer");
this.queue = queue;
this.producerCount = producerCount;
this.producerSequence = new long[producerCount];
}
@Override
public void doRun() throws Exception {
Random random = new Random();
for (; ; ) {
Item item = queue.take();
if (item.value == -1) {
completedProducers++;
if (completedProducers == producerCount) {
break;
}
} else {
itemCount++;
long last = producerSequence[item.producerId];
if (last + 1 != item.value) {
stop.set(true);
throw new RuntimeException();
}
producerSequence[item.producerId] = item.value;
}
if (itemCount % 10000 == 0) {
System.out.println(getName() + " at " + itemCount);
}
//System.out.println("Consumed: " + item);
if (random.nextInt(1000) == 0) {
sleepMillis(random.nextInt(100));
}
}
System.out.println(getName() + " Done");
}
}
}