blob: 4019b107b074478145bf2bc22b228d0476e78762 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE
* file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file
* to You 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.apache.tuweni.concurrent;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.jupiter.api.Test;
class AtomicSlotMapTest {
@Test
void shouldUseSlotsIncrementally() {
AtomicSlotMap<Integer, String> slotMap = AtomicSlotMap.positiveIntegerSlots();
assertEquals(1, (int) slotMap.add("value"));
assertEquals(2, (int) slotMap.add("value"));
assertEquals(3, (int) slotMap.add("value"));
assertEquals(4, (int) slotMap.add("value"));
}
@Test
void shouldReuseSlotsIncrementally() {
AtomicSlotMap<Integer, String> slotMap = AtomicSlotMap.positiveIntegerSlots();
assertEquals(1, (int) slotMap.add("value"));
assertEquals(2, (int) slotMap.add("value"));
assertEquals(3, (int) slotMap.add("value"));
assertEquals(4, (int) slotMap.add("value"));
slotMap.remove(2);
slotMap.remove(4);
assertEquals(2, (int) slotMap.add("value"));
assertEquals(4, (int) slotMap.add("value"));
}
@Test
void maintainsCountWhenConcurrentlyRemoving() {
AtomicSlotMap<Integer, String> slotMap = AtomicSlotMap.positiveIntegerSlots();
int firstSlot = slotMap.add("foo");
CompletableAsyncResult<String> result = AsyncResult.incomplete();
AtomicInteger secondSlot = new AtomicInteger();
slotMap.computeAsync(s -> {
secondSlot.set(s);
return result;
});
assertEquals(1, slotMap.size());
slotMap.remove(secondSlot.get());
assertEquals(1, slotMap.size());
result.complete("bar");
assertEquals(1, slotMap.size());
slotMap.remove(firstSlot);
assertEquals(0, slotMap.size());
}
@Test
void shouldNotDuplicateSlotsWhileAddingAndRemoving() throws Exception {
AtomicSlotMap<Integer, String> slotMap = AtomicSlotMap.positiveIntegerSlots();
Set<Integer> fastSlots = ConcurrentHashMap.newKeySet();
Set<Integer> slowSlots = ConcurrentHashMap.newKeySet();
Callable<Void> fastAdders = () -> {
int slot = slotMap.add("a fast value");
fastSlots.add(slot);
return null;
};
Callable<Void> slowAdders = () -> {
CompletableAsyncResult<String> result = AsyncResult.incomplete();
slotMap.computeAsync(s -> result).thenAccept(slowSlots::add);
Thread.sleep(10);
result.complete("a slow value");
return null;
};
Callable<Void> addAndRemovers = () -> {
int slot = slotMap.add("a value");
Thread.sleep(5);
slotMap.remove(slot);
return null;
};
ExecutorService fastPool = Executors.newFixedThreadPool(20);
ExecutorService slowPool = Executors.newFixedThreadPool(20);
ExecutorService addAndRemovePool = Executors.newFixedThreadPool(40);
List<Future<Void>> fastFutures = fastPool.invokeAll(Collections.nCopies(1000, fastAdders));
List<Future<Void>> slowFutures = slowPool.invokeAll(Collections.nCopies(1000, slowAdders));
List<Future<Void>> addAndRemoveFutures = addAndRemovePool.invokeAll(Collections.nCopies(2000, addAndRemovers));
for (Future<Void> future : addAndRemoveFutures) {
future.get();
}
for (Future<Void> future : slowFutures) {
future.get();
}
for (Future<Void> future : fastFutures) {
future.get();
}
assertEquals(1000, fastSlots.size());
assertEquals(1000, slowSlots.size());
slowSlots.addAll(fastSlots);
assertEquals(2000, slowSlots.size());
assertEquals(2000, slotMap.size());
}
}